/**
 * US-31 — Export robustness: format validation and edge cases.
 *
 * All 4 ACs use direct API calls (no UI needed) for speed and determinism.
 *
 * AC-31.1: missing ?format → 400 friendly message
 * AC-31.2: ?format=xml → 400 friendly message
 * AC-31.3: engagement 99999 → 404
 * AC-31.4: engagement with 0 simulations → export OK (CSV = 1 header row only)
 */
import { test, expect } from '@playwright/test';
import {
  adminToken,
  createEngagement,
  deleteEngagement,
  deleteUserByUsername,
  ensureUser,
  login,
} from '../fixtures/api';

const CSV_HEADER_COLS = [
  'Scénario',
  'Test',
  'Source de log',
  'Commentaires SOC',
  'Exécution',
  'Logs remontés au SIEM',
  'Cyber incident',
];

const ADMIN_USER = 'us31-admin';
const PASS = 'us31-pass-strong!';

const BASE_URL = process.env.MIMIC_BASE_URL ?? 'http://localhost:5000';

/**
 * RFC-4180-aware row counter — same logic as us29 helper.
 * Embedded newlines inside quoted cells are not counted as row breaks.
 */
function countCsvRows(csv: string): number {
  let inQuote = false;
  let rowCount = 0;
  let lineStart = 0;

  for (let i = 0; i < csv.length; i++) {
    const ch = csv[i];
    if (ch === '"') {
      if (inQuote && csv[i + 1] === '"') {
        i++;
      } else {
        inQuote = !inQuote;
      }
    } else if ((ch === '\n' || ch === '\r') && !inQuote) {
      if (ch === '\r' && csv[i + 1] === '\n') i++;
      const line = csv.slice(lineStart, i).trim();
      if (line.length > 0) rowCount++;
      lineStart = i + 1;
    }
  }
  const tail = csv.slice(lineStart).trim();
  if (tail.length > 0) rowCount++;
  return rowCount;
}

test.describe('US-31 — Export robustness', () => {
  let adminTok: string;

  test.beforeAll(async () => {
    await ensureUser(ADMIN_USER, PASS, 'admin');
    adminTok = (await login(ADMIN_USER, PASS)).token;
  });

  test.afterAll(async () => {
    try {
      const rootTok = await adminToken();
      await deleteUserByUsername(rootTok, ADMIN_USER);
    } catch { /* noop */ }
  });

  // AC-31.1 — missing format → 400
  test('AC-31.1 — GET /export without format → 400 with friendly error', async ({
    request,
  }) => {
    // Need a valid engagement id — use admin to create one transiently
    const engagement = await createEngagement(adminTok, {
      name: 'US31 missing format eng',
      start_date: '2026-01-01',
    });

    try {
      const response = await request.get(
        `${BASE_URL}/api/engagements/${engagement.id}/export`,
        { headers: { Authorization: `Bearer ${adminTok}` } },
      );
      expect(response.status()).toBe(400);
      const body = await response.json();
      expect(body).toHaveProperty('error');
      expect(body.error).toMatch(/format/i);
    } finally {
      await deleteEngagement(adminTok, engagement.id);
    }
  });

  // AC-31.2 — unknown format → 400
  test('AC-31.2 — GET /export?format=xml → 400 with friendly error', async ({
    request,
  }) => {
    const engagement = await createEngagement(adminTok, {
      name: 'US31 bad format eng',
      start_date: '2026-01-01',
    });

    try {
      const response = await request.get(
        `${BASE_URL}/api/engagements/${engagement.id}/export?format=xml`,
        { headers: { Authorization: `Bearer ${adminTok}` } },
      );
      expect(response.status()).toBe(400);
      const body = await response.json();
      expect(body).toHaveProperty('error');
      expect(body.error).toMatch(/format/i);
    } finally {
      await deleteEngagement(adminTok, engagement.id);
    }
  });

  // AC-31.3 — unknown engagement → 404
  test('AC-31.3 — GET /engagements/99999/export?format=md → 404', async ({
    request,
  }) => {
    const response = await request.get(
      `${BASE_URL}/api/engagements/99999/export?format=md`,
      { headers: { Authorization: `Bearer ${adminTok}` } },
    );
    expect(response.status()).toBe(404);
  });

  // AC-31.4 — engagement with 0 simulations: export OK
  test('AC-31.4 — engagement with 0 simulations: Markdown export OK (header only)', async ({
    request,
  }) => {
    const engagement = await createEngagement(adminTok, {
      name: 'US31 empty engagement',
      start_date: '2026-01-01',
    });

    try {
      const response = await request.get(
        `${BASE_URL}/api/engagements/${engagement.id}/export?format=md`,
        { headers: { Authorization: `Bearer ${adminTok}` } },
      );
      expect(response.status()).toBe(200);
      const text = await response.text();
      // Engagement header section present
      expect(text).toContain('US31 empty engagement');
      // With 0 simulations the GFM table is absent (no rows to render)
      expect(text).not.toContain('| Scénario |');
    } finally {
      await deleteEngagement(adminTok, engagement.id);
    }
  });

  test('AC-31.4 — engagement with 0 simulations: CSV export has only 1 header row', async ({
    request,
  }) => {
    const engagement = await createEngagement(adminTok, {
      name: 'US31 empty CSV engagement',
      start_date: '2026-01-01',
    });

    try {
      const response = await request.get(
        `${BASE_URL}/api/engagements/${engagement.id}/export?format=csv`,
        { headers: { Authorization: `Bearer ${adminTok}` } },
      );
      expect(response.status()).toBe(200);
      const text = await response.text();
      // Count rows via RFC-4180-aware counter (handles embedded newlines in quoted cells)
      const rowCount = countCsvRows(text);
      expect(rowCount).toBe(1);
      // The single row is the header with exactly the 7 FR columns
      const headerCells = text.trim().split(',').map((c) => c.trim().replace(/^"|"$/g, ''));
      expect(headerCells).toEqual(CSV_HEADER_COLS);
    } finally {
      await deleteEngagement(adminTok, engagement.id);
    }
  });
});
