/**
 * US-8 — redteam fills technical details of a simulation.
 * Covers AC-8.1 → AC-8.6 (AC-8.6 defers to US-10 for the autocomplete UI detail).
 */
import { test, expect } from '@playwright/test';
import {
  adminToken,
  createEngagement,
  deleteEngagement,
  deleteUserByUsername,
  ensureUser,
  login,
  makeClient,
} from '../fixtures/api';
import { seedTokenInStorage } from '../fixtures/auth';

const REDTEAM_USER = 'us8-redteam';
const SOC_USER = 'us8-soc';
const PASS = 'us8-pass-strong';

interface Simulation {
  id: number;
  engagement_id: number;
  name: string;
  status: string;
  [key: string]: unknown;
}

async function createSimulation(
  token: string,
  engagementId: number,
  name = 'US-8 sim',
): Promise<Simulation> {
  const client = makeClient(token);
  const r = await client.post(`/engagements/${engagementId}/simulations`, { name });
  if (r.status !== 201) {
    throw new Error(`create simulation failed: ${r.status} ${JSON.stringify(r.data)}`);
  }
  return r.data as Simulation;
}

async function deleteSimulation(token: string, simId: number): Promise<void> {
  const client = makeClient(token);
  await client.delete(`/simulations/${simId}`);
}

