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

// Ops-gated install e2e (SP3 W3). Proves the onboarding appearance wizard end-to-end against a live
// dev server — unlike e2e/admin.spec.ts (pre-seeded admin on an already-installed branch), this
// requires a FRESH throwaway Neon branch: auth schema applied (src/db/auth-schema.sql), NO admin
// user, site_configured unset/false. Successful install FINALIZES the branch — re-run needs a new
// branch (or reset), same as a first real deploy.
//
// Copy .dev.vars.example → .dev.vars (gitignored). DATABASE_URL → the clean branch; SITE_ORIGIN
// MUST equal baseURL (http://localhost:4331) or same-origin CSRF rejects POST /api/install.

type AppearanceOptions = {
  palettes: { id: string; name: string }[];
  themes: { id: string; name: string }[];
  defaults: { paletteId: string; themeId: string; mode: string };
};

async function readAppearanceOptions(page: import('@playwright/test').Page): Promise<AppearanceOptions> {
  const raw = await page.locator('#appearance-options').getAttribute('data-options');
  expect(raw, 'appearance-options data-options must be present').toBeTruthy();
  return JSON.parse(raw!) as AppearanceOptions;
}

async function clickContinue(page: import('@playwright/test').Page): Promise<void> {
  await page.getByRole('button', { name: /Continue|Finish setup/ }).click();
}

async function fillCoreSteps(
  page: import('@playwright/test').Page,
  stamp: number,
): Promise<{ email: string; password: string; siteTitle: string }> {
  const siteTitle = `E2e site ${stamp}`;
  const email = `install-${stamp}@e2e.test`;
  const password = `e2e-install-pw-${stamp}`;

  await page.goto('/install');
  await expect(page.locator('#install-step-title')).toHaveText('Choose your language');
  await clickContinue(page);

  await expect(page.locator('#install-step-title')).toHaveText('Name your site');
  await page.getByLabel('Site title').fill(siteTitle);
  await clickContinue(page);

  await expect(page.locator('#install-step-title')).toHaveText('Create your admin account');
  await page.getByLabel('Admin email').fill(email);
  await page.getByLabel('Admin password').fill(password);
  await clickContinue(page);

  await expect(page.locator('#install-step-title')).toHaveText('Choose your site appearance');

  return { email, password, siteTitle };
}

async function fillToStep(page: Page, step: 2 | 3): Promise<void> {
  await page.goto('/install');
  await expect(page.locator('#install-step-title')).toHaveText('Choose your language');
  await clickContinue(page);
  await expect(page.locator('#install-step-title')).toHaveText('Name your site');

  if (step === 2) return;

  await page.getByLabel('Site title').fill('Validation test site');
  await clickContinue(page);
  await expect(page.locator('#install-step-title')).toHaveText('Create your admin account');
}

function pickNonDefaultAppearance(options: AppearanceOptions): {
  palette: { id: string; name: string };
  theme: { id: string; name: string };
  mode: 'dark' | 'light';
} {
  const palette =
    options.palettes.find((p) => p.id !== options.defaults.paletteId) ?? options.palettes.at(-1)!;
  const theme =
    options.themes.find((t) => t.id !== options.defaults.themeId) ?? options.themes.at(-1)!;
  // Explicit non-system mode so data-mode is assertable on /admin (system omits the attribute).
  const mode: 'dark' | 'light' = options.defaults.mode === 'dark' ? 'light' : 'dark';
  return { palette, theme, mode };
}

test.describe.configure({ mode: 'serial' });

test('T1: validation — step 2 site title required', async ({ page }) => {
  await fillToStep(page, 2);

  await clickContinue(page);
  await expect(page.locator('#siteTitle-error')).toBeVisible();
  await expect(page.locator('#siteTitle-error')).toContainText('Site title is required.');
  await expect(page.locator('#install-step-title')).toHaveText('Name your site');

  // > 100 chars unreachable via UI — maxLength={100} on input enforces at browser layer
  await page.getByLabel('Site title').fill('Valid title');
  await clickContinue(page);
  await expect(page.locator('#install-step-title')).toHaveText('Create your admin account');
  await expect(page.locator('#siteTitle-error')).toBeHidden();
});

