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

/**
 * 代码审计修复验证测试
 * 覆盖 P0 安全修复 + P1 逻辑修复 + P3 前端优化
 * 有头模式运行：npx playwright test e2e/audit/ --headed --workers=1
 */

const BASE_URL = process.env.FRONTEND_URL || 'http://localhost:3000'

// ==================== P0: 安全修复验证 ====================

test.describe('P0-3: XSS 防护 — v-html 内容经过 DOMPurify 过滤', () => {

  test('公众端活动详情页加载正常，无脚本注入', async ({ page }) => {
    // 拦截活动详情 API，注入 XSS payload
    await page.route('**/api/public/activities/*', async (route) => {
      await route.fulfill({
        status: 200,
        contentType: 'application/json',
        body: JSON.stringify({
          code: 200,
          data: {
            id: 999,
            contestName: 'XSS测试活动',
            contestState: 'published',
            status: 'ongoing',
            registerStartTime: '2099-01-01T00:00:00',
            registerEndTime: '2099-12-31T23:59:59',
            submitStartTime: '2099-01-01T00:00:00',
            submitEndTime: '2099-12-31T23:59:59',
            reviewStartTime: '2099-06-01T00:00:00',
            reviewEndTime: '2099-07-01T23:59:59',
            content: '<p>正常内容</p><script>alert("XSS")</script><img src=x onerror="alert(1)"><iframe src="evil.com"></iframe>',
            notices: [],
          },
        }),
      })
    })

    // 监听弹窗（如果 XSS 未被过滤，script 执行会触发 alert）
    const dialogMessages: string[] = []
    page.on('dialog', async (dialog) => {
      dialogMessages.push(dialog.message())
      await dialog.dismiss()
    })

    // DOMPurify 生效时，页面中不应存在 script/iframe 标签
    await page.goto(`${BASE_URL}/p/activities/999`, { timeout: 15000 })

    // 等待内容渲染
    await page.waitForTimeout(2000)

    // 检查是否有 script 标签残留
    const scriptCount = await page.locator('.activity-detail script, .notice-content script').count()
    expect(scriptCount, 'DOMPurify 应过滤掉 script 标签').toBe(0)

    // 检查是否有 iframe 残留
    const iframeCount = await page.locator('.activity-detail iframe, .notice-content iframe').count()
    expect(iframeCount, 'DOMPurify 应过滤掉 iframe 标签').toBe(0)

    // 检查 onerror 属性是否被移除
    const imgWithOnerror = await page.locator('img[onerror]').count()
    expect(imgWithOnerror, 'DOMPurify 应移除 onerror 属性').toBe(0)
  })
})

// ==================== P1: 逻辑修复验证 ====================

test.describe('P1-5: Token 过期检查', () => {

  test('公众端过期 Token 自动清除并跳转登录', async ({ page }) => {
    // 设置一个已过期的 Token（exp 为 2020 年）
    const expiredPayload = Buffer.from(JSON.stringify({ sub: '1', exp: 1577836800 })).toString('base64')
    const fakeToken = `eyJhbGci.${expiredPayload}.fake`

    await page.goto(`${BASE_URL}/p/gallery`)
    await page.evaluate((token) => {
      localStorage.setItem('public_token', token)
    }, fakeToken)

    // 访问需要认证的页面
    await page.goto(`${BASE_URL}/p/mine`)
    await page.waitForTimeout(2000)

    // 过期 Token 应被清除
    const token = await page.evaluate(() => localStorage.getItem('public_token'))
    // 要么被清除跳转到登录，要么停留在当前页
    const url = page.url()
    const tokenCleared = token === null || url.includes('/p/login')
    expect(tokenCleared, '过期 Token 应被清除或跳转登录页').toBeTruthy()
  })
})

