/**
 * US-18 — Simulation `done` = read-only + Reopen.
 * Covers AC-18.1 → AC-18.5.
 */
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 = 'us18-redteam';
const SOC_USER = 'us18-soc';
const PASS = 'us18-pass-strong';

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

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

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

/** Drive a simulation from pending → in_progress → review_required → done */
async function driveSimToDone(token: string, simId: number): Promise<void> {
  const c = makeClient(token);
  await c.patch(`/simulations/${simId}`, { name: 'trigger in_progress' });
  await c.post(`/simulations/${simId}/transition`, { to: 'review_required' });
  await c.post(`/simulations/${simId}/transition`, { to: 'done' });
}

test.describe('US-18 — done read-only + Reopen', () => {
  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-18 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-18.1 — PATCH on done simulation returns 409 (redteam)', async () => {
    const sim = await createSimulation(redteamToken, engagementId, 'AC-18.1 done redteam');
    await driveSimToDone(redteamToken, sim.id);

    const r = await makeClient(redteamToken).patch(`/simulations/${sim.id}`, { name: 'should fail' });
    expect(r.status).toBe(409);
    expect(r.data.error).toMatch(/simulation is done — reopen first/i);

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

  test('AC-18.1 — PATCH on done simulation returns 409 (soc)', async () => {
    const sim = await createSimulation(redteamToken, engagementId, 'AC-18.1 done soc');
    await driveSimToDone(redteamToken, sim.id);

    // SOC tries to PATCH soc_comment on a done sim → 409
    const r = await makeClient(socToken).patch(`/simulations/${sim.id}`, { soc_comment: 'late note' });
    expect(r.status).toBe(409);
    expect(r.data.error).toMatch(/simulation is done — reopen first/i);

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

  test('AC-18.1 — PATCH on done simulation returns 409 (admin)', async () => {
    const sim = await createSimulation(redteamToken, engagementId, 'AC-18.1 done admin');
    await driveSimToDone(redteamToken, sim.id);
    const tok = await adminToken();

    const r = await makeClient(tok).patch(`/simulations/${sim.id}`, { name: 'admin override' });
    expect(r.status).toBe(409);
    expect(r.data.error).toMatch(/simulation is done — reopen first/i);

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

  test('AC-18.2 — Reopen: done → review_required via transition (redteam)', async () => {
    const sim = await createSimulation(redteamToken, engagementId, 'AC-18.2 reopen redteam');
    await driveSimToDone(redteamToken, sim.id);

    const r = await makeClient(redteamToken).post(`/simulations/${sim.id}/transition`, { to: 'review_required' });
    expect(r.status).toBe(200);
    expect(r.data.status).toBe('review_required');
    expect(r.data.updated_at).toBeTruthy();

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

  test('AC-18.2 — Reopen: done → review_required via transition (soc)', async () => {
    const sim = await createSimulation(redteamToken, engagementId, 'AC-18.2 reopen soc');
    await driveSimToDone(redteamToken, sim.id);

    const r = await makeClient(socToken).post(`/simulations/${sim.id}/transition`, { to: 'review_required' });
    expect(r.status).toBe(200);
    expect(r.data.status).toBe('review_required');

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

  test('AC-18.3 — review_required from pending/in_progress stays admin/redteam only (not soc)', async () => {
    const sim = await createSimulation(redteamToken, engagementId, 'AC-18.3 soc cannot mark review');
    await makeClient(redteamToken).patch(`/simulations/${sim.id}`, { name: 'trigger' });
    // SOC cannot mark in_progress → review_required
    const r = await makeClient(socToken).post(`/simulations/${sim.id}/transition`, { to: 'review_required' });
    expect(r.status).toBe(403);

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

  test('AC-18.3 — other transitions from done still return 409', async () => {
    const sim = await createSimulation(redteamToken, engagementId, 'AC-18.3 done bad transition');
    await driveSimToDone(redteamToken, sim.id);

    // Trying to go done → done or done → in_progress should 409
    const r1 = await makeClient(redteamToken).post(`/simulations/${sim.id}/transition`, { to: 'done' });
    expect(r1.status).toBe(409);

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

  test('AC-18.4 — SimulationFormPage done: all fields disabled, only Reopen button visible', async ({
    page,
    context,
  }) => {
    const sim = await createSimulation(redteamToken, engagementId, 'AC-18.4 done UI');
    await driveSimToDone(redteamToken, sim.id);

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

    // Read-only banner visible
    await expect(page.getByText(/done.*read-only|read-only.*done/i)).toBeVisible();

    // Name field disabled
    const nameField = page.locator('#sim-name');
    await expect(nameField).toBeDisabled();

    // Reopen button visible
    await expect(page.getByRole('button', { name: /reopen/i })).toBeVisible();

    // Save RT, Save SOC, Mark for review, Close, Delete — all absent
    await expect(page.getByRole('button', { name: /save/i })).not.toBeVisible();
    await expect(page.getByRole('button', { name: /mark for review/i })).not.toBeVisible();
    await expect(page.getByRole('button', { name: /^close$/i })).not.toBeVisible();

    // MitreTechniquesField in read-only mode: no matrix icon, no input
    await expect(page.getByLabel(/open mitre matrix/i)).not.toBeVisible();

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

  test('AC-18.5 — Reopen via UI: toast appears, badge updates, fields editable', async ({
    page,
    context,
  }) => {
    const sim = await createSimulation(redteamToken, engagementId, 'AC-18.5 reopen UI');
    await driveSimToDone(redteamToken, sim.id);

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

    // Click Reopen
    await page.getByRole('button', { name: /reopen/i }).click();

    // Toast: "Simulation reopened"
    await expect(page.getByText(/simulation reopened/i)).toBeVisible({ timeout: 5_000 });

    // Badge updates to review_required
    const badge = page.getByTestId('simulation-status-badge');
    await expect(badge).toHaveAttribute('data-status', 'review_required', { timeout: 5_000 });

    // Fields become editable again (name field enabled)
    await expect(page.locator('#sim-name')).toBeEnabled({ timeout: 3_000 });

    // Reopen button gone; Save button now visible
    await expect(page.getByRole('button', { name: /reopen/i })).not.toBeVisible();
    await expect(page.getByRole('button', { name: /save/i }).first()).toBeVisible();

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

  test('AC-18.5 — after Reopen, PATCH succeeds (no longer 409)', async () => {
    const sim = await createSimulation(redteamToken, engagementId, 'AC-18.5 PATCH after reopen');
    await driveSimToDone(redteamToken, sim.id);

    // Reopen via API
    await makeClient(redteamToken).post(`/simulations/${sim.id}/transition`, { to: 'review_required' });

    // Now PATCH should succeed
    const r = await makeClient(redteamToken).patch(`/simulations/${sim.id}`, { soc_comment: 'updated' });
    expect(r.status).toBe(200);

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