test('T2: validation — step 3 admin credentials', async ({ page }) => {
  await fillToStep(page, 3);

  await clickContinue(page);
  await expect(page.locator('#adminEmail-error')).toBeVisible();
  await expect(page.locator('#adminEmail-error')).toContainText('That email is not valid.');
  await expect(page.locator('#adminPassword-error')).toBeVisible();
  await expect(page.locator('#adminPassword-error')).toContainText('Password must be at least 12 characters.');
  await expect(page.locator('#install-step-title')).toHaveText('Create your admin account');

  await page.getByLabel('Admin email').fill('test@example.com');
  await page.getByLabel('Admin password').fill('short');
  await clickContinue(page);
  await expect(page.locator('#adminEmail-error')).toBeHidden();
  await expect(page.locator('#adminPassword-error')).toBeVisible();

  await page.getByLabel('Admin password').fill('long-enough-pw-12chars');
  await clickContinue(page);
  await expect(page.locator('#install-step-title')).toHaveText('Choose your site appearance');
});

test('T3: back navigation', async ({ page }) => {
  const consoleProblems: string[] = [];
  page.on('console', (msg) => {
    if (msg.type() === 'error' || msg.type() === 'warning')
      consoleProblems.push(`[${msg.type()}] ${msg.text()}`);
  });

  const stamp = Date.now();
  await fillCoreSteps(page, stamp);

  await expect(page.locator('#install-back')).toBeVisible();
  await page.locator('#install-back').click();
  await expect(page.locator('#install-step-title')).toHaveText('Create your admin account');
  await page.locator('#install-back').click();
  await expect(page.locator('#install-step-title')).toHaveText('Name your site');
  await page.locator('#install-back').click();
  await expect(page.locator('#install-step-title')).toHaveText('Choose your language');
  await expect(page.locator('#install-back')).toBeHidden();

  expect(consoleProblems, 'no console errors or warnings').toHaveLength(0);
});