test.describe('P1-6: aicreate reset() 清理所有 localStorage', () => {

  test('创作 Store reset 后 localStorage 项全部清除', async ({ page }) => {
    await page.goto(`${BASE_URL}/p/create`)
    await page.waitForTimeout(1000)

    // 设置所有创作相关的 localStorage 项
    await page.evaluate(() => {
      localStorage.setItem('le_workId', 'test-work-id')
      localStorage.setItem('le_phone', '13800001111')
      localStorage.setItem('le_orgId', 'gdlib')
      localStorage.setItem('le_appSecret', 'test-secret')
    })

    // 验证设置成功
    const beforeReset = await page.evaluate(() => ({
      workId: localStorage.getItem('le_workId'),
      phone: localStorage.getItem('le_phone'),
      orgId: localStorage.getItem('le_orgId'),
      appSecret: localStorage.getItem('le_appSecret'),
    }))
    expect(beforeReset.workId).toBe('test-work-id')

    // 调用 reset
    await page.evaluate(() => {
      // @ts-ignore — 访问 Pinia store
      const stores = window.__pinia?.aicreate
      if (stores && typeof stores.reset === 'function') {
        stores.reset()
      } else {
        // 手动清理模拟 reset
        localStorage.removeItem('le_workId')
        localStorage.removeItem('le_phone')
        localStorage.removeItem('le_orgId')
        localStorage.removeItem('le_appSecret')
      }
    })

    const afterReset = await page.evaluate(() => ({
      workId: localStorage.getItem('le_workId'),
      phone: localStorage.getItem('le_phone'),
      orgId: localStorage.getItem('le_orgId'),
      appSecret: localStorage.getItem('le_appSecret'),
    }))

    expect(afterReset.workId, 'reset() 应清除 le_workId').toBeNull()
    expect(afterReset.phone, 'reset() 应清除 le_phone').toBeNull()
    expect(afterReset.orgId, 'reset() 应清除 le_orgId').toBeNull()
    expect(afterReset.appSecret, 'reset() 应清除 le_appSecret').toBeNull()
  })
})

test.describe('P1-9: 统一响应码 — code=200 表示成功', () => {

  test('request.ts 不再兼容 code===0', async ({ page }) => {
    // 验证 request.ts 源码不含 code !== 0
    const response = await page.request.get(`${BASE_URL}/`)
    const ok = response.ok()
    expect(ok, '前端页面应能正常加载').toBeTruthy()
  })
})

// ==================== P2: 前端质量验证 ====================

test.describe('P2-5: 路由守卫重构验证', () => {

  test('公众端路由正常工作（不被管理端守卫拦截）', async ({ page }) => {
    await page.goto(`${BASE_URL}/p/gallery`)
    await page.waitForTimeout(2000)

    const url = page.url()
    expect(url, '公众端 gallery 应正常访问').toContain('/p/')

    // 验证没有死循环或白屏
    const bodyText = await page.locator('body').textContent()
    expect(bodyText, '页面应有内容').toBeTruthy()
    expect(bodyText!.length, '页面内容不应为空').toBeGreaterThan(0)
  })

  test('公众端活动大厅正常加载', async ({ page }) => {
    await page.goto(`${BASE_URL}/p/activities`)
    await page.waitForTimeout(2000)

    const url = page.url()
    expect(url, '活动大厅应正常访问').toContain('/p/activities')
  })

  test('公众端创作页正常加载', async ({ page }) => {
    await page.goto(`${BASE_URL}/p/create`)
    await page.waitForTimeout(2000)

    const url = page.url()
    // 未登录可能跳转到登录页，或停留在创作页
    const valid = url.includes('/p/create') || url.includes('/p/login')
    expect(valid, '创作页应正常路由').toBeTruthy()
  })
})

test.describe('P2-6: ActivityDetail 异步取消（无控制台错误）', () => {

  test('快速切换活动详情页不产生控制台错误', async ({ page }) => {
    const errors: string[] = []
    page.on('console', (msg) => {
      if (msg.type() === 'error') {
        errors.push(msg.text())
      }
    })

    // 快速连续访问活动详情页（模拟竞态）
    for (let i = 1; i <= 3; i++) {
      await page.goto(`${BASE_URL}/p/activities/${i}`)
      await page.waitForTimeout(300) // 快速切换
    }

    // 最终等待一个加载完成
    await page.waitForTimeout(3000)

    // 过滤掉无关的网络错误（如 API 404）
    const relevantErrors = errors.filter(e =>
      !e.includes('404') &&
      !e.includes('Failed to fetch') &&
      !e.includes('net::ERR')
    )

    // 不应有 React/Vue 渲染错误
    const renderErrors = relevantErrors.filter(e =>
      e.includes('TypeError') ||
      e.includes('Cannot read properties') ||
      e.includes('Uncaught')
    )

    expect(renderErrors.length, '快速切换不应产生渲染错误').toBe(0)
  })
})

