/**
 * V2 Smoke Test — 验证主题切换/登录/导航/Modal 等关键交互在 V2 改动下没坏。
 *
 * Run: node test/v2-smoke.mjs
 * Pre-req: backend 8000 + frontend 5173 都已启动
 */
import { chromium } from '@playwright/test';

const BASE = 'http://localhost:5173';
const API = 'http://localhost:8000';
const ADMIN = { username: 'admin', password: 'admin123' };
const TEAM_USER = { username: 'screenshot_user', password: 'shotpass123' };

const results = [];
const errors = [];

function pass(name) {
  results.push({ name, status: '✓ PASS' });
  console.log(`  ✓ ${name}`);
}
function fail(name, err) {
  results.push({ name, status: '✗ FAIL' });
  errors.push({ name, err: err?.message || String(err) });
  console.log(`  ✗ ${name}: ${err?.message || err}`);
}

async function login(page, creds) {
  const res = await page.request.post(`${API}/api/v1/auth/login`, { data: creds });
  if (!res.ok()) throw new Error(`login ${creds.username}: ${res.status()}`);
  const body = await res.json();
  await page.goto(`${BASE}/login`, { waitUntil: 'domcontentloaded' });
  await page.evaluate(({ access, refresh, user }) => {
    localStorage.setItem('access_token', access);
    if (refresh) localStorage.setItem('refresh_token', refresh);
    if (user) localStorage.setItem('user', JSON.stringify(user));
  }, { access: body?.tokens?.access, refresh: body?.tokens?.refresh, user: body?.user });
}

