/**
 * US-23 — Dark mode.
 * Covers AC-23.1 (toggle in topbar), AC-23.2 (3-state cycle), AC-23.3 (localStorage persistence).
 * AC-23.4/5/6 (Tailwind tokens, component audit, screenshots) are frontend-builder scope.
 */
import { test, expect } from '@playwright/test';
import {
  adminToken,
  deleteUserByUsername,
  ensureUser,
  login,
} from '../fixtures/api';
import { seedTokenInStorage } from '../fixtures/auth';

const REDTEAM_USER = 'us23-redteam';
const PASS = 'us23-pass-strong';

test.describe('US-23 — dark mode', () => {
  let redteamToken: string;

  test.beforeAll(async () => {
    await ensureUser(REDTEAM_USER, PASS, 'redteam');
    redteamToken = (await login(REDTEAM_USER, PASS)).token;
  });

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

  test('AC-23.1 — theme toggle button is visible in the topbar', async ({
    page,
    context,
  }) => {
    await seedTokenInStorage(context, redteamToken);
    await page.goto('/engagements');

    // The theme toggle has aria-label containing "Theme:" and the current mode
    const themeBtn = page.getByRole('button', { name: /theme:/i });
    await expect(themeBtn).toBeVisible();

    // Shows current theme label (Light, Dark, or System)
    const label = await themeBtn.textContent();
    expect(label).toMatch(/light|dark|system/i);
  });

  test('AC-23.2 — toggle cycles through 3 states: system → light → dark → system', async ({
    page,
    context,
  }) => {
    await seedTokenInStorage(context, redteamToken);
    await page.goto('/engagements');
    // Clear any stored theme via evaluate (not addInitScript — that would fire on every load)
    await page.evaluate(() => localStorage.removeItem('mimic-theme'));
    await page.reload();
    await page.waitForLoadState('networkidle');

    const themeBtn = page.getByRole('button', { name: /theme:/i });
    await expect(themeBtn).toBeVisible();

    // Collect 4 states to confirm a full cycle
    const states: string[] = [];
    for (let i = 0; i < 4; i++) {
      const label = await themeBtn.textContent();
      states.push(label?.trim().toLowerCase() ?? '');
      await themeBtn.click();
      await page.waitForTimeout(100);
    }

    // Must contain all 3 modes within the cycle
    expect(states.some(s => s.includes('system'))).toBe(true);
    expect(states.some(s => s.includes('light'))).toBe(true);
    expect(states.some(s => s.includes('dark'))).toBe(true);
    // 4th state must equal 1st (full cycle completed)
    expect(states[3]).toBe(states[0]);
  });

  test('AC-23.3 — theme persists in localStorage under "mimic-theme"', async ({
    page,
    context,
  }) => {
    await seedTokenInStorage(context, redteamToken);
    await page.goto('/engagements');

    // Clear any stored theme via evaluate (NOT addInitScript — that runs on reload too)
    await page.evaluate(() => localStorage.removeItem('mimic-theme'));
    await page.reload();
    await page.waitForLoadState('networkidle');

    const themeBtn = page.getByRole('button', { name: /theme:/i });
    await expect(themeBtn).toBeVisible();

    // Click until we reach "dark"
    for (let i = 0; i < 5; i++) {
      const label = await themeBtn.textContent();
      if (label?.toLowerCase().includes('dark')) break;
      await themeBtn.click();
      await page.waitForTimeout(100);
    }

    // Read localStorage — must be 'dark'
    const stored = await page.evaluate(() => localStorage.getItem('mimic-theme'));
    expect(stored).toBe('dark');

    // Reload page — should restore dark mode (localStorage persists across reload)
    await page.reload();
    await page.waitForLoadState('networkidle');
    const themeAfterReload = page.getByRole('button', { name: /theme:/i });
    await expect(themeAfterReload).toBeVisible();
    const labelAfterReload = await themeAfterReload.textContent();
    expect(labelAfterReload?.toLowerCase()).toContain('dark');

    // html element should have class "dark"
    const hasDarkClass = await page.evaluate(() => document.documentElement.classList.contains('dark'));
    expect(hasDarkClass).toBe(true);
  });

  test('AC-23.3 — default is "system" when no localStorage value', async ({
    page,
    context,
  }) => {
    await seedTokenInStorage(context, redteamToken);
    await page.goto('/engagements');
    // Clear theme via evaluate then reload so page renders with no stored theme
    await page.evaluate(() => localStorage.removeItem('mimic-theme'));
    await page.reload();
    await page.waitForLoadState('networkidle');

    const stored = await page.evaluate(() => localStorage.getItem('mimic-theme'));
    // Default state: localStorage not yet set (null) OR set to 'system'
    expect(stored === null || stored === 'system').toBe(true);

    const themeBtn = page.getByRole('button', { name: /theme:/i });
    const label = await themeBtn.textContent();
    // Initial label should be System (or light if system resolves to light)
    expect(label?.toLowerCase()).toMatch(/system|light|dark/);
  });

  test('AC-23.1 — dark mode: html has class "dark" when dark selected', async ({
    page,
    context,
  }) => {
    await seedTokenInStorage(context, redteamToken);
    await page.goto('/engagements');
    // Set dark theme via evaluate then reload so React reads it on mount
    await page.evaluate(() => localStorage.setItem('mimic-theme', 'dark'));
    await page.reload();
    await page.waitForLoadState('networkidle');

    // Wait for React to apply the dark class via useEffect
    await page.waitForFunction(() => document.documentElement.classList.contains('dark'), { timeout: 3_000 });
    const hasDarkClass = await page.evaluate(() =>
      document.documentElement.classList.contains('dark'),
    );
    expect(hasDarkClass).toBe(true);
  });

  test('AC-23.1 — light mode: html does NOT have class "dark" when light selected', async ({
    page,
    context,
  }) => {
    await seedTokenInStorage(context, redteamToken);
    await page.goto('/engagements');
    await page.evaluate(() => localStorage.setItem('mimic-theme', 'light'));
    await page.reload();
    await page.waitForLoadState('networkidle');

    const hasDarkClass = await page.evaluate(() =>
      document.documentElement.classList.contains('dark'),
    );
    expect(hasDarkClass).toBe(false);
  });
});