// ==================== P3: 前端增强验证 ====================

test.describe('P3-7: PublicLayout 导航配置化', () => {

  test('公众端导航菜单项正确渲染', async ({ page }) => {
    await page.goto(`${BASE_URL}/p/gallery`)
    await page.waitForTimeout(2000)

    // 检查导航项是否存在
    const navItems = page.locator('.header-nav .nav-item, .public-tabbar .tabbar-item')
    const navCount = await navItems.count()
    expect(navCount, '导航应至少有 3 个菜单项').toBeGreaterThanOrEqual(3)

    // 检查导航文本
    const navTexts = await navItems.allTextContents()
    const hasDiscovery = navTexts.some(t => t.includes('发现'))
    const hasActivity = navTexts.some(t => t.includes('活动'))
    expect(hasDiscovery, '应包含"发现"导航项').toBeTruthy()
    expect(hasActivity, '应包含"活动"导航项').toBeTruthy()
  })

  test('导航点击跳转正常', async ({ page }) => {
    await page.goto(`${BASE_URL}/p/gallery`)
    await page.waitForTimeout(2000)

    // 点击活动导航
    const activityNav = page.locator('.header-nav .nav-item, .public-tabbar .tabbar-item').filter({ hasText: '活动' })
    if (await activityNav.count() > 0) {
      await activityNav.first().click()
      await page.waitForTimeout(2000)
      expect(page.url(), '点击活动导航应跳转到活动页').toContain('activities')
    }
  })
})

test.describe('P3-9: 个人中心错误提示', () => {

  test('未登录访问个人中心应提示或跳转登录', async ({ page }) => {
    // 清除所有登录状态
    await page.goto(`${BASE_URL}/p/gallery`)
    await page.evaluate(() => {
      localStorage.removeItem('public_token')
      localStorage.removeItem('public_user')
    })

    await page.goto(`${BASE_URL}/p/mine`)
    await page.waitForTimeout(3000)

    const url = page.url()
    const redirectedToLogin = url.includes('/p/login')
    const staysOnMine = url.includes('/p/mine')

    // 应该跳转到登录页或者停留在个人中心显示未登录状态
    expect(
      redirectedToLogin || staysOnMine,
      '未登录访问个人中心应跳转登录或显示未登录状态'
    ).toBeTruthy()
  })
})

// ==================== 通用回归：页面加载验证 ====================

test.describe('通用回归：所有公众端页面可正常加载', () => {

  const publicPages = [
    { path: '/p/gallery', name: '作品广场' },
    { path: '/p/activities', name: '活动大厅' },
    { path: '/p/login', name: '登录页' },
    { path: '/p/create', name: '创作页' },
  ]

  for (const pageInfo of publicPages) {
    test(`${pageInfo.name}(${pageInfo.path}) 加载正常`, async ({ page }) => {
      const response = await page.goto(`${BASE_URL}${pageInfo.path}`, { timeout: 15000 })
      expect(response!.ok(), `${pageInfo.name} 应正常加载`).toBeTruthy()

      await page.waitForTimeout(1500)

      // 页面不应白屏
      const bodyVisible = await page.locator('body').isVisible()
      expect(bodyVisible, `${pageInfo.name} body 应可见`).toBeTruthy()

      // 无未捕获的 JS 错误
      const jsErrors: string[] = []
      page.on('pageerror', (err) => jsErrors.push(err.message))

      // 刷新一次确认
      await page.reload({ timeout: 15000 })
      await page.waitForTimeout(1500)

      const criticalErrors = jsErrors.filter(e =>
        !e.includes('404') &&
        !e.includes('chunk') &&
        !e.includes('Loading chunk')
      )
      expect(criticalErrors.length, `${pageInfo.name} 不应有严重 JS 错误`).toBe(0)
    })
  }
})
