/**
 * US-11 — workflow transitions.
 * Covers AC-11.1 → AC-11.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 = 'us11-redteam';
const SOC_USER = 'us11-soc';
const PASS = 'us11-pass-strong';

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

async function createSimulation(
  token: string,
  engagementId: number,
  name = 'US-11 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-11 — workflow transitions', () => {
  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-11 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-11.1 — pending→review_required valid (redteam); invalid target → 409', async () => {
    const rtClient = makeClient(redteamToken);

    // pending → review_required: valid
    const sim = await createSimulation(redteamToken, engagementId, 'AC-11.1 sim');
    const rOk = await rtClient.post(`/simulations/${sim.id}/transition`, {
      to: 'review_required',
    });
    expect(rOk.status).toBe(200);
    expect(rOk.data.status).toBe('review_required');

    // Invalid target → 409
    const simBad = await createSimulation(redteamToken, engagementId, 'AC-11.1 bad sim');
    const rBad = await rtClient.post(`/simulations/${simBad.id}/transition`, {
      to: 'done',
    });
    expect(rBad.status).toBe(409);
    expect(rBad.data.error).toMatch(/invalid transition/i);

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

  test('AC-11.1 — in_progress→review_required valid (redteam)', async () => {
    const rtClient = makeClient(redteamToken);
    const sim = await createSimulation(redteamToken, engagementId, 'AC-11.1 in_progress sim');

    // Trigger in_progress via auto-transition
    await rtClient.patch(`/simulations/${sim.id}`, { name: 'trigger' });
    const r = await rtClient.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-11.2 — review_required→done valid for redteam and soc', async () => {
    const rtClient = makeClient(redteamToken);
    const socClient = makeClient(socToken);

    // redteam can close
    const simRT = await createSimulation(redteamToken, engagementId, 'AC-11.2 redteam close');
    await rtClient.post(`/simulations/${simRT.id}/transition`, { to: 'review_required' });
    const rRT = await rtClient.post(`/simulations/${simRT.id}/transition`, { to: 'done' });
    expect(rRT.status).toBe(200);
    expect(rRT.data.status).toBe('done');

    // soc can close
    const simSOC = await createSimulation(redteamToken, engagementId, 'AC-11.2 soc close');
    await rtClient.post(`/simulations/${simSOC.id}/transition`, { to: 'review_required' });
    const rSOC = await socClient.post(`/simulations/${simSOC.id}/transition`, { to: 'done' });
    expect(rSOC.status).toBe(200);
    expect(rSOC.data.status).toBe('done');

    // Sprint 4: done → review_required is the Reopen flow, now valid (200).
    const rBack = await rtClient.post(`/simulations/${simRT.id}/transition`, {
      to: 'review_required',
    });
    expect(rBack.status).toBe(200);
    expect(rBack.data.status).toBe('review_required');

    await deleteSimulation(redteamToken, simRT.id);
    await deleteSimulation(redteamToken, simSOC.id);
  });

  test('AC-11.3 — no backward transitions; no →pending or →in_progress via endpoint', async () => {
    const rtClient = makeClient(redteamToken);
    const sim = await createSimulation(redteamToken, engagementId, 'AC-11.3 sim');

    // done → pending: invalid
    await rtClient.post(`/simulations/${sim.id}/transition`, { to: 'review_required' });
    await rtClient.post(`/simulations/${sim.id}/transition`, { to: 'done' });
    const rPending = await rtClient.post(`/simulations/${sim.id}/transition`, {
      to: 'pending',
    });
    expect(rPending.status).toBe(409);
    expect(rPending.data.error).toMatch(/invalid transition/i);

    // →in_progress via endpoint is always invalid
    const simNew = await createSimulation(redteamToken, engagementId, 'AC-11.3 in_progress');
    const rIP = await rtClient.post(`/simulations/${simNew.id}/transition`, {
      to: 'in_progress',
    });
    expect(rIP.status).toBe(409);

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

  test('AC-11.4 — workflow buttons visible per role+status in UI', async ({
    page,
    context,
  }) => {
    const rtClient = makeClient(redteamToken);

    // pending → "Mark for review" visible for redteam; "Close" hidden
    const simPending = await createSimulation(
      redteamToken,
      engagementId,
      'AC-11.4 pending UI',
    );
    await seedTokenInStorage(context, redteamToken);
    await page.goto(`/engagements/${engagementId}/simulations/${simPending.id}/edit`);
    await expect(page.getByRole('button', { name: /mark for review/i })).toBeVisible();
    await expect(page.getByRole('button', { name: /^close$/i })).toHaveCount(0);

    // in_progress → "Mark for review" visible
    const simIP = await createSimulation(redteamToken, engagementId, 'AC-11.4 in_progress UI');
    await rtClient.patch(`/simulations/${simIP.id}`, { name: 'trigger' });
    await page.goto(`/engagements/${engagementId}/simulations/${simIP.id}/edit`);
    await expect(page.getByRole('button', { name: /mark for review/i })).toBeVisible();
    await expect(page.getByRole('button', { name: /^close$/i })).toHaveCount(0);

    // review_required → "Close" visible for redteam; "Mark for review" hidden
    const simRR = await createSimulation(redteamToken, engagementId, 'AC-11.4 review UI');
    await rtClient.post(`/simulations/${simRR.id}/transition`, { to: 'review_required' });
    await page.goto(`/engagements/${engagementId}/simulations/${simRR.id}/edit`);
    await expect(page.getByRole('button', { name: /^close$/i })).toBeVisible();
    await expect(page.getByRole('button', { name: /mark for review/i })).toHaveCount(0);

    // review_required → "Close" also visible for SOC
    await seedTokenInStorage(context, socToken);
    await page.goto(`/engagements/${engagementId}/simulations/${simRR.id}/edit`);
    await expect(page.getByRole('button', { name: /^close$/i })).toBeVisible();

    // done → both buttons hidden
    await rtClient.post(`/simulations/${simRR.id}/transition`, { to: 'done' });
    await seedTokenInStorage(context, redteamToken);
    await page.goto(`/engagements/${engagementId}/simulations/${simRR.id}/edit`);
    await expect(page.getByRole('button', { name: /mark for review/i })).toHaveCount(0);
    await expect(page.getByRole('button', { name: /^close$/i })).toHaveCount(0);

    await deleteSimulation(redteamToken, simPending.id);
    await deleteSimulation(redteamToken, simIP.id);
    await deleteSimulation(redteamToken, simRR.id);
  });

  test('AC-11.5 — after transition, badge updates in UI (TanStack Query invalidation)', async ({
    page,
    context,
  }) => {
    const rtClient = makeClient(redteamToken);
    const sim = await createSimulation(redteamToken, engagementId, 'AC-11.5 badge sim');

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

    // Initially pending
    const badge = page.getByTestId('simulation-status-badge');
    await expect(badge).toHaveAttribute('data-status', 'pending');

    // Click "Mark for review"
    await page.getByRole('button', { name: /mark for review/i }).click();

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

    // "Close" now visible; click it
    await page.getByRole('button', { name: /^close$/i }).click();
    await expect(badge).toHaveAttribute('data-status', 'done', { timeout: 5_000 });

    // Verify list is also updated: navigate to engagement detail and check badge there
    await page.goto(`/engagements/${engagementId}`);
    const listBadge = page
      .getByRole('row', { name: /AC-11.5 badge sim/i })
      .getByTestId('simulation-status-badge');
    await expect(listBadge).toHaveAttribute('data-status', 'done');

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