test.describe('US-8 — redteam fill simulation details', () => {
  let redteamToken: string;
  let socToken: string;
  let engagementId: number;

  test.beforeAll(async () => {
    await ensureUser(REDTEAM_USER, PASS, 'redteam');
    await ensureUser(SOC_USER, PASS, 'soc');
    redteamToken = (await login(REDTEAM_USER, PASS)).token;
    socToken = (await login(SOC_USER, PASS)).token;
    const eng = await createEngagement(redteamToken, {
      name: 'US-8 Test Engagement',
      start_date: '2026-01-01',
    });
    engagementId = eng.id;
  });

  test.afterAll(async () => {
    try {
      const tok = await adminToken();
      await deleteEngagement(tok, engagementId);
      for (const u of [REDTEAM_USER, SOC_USER]) {
        await deleteUserByUsername(tok, u);
      }
    } catch {
      /* noop */
    }
  });

  test('AC-8.1 — PATCH accepts all redteam fields (partial OK)', async () => {
    const sim = await createSimulation(redteamToken, engagementId, 'AC-8.1 sim');
    const client = makeClient(redteamToken);

    const patch = {
      name: 'Updated name',
      mitre_technique_id: 'T1059',
      mitre_technique_name: 'Command and Scripting Interpreter',
      description: 'Some description',
      commands: 'cmd /c whoami\ncmd /c ipconfig',
      prerequisites: 'Admin shell',
      executed_at: '2026-05-01T12:00:00',
      execution_result: 'Success',
    };
    const r = await client.patch(`/simulations/${sim.id}`, patch);
    expect(r.status).toBe(200);
    expect(r.data.name).toBe('Updated name');
    expect(r.data.mitre_technique_id).toBe('T1059');
    expect(r.data.mitre_technique_name).toBe('Command and Scripting Interpreter');
    expect(r.data.description).toBe('Some description');
    expect(r.data.commands).toBe('cmd /c whoami\ncmd /c ipconfig');
    expect(r.data.prerequisites).toBe('Admin shell');
    expect(r.data.execution_result).toBe('Success');

    // Partial PATCH (only name) should also work
    const rPartial = await client.patch(`/simulations/${sim.id}`, { name: 'Partial update' });
    expect(rPartial.status).toBe(200);
    expect(rPartial.data.name).toBe('Partial update');

    await deleteSimulation(redteamToken, sim.id);
  });

  test('AC-8.2 — auto-transition pending→in_progress on PATCH with non-empty redteam field', async () => {
    const sim = await createSimulation(redteamToken, engagementId, 'AC-8.2 sim');
    expect(sim.status).toBe('pending');
    const client = makeClient(redteamToken);

    // PATCH with a non-empty redteam field triggers auto-transition
    const r = await client.patch(`/simulations/${sim.id}`, { name: 'trigger transition' });
    expect(r.status).toBe(200);
    expect(r.data.status).toBe('in_progress');

    await deleteSimulation(redteamToken, sim.id);
  });

  test('AC-8.2 — auto-transition does NOT trigger for soc PATCH', async () => {
    // Create sim then transition it to review_required so SOC can patch it
    const sim = await createSimulation(redteamToken, engagementId, 'AC-8.2 soc no-trigger');
    const rtClient = makeClient(redteamToken);
    // Move to in_progress first
    await rtClient.patch(`/simulations/${sim.id}`, { name: 'AC-8.2 soc no-trigger' });
    // Move to review_required
    const tr = await rtClient.post(`/simulations/${sim.id}/transition`, {
      to: 'review_required',
    });
    expect(tr.status).toBe(200);

    // Now SOC patches soc-only fields — status must NOT change
    const socClient = makeClient(socToken);
    const rSoc = await socClient.patch(`/simulations/${sim.id}`, { soc_comment: 'noted' });
    expect(rSoc.status).toBe(200);
    expect(rSoc.data.status).toBe('review_required');

    await deleteSimulation(redteamToken, sim.id);
  });

  test('AC-8.3 — commands stored and returned as raw multiline string', async () => {
    const sim = await createSimulation(redteamToken, engagementId, 'AC-8.3 sim');
    const client = makeClient(redteamToken);
    const cmds = 'cmd1\ncmd2\ncmd3';
    const r = await client.patch(`/simulations/${sim.id}`, { commands: cmds });
    expect(r.status).toBe(200);
    expect(r.data.commands).toBe(cmds);

    await deleteSimulation(redteamToken, sim.id);
  });

  test('AC-8.4 — invalid executed_at → 400; null clears field', async () => {
    const sim = await createSimulation(redteamToken, engagementId, 'AC-8.4 sim');
    const client = makeClient(redteamToken);

    const rBad = await client.patch(`/simulations/${sim.id}`, {
      executed_at: 'not-a-date',
    });
    expect(rBad.status).toBe(400);
    expect(rBad.data.error).toMatch(/invalid executed_at/i);

    // Valid ISO 8601
    const rOk = await client.patch(`/simulations/${sim.id}`, {
      executed_at: '2026-05-01T09:00:00',
    });
    expect(rOk.status).toBe(200);

    // Null clears the field
    const rNull = await client.patch(`/simulations/${sim.id}`, { executed_at: null });
    expect(rNull.status).toBe(200);

    await deleteSimulation(redteamToken, sim.id);
  });

  test('AC-8.5 — edit page shows Red Team and SOC sections; redteam sees both editable, name validation', async ({
    page,
    context,
  }) => {
    const sim = await createSimulation(redteamToken, engagementId, 'AC-8.5 sim');

    await seedTokenInStorage(context, redteamToken);
    await page.goto(`/engagements/${engagementId}/simulations/${sim.id}/edit`);

    // Both sections visible
    await expect(page.getByRole('heading', { name: /red team/i })).toBeVisible();
    await expect(page.getByRole('heading', { name: /soc/i })).toBeVisible();

    // Name field is present and enabled for redteam
    const nameField = page.locator('#sim-name');
    await expect(nameField).toBeVisible();
    await expect(nameField).toBeEnabled();

    // Clear the name, try to save → client validation error
    await nameField.fill('');
    await page.getByRole('button', { name: /save red team/i }).click();
    await expect(page.getByText(/name is required/i)).toBeVisible();

    await deleteSimulation(redteamToken, sim.id);
  });

  test('AC-8.6 — MITRE technique picker is present on the edit form', async ({
    page,
    context,
  }) => {
    const sim = await createSimulation(redteamToken, engagementId, 'AC-8.6 sim');

    await seedTokenInStorage(context, redteamToken);
    await page.goto(`/engagements/${engagementId}/simulations/${sim.id}/edit`);

    // MitreTechniquePicker renders an input with combobox role
    await expect(page.getByRole('combobox', { name: /mitre technique/i })).toBeVisible();

    await deleteSimulation(redteamToken, sim.id);
  });
});
