/**
 * US-4 — engagement CRUD + RBAC + UI surfaces.
 * Covers AC-4.1 → AC-4.9.
 */
import { test, expect } from '@playwright/test';
import {
  adminToken,
  createEngagement,
  deleteAllEngagements,
  deleteEngagement,
  deleteUserByUsername,
  ensureUser,
  listEngagements,
  login,
  makeClient,
} from '../fixtures/api';
import { seedTokenInStorage } from '../fixtures/auth';

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

test.describe('US-4 — engagement CRUD', () => {
  let redteamToken: string;
  let socToken: string;

  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;
    // Clean slate so AC-4.7 list assertions are predictable.
    await deleteAllEngagements(await adminToken());
  });

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

  test('AC-4.1 — GET /api/engagements returns serialized list (created_by = {id, username})', async () => {
    const seeded = await createEngagement(redteamToken, {
      name: 'AC-4.1 sample',
      start_date: '2026-02-01',
    });
    const items = await listEngagements(redteamToken);
    const row = items.find((i) => i.id === seeded.id);
    expect(row).toBeTruthy();
    expect(row).toMatchObject({
      name: 'AC-4.1 sample',
      status: 'planned',
      start_date: '2026-02-01',
    });
    expect(row!.created_by).toMatchObject({ username: REDTEAM_USER });
    expect(typeof row!.created_by!.id).toBe('number');
  });

  test('AC-4.2 — POST validates name/dates/status', async () => {
    const client = makeClient(redteamToken);

    const blankName = await client.post('/engagements', {
      name: '',
      start_date: '2026-03-01',
    });
    expect(blankName.status).toBe(400);

    const noStart = await client.post('/engagements', { name: 'x' });
    expect(noStart.status).toBe(400);

    const badDate = await client.post('/engagements', {
      name: 'x',
      start_date: 'not-a-date',
    });
    expect(badDate.status).toBe(400);

    const endBeforeStart = await client.post('/engagements', {
      name: 'x',
      start_date: '2026-04-10',
      end_date: '2026-04-01',
    });
    expect(endBeforeStart.status).toBe(400);

    const badStatus = await client.post('/engagements', {
      name: 'x',
      start_date: '2026-04-01',
      status: 'frozen',
    });
    expect(badStatus.status).toBe(400);

    const defaultStatus = await client.post('/engagements', {
      name: 'AC-4.2 default-status',
      start_date: '2026-04-01',
    });
    expect(defaultStatus.status).toBe(201);
    expect(defaultStatus.data.status).toBe('planned');
  });

  test('AC-4.3 — GET /api/engagements/<id> returns 200 + object, 404 if unknown', async () => {
    const seeded = await createEngagement(redteamToken, {
      name: 'AC-4.3 sample',
      start_date: '2026-05-01',
    });
    const client = makeClient(redteamToken);
    const ok = await client.get(`/engagements/${seeded.id}`);
    expect(ok.status).toBe(200);
    expect(ok.data.id).toBe(seeded.id);

    const missing = await client.get('/engagements/999999');
    expect(missing.status).toBe(404);
  });

  test('AC-4.4 — PATCH (admin/redteam) updates fields', async () => {
    const seeded = await createEngagement(redteamToken, {
      name: 'AC-4.4 orig',
      start_date: '2026-06-01',
    });
    const client = makeClient(redteamToken);
    const r = await client.patch(`/engagements/${seeded.id}`, {
      name: 'AC-4.4 updated',
      status: 'active',
      end_date: '2026-06-15',
    });
    expect(r.status).toBe(200);
    expect(r.data).toMatchObject({
      name: 'AC-4.4 updated',
      status: 'active',
      end_date: '2026-06-15',
    });
  });

  test('AC-4.5 — DELETE (admin/redteam) returns 204', async () => {
    const seeded = await createEngagement(redteamToken, {
      name: 'AC-4.5 disposable',
      start_date: '2026-07-01',
    });
    const client = makeClient(redteamToken);
    const r = await client.delete(`/engagements/${seeded.id}`);
    expect(r.status).toBe(204);

    const after = await client.get(`/engagements/${seeded.id}`);
    expect(after.status).toBe(404);
  });

  test('AC-4.6 — soc can read but not write (403 on POST/PATCH/DELETE)', async () => {
    const socClient = makeClient(socToken);
    const list = await socClient.get('/engagements');
    expect(list.status).toBe(200);

    const post = await socClient.post('/engagements', {
      name: 'soc-blocked',
      start_date: '2026-08-01',
    });
    expect(post.status).toBe(403);

    // Seed via redteam to get a target id.
    const target = await createEngagement(redteamToken, {
      name: 'AC-4.6 target',
      start_date: '2026-08-15',
    });

    const patch = await socClient.patch(`/engagements/${target.id}`, { name: 'soc-edit' });
    expect(patch.status).toBe(403);

    const del = await socClient.delete(`/engagements/${target.id}`);
    expect(del.status).toBe(403);

    // Clean up via redteam.
    await deleteEngagement(redteamToken, target.id);
  });

  test('AC-4.7 — /engagements page lists rows with required columns + role-aware buttons', async ({
    page,
    context,
  }) => {
    // Seed one row visible to the redteam user.
    await createEngagement(redteamToken, {
      name: 'UI list sample',
      start_date: '2026-09-01',
      status: 'active',
    });
    await seedTokenInStorage(context, redteamToken);
    await page.goto('/engagements');

    await expect(page.getByRole('heading', { name: /^engagements$/i })).toBeVisible();
    // Column headers
    for (const h of ['Name', 'Status', 'Start', 'End', 'Created by']) {
      await expect(page.getByRole('columnheader', { name: new RegExp(h, 'i') })).toBeVisible();
    }
    // The row + status badge + created_by visible
    const row = page.getByRole('row', { name: /UI list sample/i });
    await expect(row).toBeVisible();
    await expect(row.getByText(REDTEAM_USER)).toBeVisible();

    // Redteam sees the action buttons.
    await expect(page.getByRole('link', { name: /new engagement/i })).toBeVisible();
    await expect(row.getByRole('link', { name: /^edit$/i })).toBeVisible();
    await expect(row.getByRole('button', { name: /^delete$/i })).toBeVisible();

    // Soc should NOT see write buttons.
    await seedTokenInStorage(context, socToken);
    await page.goto('/engagements');
    const rowAsSoc = page.getByRole('row', { name: /UI list sample/i });
    await expect(rowAsSoc).toBeVisible();
    await expect(page.getByRole('link', { name: /new engagement/i })).toHaveCount(0);
    await expect(rowAsSoc.getByRole('link', { name: /^edit$/i })).toHaveCount(0);
    await expect(rowAsSoc.getByRole('button', { name: /^delete$/i })).toHaveCount(0);
  });

  test('AC-4.8 — /engagements/new form: client validation + API error display', async ({
    page,
    context,
  }) => {
    await seedTokenInStorage(context, redteamToken);

    await page.goto('/engagements/new');
    await expect(page.getByRole('heading', { name: /new engagement/i })).toBeVisible();

    // Submit empty → client-side errors visible.
    await page.getByRole('button', { name: /create engagement/i }).click();
    await expect(page.getByText(/name is required/i)).toBeVisible();
    await expect(page.getByText(/start date is required/i)).toBeVisible();

    // Fill bad date order → client validation flags end_date.
    await page.fill('#eng-name', 'UI form test');
    await page.fill('#eng-start', '2026-10-10');
    await page.fill('#eng-end', '2026-10-01');
    await page.getByRole('button', { name: /create engagement/i }).click();
    await expect(page.getByText(/end date must be on or after start date/i)).toBeVisible();

    // Fix dates → submit succeeds, redirects to detail.
    await page.fill('#eng-end', '2026-10-20');
    await page.getByRole('button', { name: /create engagement/i }).click();
    await page.waitForURL(/\/engagements\/\d+$/);
    await expect(page.getByRole('heading', { name: /UI form test/i })).toBeVisible();

    // Edit path: navigate to /edit and tweak.
    const detailUrl = page.url();
    const id = Number(detailUrl.split('/').pop());
    await page.goto(`/engagements/${id}/edit`);
    await expect(page.getByRole('heading', { name: /edit engagement/i })).toBeVisible();
    await page.fill('#eng-name', 'UI form test (edited)');
    await page.getByRole('button', { name: /save changes/i }).click();
    await page.waitForURL(new RegExp(`/engagements/${id}$`));
    await expect(page.getByRole('heading', { name: /UI form test \(edited\)/i })).toBeVisible();
  });

  test('AC-4.9 — /engagements/<id> detail page shows Simulations section (sprint 2 replaced placeholder)', async ({
    page,
    context,
  }) => {
    const seeded = await createEngagement(redteamToken, {
      name: 'AC-4.9 detail target',
      start_date: '2026-11-01',
      description: 'A description for detail rendering.',
    });
    await seedTokenInStorage(context, redteamToken);
    await page.goto(`/engagements/${seeded.id}`);
    await expect(page.getByRole('heading', { name: /AC-4.9 detail target/i })).toBeVisible();
    // Sprint 2 replaced the placeholder with the real SimulationList — covered by AC-7.5.
    await expect(page.getByRole('heading', { name: /simulations/i })).toBeVisible();
    // admin/redteam see the "Nouvelle simulation" button
    await expect(
      page.getByRole('link', { name: /nouvelle simulation/i }),
    ).toBeVisible();
  });
});
