/**
 * US-16 — regression: sprint 2 features still work under the sprint 3 model.
 * Covers AC-16.1 → AC-16.3.
 *
 * This file re-exercises critical sprint 2 ACs that are most likely to break
 * due to the scalar→array MITRE migration:
 *   - Auto-transition pending→in_progress (AC-8.2 / AC-13.5)
 *   - Manual workflow transitions + badge update (AC-11.x)
 *   - SOC field-level RBAC (AC-9.x)
 *   - MitreTechniquePicker still accessible via Quick Search (AC-16.2)
 */
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 = 'us16-redteam';
const SOC_USER = 'us16-soc';
const PASS = 'us16-pass-strong';

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

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

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

test.describe('US-16 — sprint 2 regression', () => {
  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-16 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 */
    }
  });

  // AC-16.1 — workflow sprint 2: auto-transition, manual transitions, SOC RBAC

  test('AC-16.1 — auto-transition pending→in_progress triggered by PATCH with non-empty technique_ids', async () => {
    const sim = await createSimulation(redteamToken, engagementId, 'AC-16.1 auto-transition');
    expect(sim.status).toBe('pending');

    const r = await makeClient(redteamToken).patch(`/simulations/${sim.id}`, {
      technique_ids: ['T1059'],
    });
    expect(r.status).toBe(200);
    expect(r.data.status).toBe('in_progress');

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

  test('AC-16.1 — auto-transition triggered by non-technique redteam PATCH (name)', async () => {
    const sim = await createSimulation(redteamToken, engagementId, 'AC-16.1 auto-name');
    expect(sim.status).toBe('pending');

    const r = await makeClient(redteamToken).patch(`/simulations/${sim.id}`, {
      name: 'trigger by name',
    });
    expect(r.status).toBe(200);
    expect(r.data.status).toBe('in_progress');

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

  test('AC-16.1 — manual transition in_progress→review_required→closed', async () => {
    const sim = await createSimulation(redteamToken, engagementId, 'AC-16.1 workflow');
    const rtClient = makeClient(redteamToken);

    // Trigger in_progress
    await rtClient.patch(`/simulations/${sim.id}`, { name: 'AC-16.1 workflow' });

    // in_progress → review_required
    const r1 = await rtClient.post(`/simulations/${sim.id}/transition`, {
      to: 'review_required',
    });
    expect(r1.status).toBe(200);
    expect(r1.data.status).toBe('review_required');

    // review_required → done
    const r2 = await rtClient.post(`/simulations/${sim.id}/transition`, { to: 'done' });
    expect(r2.status).toBe(200);
    expect(r2.data.status).toBe('done');

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

  test('AC-16.1 — SOC cannot PATCH technique_ids (403)', async () => {
    const sim = await createSimulation(redteamToken, engagementId, 'AC-16.1 soc block');
    const rtClient = makeClient(redteamToken);

    // Advance to review_required so SOC has access
    await rtClient.patch(`/simulations/${sim.id}`, { name: 'AC-16.1 soc block' });
    await rtClient.post(`/simulations/${sim.id}/transition`, { to: 'review_required' });

    const r = await makeClient(socToken).patch(`/simulations/${sim.id}`, {
      technique_ids: ['T1059'],
    });
    expect(r.status).toBe(403);

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

  test('AC-16.1 — SOC can PATCH soc_comment without affecting status', async () => {
    const sim = await createSimulation(redteamToken, engagementId, 'AC-16.1 soc comment');
    const rtClient = makeClient(redteamToken);

    // Advance to review_required
    await rtClient.patch(`/simulations/${sim.id}`, { name: 'AC-16.1 soc comment' });
    await rtClient.post(`/simulations/${sim.id}/transition`, { to: 'review_required' });

    const r = await makeClient(socToken).patch(`/simulations/${sim.id}`, {
      soc_comment: 'Looks good, close it.',
    });
    expect(r.status).toBe(200);
    expect(r.data.status).toBe('review_required');
    expect(r.data.soc_comment).toBe('Looks good, close it.');

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

  test('AC-16.1 — SOC cannot transition pending simulation', async () => {
    const sim = await createSimulation(redteamToken, engagementId, 'AC-16.1 soc transition');

    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-16.1 — workflow badge updates in UI without page reload', async ({
    page,
    context,
  }) => {
    const sim = await createSimulation(redteamToken, engagementId, 'AC-16.1 badge');
    const rtClient = makeClient(redteamToken);
    await rtClient.patch(`/simulations/${sim.id}`, { name: 'AC-16.1 badge' });

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

    const badge = page.getByTestId('simulation-status-badge');
    await expect(badge).toBeVisible();
    await expect(badge).toHaveAttribute('data-status', 'in_progress');

    // Trigger transition via button
    await page.getByRole('button', { name: /mark for review/i }).click();
    await expect(badge).toHaveAttribute('data-status', 'review_required', { timeout: 5_000 });

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

  // AC-16.2 — MitreTechniquePicker still accessible via Quick Search (clean rewrite onSelect)

  test('AC-16.2 — MitreTechniquePicker accessible via inline search, appends tag on selection', async ({
    page,
    context,
  }) => {
    const sim = await createSimulation(redteamToken, engagementId, 'AC-16.2 picker');

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

    // Sprint 4: click inline placeholder text to reveal the picker
    await page.getByText(/search technique/i).click();
    const picker = page.getByRole('combobox', { name: /mitre technique/i });
    await expect(picker).toBeVisible();

    // Type to search
    await picker.fill('T1078');
    const listbox = page.getByRole('listbox', { name: /mitre techniques/i });
    await expect(listbox).toBeVisible({ timeout: 5_000 });

    // Keyboard select
    await picker.press('ArrowDown');
    await picker.press('Enter');

    // Tag appears (onSelect one-shot mode — appends to list)
    await expect(page.getByTestId('techniques-tag-list')).toContainText('T1078', {
      timeout: 5_000,
    });

    // Auto-save toast
    await expect(page.getByText(/techniques updated/i)).toBeVisible({ timeout: 5_000 });

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

  // AC-16.3 — no sprint 1/2 e2e broken: spot-check key assertions with new model

  test('AC-16.3 — simulation serialisation has techniques array (not scalar MITRE fields)', async () => {
    const sim = await createSimulation(redteamToken, engagementId, 'AC-16.3 schema');

    // New schema: techniques array
    expect(Array.isArray(sim.techniques)).toBe(true);
    expect(sim).not.toHaveProperty('mitre_technique_id');
    expect(sim).not.toHaveProperty('mitre_technique_name');

    // PATCH technique_ids → techniques array in response
    const r = await makeClient(redteamToken).patch(`/simulations/${sim.id}`, {
      technique_ids: ['T1059', 'T1078'],
    });
    expect(r.status).toBe(200);
    expect(Array.isArray(r.data.techniques)).toBe(true);
    expect(r.data.techniques).toHaveLength(2);
    expect(r.data.techniques[0].id).toBe('T1059');
    expect(r.data.techniques[0].name).toBeTruthy();
    expect(Array.isArray(r.data.techniques[0].tactics)).toBe(true);

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

  test('AC-16.3 — edit form Red Team section still has name, description, commands fields', async ({
    page,
    context,
  }) => {
    const sim = await createSimulation(redteamToken, engagementId, 'AC-16.3 form fields');

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

    // Red Team section
    await expect(page.getByRole('heading', { name: /red team/i })).toBeVisible();

    // Core fields still present
    await expect(page.locator('#sim-name')).toBeVisible();
    await expect(page.locator('#sim-description')).toBeVisible();
    await expect(page.locator('#sim-commands')).toBeVisible();

    // Sprint 4: "Save Red Team" renamed to "Save"
    await expect(page.getByRole('button', { name: /^save$/i })).toBeVisible();

    // Sprint 4: matrix icon + inline search placeholder replace the old text buttons
    await expect(page.getByLabel(/open mitre matrix/i)).toBeVisible();
    await expect(page.getByText(/search technique/i)).toBeVisible();

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