/**
 * Theme Switching Behavior Tests
 *
 * Complements `theme-visual-regression.spec.js` (which only checks how each
 * theme *looks* by setting `data-theme` directly via JS) with tests for the
 * actual user-facing theme-switching workflow:
 *
 *   1. The theme dropdown exists and is interactive
 *   2. Selecting a theme applies it immediately (live `data-theme` change)
 *   3. The theme persists across a page reload (server-side preference saved)
 *   4. Switching to a different theme replaces the previous one
 *   5. The dropdown's selected value reflects the active theme
 *
 * If theme persistence breaks (e.g., the `app.theme` setting save endpoint
 * regresses), no other test catches it today — they all bypass the dropdown
 * and apply themes via JS. This spec exercises the full round-trip.
 *
 * Note: Authentication is handled by auth.setup.js via storageState
 */

import { test, expect } from '@playwright/test';

const THEME_DROPDOWN = '#theme-dropdown';

// Three themes covering distinct categories, chosen so we don't depend on the
// dropdown's default — whichever the test starts with, all three options
// remain meaningful targets to switch *to*.
const TEST_THEMES = ['nord', 'light', 'midnight'];

test.describe('Theme Switching Behavior', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('/');
    await page.waitForSelector(THEME_DROPDOWN, { timeout: 10000 });
  });

  test('theme dropdown is present and contains expected options', async ({ page }) => {
    const dropdown = page.locator(THEME_DROPDOWN);
    await expect(dropdown).toBeVisible();

    // The defaults file ships 33 themes; we just check >= 10 to avoid
    // brittleness if a single theme is added or removed.
    const optionCount = await dropdown.locator('option').count();
    expect(optionCount, 'theme dropdown should have many options').toBeGreaterThanOrEqual(10);

    // Spot-check that a few well-known themes are present.
    const optionValues = await dropdown.locator('option').evaluateAll(
      (opts) => opts.map((o) => o.value),
    );
    expect(optionValues, 'should contain known themes').toEqual(
      expect.arrayContaining(TEST_THEMES),
    );
  });

  test('selecting a theme applies it immediately', async ({ page }) => {
    const dropdown = page.locator(THEME_DROPDOWN);

    // Pick a target that's different from whatever the page started on, so
    // the assertion isn't trivially satisfied by the initial state.
    const initial = await page.evaluate(() =>
      document.documentElement.getAttribute('data-theme'),
    );
    const target = TEST_THEMES.find((t) => t !== initial) || TEST_THEMES[0];

    await dropdown.selectOption(target);

    // Theme apply is synchronous in the JS layer — give a small grace period
    // for the attribute mutation to land.
    await expect.poll(
      async () => page.evaluate(() => document.documentElement.getAttribute('data-theme')),
      { timeout: 2000, message: `data-theme should become "${target}"` },
    ).toBe(target);
  });

  test('theme persists across a page reload', async ({ page }) => {
    const dropdown = page.locator(THEME_DROPDOWN);
    const initial = await page.evaluate(() =>
      document.documentElement.getAttribute('data-theme'),
    );
    const target = TEST_THEMES.find((t) => t !== initial) || TEST_THEMES[0];

    await dropdown.selectOption(target);

    // Wait for the change to take effect before reload — without this, the
    // PUT to /settings/api/app.theme can still be in flight when we navigate.
    await expect.poll(
      async () => page.evaluate(() => document.documentElement.getAttribute('data-theme')),
      { timeout: 2000 },
    ).toBe(target);

    await page.reload();
    await page.waitForSelector(THEME_DROPDOWN, { timeout: 10000 });

    // After reload, the saved theme should be re-applied from the server-side
    // preference.
    const afterReload = await page.evaluate(() =>
      document.documentElement.getAttribute('data-theme'),
    );
    expect(afterReload, 'theme should persist across reload').toBe(target);
  });

  test('switching twice replaces the previous theme', async ({ page }) => {
    const dropdown = page.locator(THEME_DROPDOWN);

    // First switch
    await dropdown.selectOption('nord');
    await expect.poll(
      async () => page.evaluate(() => document.documentElement.getAttribute('data-theme')),
      { timeout: 2000 },
    ).toBe('nord');

    // Second switch — the new theme should fully replace, not stack
    await dropdown.selectOption('light');
    await expect.poll(
      async () => page.evaluate(() => document.documentElement.getAttribute('data-theme')),
      { timeout: 2000 },
    ).toBe('light');

    // No multiple data-theme attributes
    const themeAttrCount = await page.evaluate(
      () => Array.from(document.documentElement.attributes)
        .filter((a) => a.name === 'data-theme')
        .length,
    );
    expect(themeAttrCount, 'exactly one data-theme attribute').toBe(1);
  });
});