async function main() {
  const browser = await chromium.launch({ headless: true });
  const ctx = await browser.newContext({ viewport: { width: 1440, height: 900 } });
  const page = await ctx.newPage();

  const consoleErrors = [];
  const pageErrors = [];
  page.on('console', (msg) => {
    if (msg.type() === 'error') {
      const t = msg.text();
      // 忽略已知 API 401/404 (test_db 数据缺失) + DevTools React 警告
      if (/401|404|Failed to load resource|Download the React DevTools/.test(t)) return;
      consoleErrors.push(t);
    }
  });
  page.on('pageerror', (e) => pageErrors.push(e.message));

  console.log('\n══════════════════════════════════════════');
  console.log(' V2 SMOKE TEST — 主题切换 / 登录 / 导航');
  console.log('══════════════════════════════════════════');

  // ─────────────────────────────────────
  // Group 1: Login page (no auth) + LoginModal submitBtn
  // ─────────────────────────────────────
  console.log('\n[1/5] Login Page + LoginModal');
  try {
    await page.goto(`${BASE}/login`, { waitUntil: 'domcontentloaded' });
    await page.waitForTimeout(800);

    // LoginModal 应自动弹出 (autoLogin)
    const hasLoginModal = await page.locator('input[type="text"]').first().isVisible();
    if (hasLoginModal) pass('1.1 LoginModal 自动弹出 (autoLogin)');
    else fail('1.1 LoginModal 自动弹出', new Error('找不到用户名输入框'));

    // 数据-theme 属性默认应 dark
    const theme1 = await page.evaluate(() => document.documentElement.dataset.theme);
    if (theme1 === 'dark') pass('1.2 默认主题=dark');
    else fail('1.2 默认主题=dark', new Error(`got ${theme1}`));

    // V2: 浅色切换后 LoginModal 仍可见 (LandingPage data-theme="dark" 被移除)
    await page.evaluate(() => {
      localStorage.setItem('airdrama-theme', 'light');
      document.documentElement.dataset.theme = 'light';
    });
    await page.reload({ waitUntil: 'domcontentloaded' });
    await page.waitForTimeout(800);
    const lightLoginVisible = await page.locator('input[type="text"]').first().isVisible();
    if (lightLoginVisible) pass('1.3 浅色 LoginModal 仍渲染 (V2 关键)');
    else fail('1.3 浅色 LoginModal 仍渲染', new Error('浅色下 LoginModal 不可见'));

    // submitBtn 仍可点 (V2 字重 600 + 加 box-shadow,不改 onClick)
    const submitBtn = page.locator('button:has-text("登录")').first();
    const submitVisible = await submitBtn.isVisible();
    if (submitVisible) pass('1.4 submitBtn 在浅色下仍可见可点');
    else fail('1.4 submitBtn', new Error('登录按钮不可见'));
  } catch (e) {
    fail('1.x LoginPage block', e);
  }

  // ─────────────────────────────────────
  // Group 2: 实际登录 — admin
  // ─────────────────────────────────────
  console.log('\n[2/5] 登录 admin 走完真实流程');
  try {
    await page.evaluate(() => { localStorage.clear(); });
    await page.goto(`${BASE}/login`, { waitUntil: 'domcontentloaded' });
    await page.waitForTimeout(600);

    await page.locator('input[type="text"]').first().fill(ADMIN.username);
    await page.locator('input[type="password"]').first().fill(ADMIN.password);
    await page.locator('button:has-text("登录")').first().click();
    await page.waitForURL((url) => url.pathname.startsWith('/admin'), { timeout: 8000 });
    pass('2.1 admin 登录成功 → 跳转 /admin/*');

    // access_token 已持久化
    const hasToken = await page.evaluate(() => !!localStorage.getItem('access_token'));
    if (hasToken) pass('2.2 access_token 写入 localStorage');
    else fail('2.2 access_token', new Error('localStorage 没有 access_token'));
  } catch (e) {
    fail('2.x admin 登录', e);
  }

  // ─────────────────────────────────────
  // Group 3: Theme toggle 端到端
  // ─────────────────────────────────────
  console.log('\n[3/5] Theme Toggle 端到端');
  try {
    // 确保深色态进入
    await page.evaluate(() => {
      localStorage.setItem('airdrama-theme', 'dark');
      document.documentElement.dataset.theme = 'dark';
    });
    await page.reload({ waitUntil: 'domcontentloaded' });
    await page.waitForTimeout(800);

    const themeBefore = await page.evaluate(() => document.documentElement.dataset.theme);
    if (themeBefore === 'dark') pass('3.1 初始 theme=dark');
    else fail('3.1 初始 theme', new Error(`got ${themeBefore}`));

    // 找 Sidebar 切换按钮 (aria-label 含 "切换到浅色主题")
    const toggle = page.locator('button[aria-label*="切换"]').first();
    const toggleVisible = await toggle.isVisible();
    if (toggleVisible) pass('3.2 Sidebar 主题切换按钮可见');
    else fail('3.2 切换按钮', new Error('找不到切换按钮'));

    if (toggleVisible) {
      await toggle.click();
      await page.waitForTimeout(500);
      const themeAfter = await page.evaluate(() => document.documentElement.dataset.theme);
      if (themeAfter === 'light') pass('3.3 点按钮 → theme=light');
      else fail('3.3 切换到 light', new Error(`got ${themeAfter}`));

      // localStorage 持久化
      const stored = await page.evaluate(() => localStorage.getItem('airdrama-theme'));
      if (stored === 'light') pass('3.4 localStorage 持久化 light');
      else fail('3.4 localStorage', new Error(`got ${stored}`));

      // 刷新后保持
      await page.reload({ waitUntil: 'domcontentloaded' });
      await page.waitForTimeout(600);
      const themePersisted = await page.evaluate(() => document.documentElement.dataset.theme);
      if (themePersisted === 'light') pass('3.5 刷新后 theme=light 保持');
      else fail('3.5 刷新持久化', new Error(`got ${themePersisted}`));

      // 切回深色
      await toggle.click();
      await page.waitForTimeout(300);
      const themeBack = await page.evaluate(() => document.documentElement.dataset.theme);
      if (themeBack === 'dark') pass('3.6 再点 → theme=dark (来回切换 OK)');
      else fail('3.6 切回 dark', new Error(`got ${themeBack}`));
    }
  } catch (e) {
    fail('3.x theme toggle', e);
  }

  // ─────────────────────────────────────
  // Group 4: Admin sidebar nav (V2 AdminLayout transparent + glass 改动)
  // ─────────────────────────────────────
  console.log('\n[4/5] Admin 侧栏导航');
  const adminRoutes = [
    { name: '用户管理', url: '/admin/users' },
    { name: '消费记录', url: '/admin/records' },
    { name: '内容资产', url: '/admin/assets' },
    { name: '系统设置', url: '/admin/settings' },
    { name: '安全日志', url: '/admin/security' },
    { name: '操作日志', url: '/admin/logs' },
    { name: '团队管理', url: '/admin/teams' },
  ];
  try {
    for (const r of adminRoutes) {
      try {
        await page.goto(`${BASE}${r.url}`, { waitUntil: 'domcontentloaded' });
        await page.waitForTimeout(600);
        // 页面 mount 标志:body 不为空 + 找到 sidebar 主导航
        const sidebarVisible = await page.locator('text=AirDrama Admin').first().isVisible().catch(() => false);
        if (sidebarVisible) pass(`4.x ${r.name} (${r.url}) 渲染 OK + sidebar 在`);
        else fail(`4.x ${r.name}`, new Error('sidebar 或页面没渲染'));
      } catch (e) {
        fail(`4.x ${r.name}`, e);
      }
    }
  } catch (e) {
    fail('4.x admin 导航', e);
  }

  // ─────────────────────────────────────
  // Group 5: Team user → 生成页 + Modal 交互
  // ─────────────────────────────────────
  console.log('\n[5/5] 团管用户 → 生成页 + AnnouncementModal');
  try {
    await ctx.clearCookies();
    await page.evaluate(() => localStorage.clear());
    await login(page, TEAM_USER);
    await page.goto(`${BASE}/app`, { waitUntil: 'domcontentloaded' });
    await page.waitForTimeout(1200);

    // 生成页 sidebar 可见 (component/Sidebar 76px 窄版,V2 玻璃)
    const genSidebar = page.locator('aside').first();
    const sidebarOk = await genSidebar.isVisible();
    if (sidebarOk) pass('5.1 生成页 Sidebar (76px 玻璃版) 可见');
    else fail('5.1 生成页 Sidebar', new Error('找不到 aside 元素'));

    // 主题切换按钮在
    const themeBtn = page.locator('button[aria-label*="切换"]').first();
    const themeBtnOk = await themeBtn.isVisible();
    if (themeBtnOk) pass('5.2 团管 Sidebar 主题切换按钮在');
    else fail('5.2 主题切换按钮', new Error('团管侧栏没切换按钮'));

    // 公告弹窗 — first-visit only,所以用 soft check (有就关掉,没有跳过)
    const announcement = page.locator('text=AirDrama 使用说明').first();
    const annOk = await announcement.isVisible({ timeout: 1500 }).catch(() => false);
    if (annOk) {
      pass('5.3 AnnouncementModal 弹出 (首次访问)');
      const closeBtn = page.locator('button:has-text("我知道了")').first();
      if (await closeBtn.isVisible().catch(() => false)) {
        await closeBtn.click();
        await page.waitForTimeout(400);
        const annStillOpen = await announcement.isVisible().catch(() => false);
        if (!annStillOpen) pass('5.4 公告 modal 关闭按钮 OK');
        else fail('5.4 公告关闭', new Error('点了我知道了 modal 还在'));
      }
    } else {
      pass('5.3 公告 modal soft-skip (已读过)');
    }

    // 5.5 生成页 InputBar 核心元素 — prompt 输入区可见
    const promptInput = page.locator('[contenteditable="true"]').first();
    const inputOk = await promptInput.isVisible({ timeout: 3000 }).catch(() => false);
    if (inputOk) pass('5.5 InputBar prompt 输入区可见 (生成页核心)');
    else fail('5.5 InputBar', new Error('找不到 contenteditable prompt 输入'));
  } catch (e) {
    fail('5.x team user 生成页', e);
  }

  // ─────────────────────────────────────
  // 收尾 — console / pageerror
  // ─────────────────────────────────────
  if (consoleErrors.length > 0) {
    fail('JS 运行错误', new Error(`${consoleErrors.length} 个 console.error:\n  ` + consoleErrors.slice(0, 5).join('\n  ')));
  } else {
    pass('全程无 console.error');
  }
  if (pageErrors.length > 0) {
    fail('JS 异常崩溃', new Error(`${pageErrors.length} 个未捕获:\n  ` + pageErrors.slice(0, 5).join('\n  ')));
  } else {
    pass('全程无 page crash');
  }

  await browser.close();

  // 汇总
  console.log('\n══════════════════════════════════════════');
  console.log(' 汇总');
  console.log('══════════════════════════════════════════');
  const passCount = results.filter(r => r.status.includes('PASS')).length;
  const failCount = results.filter(r => r.status.includes('FAIL')).length;
  console.log(`Pass: ${passCount}    Fail: ${failCount}`);
  if (failCount > 0) {
    console.log('\n❌ 失败明细:');
    errors.forEach(e => console.log(`  • ${e.name}: ${e.err}`));
    process.exit(1);
  }
  console.log('\n✅ 全部 smoke check 通过');
}

main().catch(err => {
  console.error('❌ Smoke test crashed:', err);
  process.exit(1);
});