test('step 4 controls, setup-method fork, and review summary', async ({ page }) => {
  const consoleProblems: string[] = [];
  page.on('console', (msg) => {
    if (msg.type() === 'error' || msg.type() === 'warning')
      consoleProblems.push(`[${msg.type()}] ${msg.text()}`);
  });

  const stamp = Date.now();
  await fillCoreSteps(page, stamp);

  const options = await readAppearanceOptions(page);
  const paletteRadios = page.locator('input[name="paletteId"]');
  const modeRadios = page.locator('input[name="mode"]');
  const themeRadios = page.locator('input[name="themeId"]');

  await expect(paletteRadios).toHaveCount(options.palettes.length);
  await expect(themeRadios).toHaveCount(options.themes.length);
  await expect(modeRadios).toHaveCount(3);

  for (const palette of options.palettes) {
    await expect(page.locator(`input[name="paletteId"][value="${palette.id}"]`)).toHaveCount(1);
  }
  for (const theme of options.themes) {
    await expect(page.locator(`input[name="themeId"][value="${theme.id}"]`)).toHaveCount(1);
  }

  const { palette, theme, mode } = pickNonDefaultAppearance(options);
  await page.locator(`input[name="paletteId"][value="${palette.id}"]`).check();
  await page.locator(`input[name="mode"][value="${mode}"]`).check();
  await page.locator(`input[name="themeId"][value="${theme.id}"]`).check({ force: true });

  // T4: iframe src tracks appearance selections
  const iframeSrc0 = await page.locator('#theme-preview-iframe').getAttribute('src');
  const params0 = new URLSearchParams(new URL(iframeSrc0!, 'http://localhost:4331').search);
  expect(params0.get('theme')).toBe(theme.id);

  const iframeSrc1 = await page.locator('#theme-preview-iframe').getAttribute('src');
  const params1 = new URLSearchParams(new URL(iframeSrc1!, 'http://localhost:4331').search);
  expect(params1.get('palette')).toBe(palette.id);

  const iframeSrc2 = await page.locator('#theme-preview-iframe').getAttribute('src');
  const params2 = new URLSearchParams(new URL(iframeSrc2!, 'http://localhost:4331').search);
  expect(params2.get('mode')).toBe(mode);
  expect(params2.get('name')).not.toBe('Your site');
  expect(params2.get('name')).toBeTruthy();

  await clickContinue(page);

  await expect(page.locator('#install-step-title')).toHaveText('How do you want to set up your site?');
  const agentPanel = page.locator('#agent-panel');
  await expect(agentPanel).toBeHidden();

  await page.getByRole('radio', { name: 'By Agent' }).check();
  await expect(agentPanel).toBeVisible();
  await expect(page.locator('#setup-method-agent')).toHaveAttribute('aria-expanded', 'true');

  await page.getByRole('radio', { name: /Manually/ }).check();
  await expect(agentPanel).toBeHidden();
  await expect(page.locator('#setup-method-agent')).toHaveAttribute('aria-expanded', 'false');
  await clickContinue(page);

  await expect(page.locator('#install-step-title')).toHaveText('Finish setup');
  const review = page.locator('#install-review');
  await expect(review.locator('dt', { hasText: 'Appearance' })).toBeVisible();
  await expect(review).toContainText(`Palette: ${palette.name}`);
  await expect(review).toContainText(`Mode: ${mode === 'dark' ? 'Dark' : 'Light'}`);
  await expect(review).toContainText(`Theme: ${theme.name}`);
  await expect(review.locator('dt', { hasText: 'Setup method' })).toBeVisible();
  await expect(review).toContainText('Manually');

  expect(consoleProblems, 'no console errors or warnings').toHaveLength(0);
});

test('full happy path persists appearance to the admin shell', async ({ page }) => {
  const consoleProblems: string[] = [];
  page.on('console', (msg) => {
    if (msg.type() === 'error' || msg.type() === 'warning')
      consoleProblems.push(`[${msg.type()}] ${msg.text()}`);
  });

  const stamp = Date.now();
  await fillCoreSteps(page, stamp);

  const options = await readAppearanceOptions(page);
  const { palette, mode } = pickNonDefaultAppearance(options);

  await page.locator(`input[name="paletteId"][value="${palette.id}"]`).check();
  await page.locator(`input[name="mode"][value="${mode}"]`).check();
  await page.locator('input[name="themeId"][value="editorial"]').check({ force: true });
  await clickContinue(page);

  await expect(page.locator('#install-step-title')).toHaveText('How do you want to set up your site?');
  await page.getByRole('radio', { name: /Manually/ }).check();
  await clickContinue(page);

  await expect(page.locator('#install-step-title')).toHaveText('Finish setup');
  await expect(page.locator('#install-review')).toContainText(`Palette: ${palette.name}`);

  await page.getByRole('button', { name: 'Finish setup' }).click();
  await page.waitForURL('**/admin');

  const html = page.locator('html');
  await expect(html).toHaveAttribute('data-theme', 'mod-cms');
  await expect(html).toHaveAttribute('data-palette', palette.id);
  await expect(html).toHaveAttribute('data-mode', mode);
  await expect(page.locator('.editorial-shell')).toHaveCount(1);
  await expect(page.locator('header.site-header')).toHaveCount(0);

  // Proves session login succeeded (not bounced to /admin/login).
  await expect(page.getByRole('heading', { name: 'Content', exact: true })).toBeVisible();

  expect(consoleProblems, 'no console errors or warnings').toHaveLength(0);
});
