import { test as base, expect, type Page, type BrowserContext } from '@playwright/test'

/**
 * 管理端测试 Fixture
 * 提供 Mock 数据、登录状态注入、API 路由拦截
 */

// ==================== 常量配置 ====================

/** 测试租户编码 */
export const TENANT_CODE = 'gdlib'

/** 测试用户信息 */
export const MOCK_USER = {
  id: 1,
  username: 'admin',
  nickname: '测试管理员',
  phone: '13800000001',
  email: 'admin@test.com',
  avatar: null,
  tenantId: 2,
  tenantCode: TENANT_CODE,
  tenantName: '广东省立中山图书馆',
  roles: ['tenant_admin'],
  permissions: [
    'contest:create', 'contest:read', 'contest:update', 'contest:delete', 'contest:publish',
    'contest:registration:read', 'contest:work:read',
    'registration:read', 'registration:update',
    'judge:read', 'judge:create', 'judge:update', 'judge:delete',
    'review:read', 'review:score',
    'user:read', 'user:create', 'user:update', 'user:delete',
    'menu:read',
    'homework:read',
    'activity:read',
  ],
}

/** Mock JWT Token */
export const MOCK_TOKEN = 'mock-jwt-token-for-e2e-testing-' + Date.now()

/** Mock 菜单数据（模拟后端返回的菜单树） */
export const MOCK_MENUS = [
  {
    id: 100,
    name: '工作台',
    path: '/workbench/dashboard',
    icon: 'DashboardOutlined',
    component: 'workbench/TenantDashboard',
    sort: 1,
    children: undefined,
  },
  {
    id: 200,
    name: '活动管理',
    path: null,
    icon: 'TrophyOutlined',
    component: null,
    sort: 2,
    children: [
      {
        id: 201,
        name: '活动列表',
        path: '/contests/list',
        icon: 'UnorderedListOutlined',
        component: 'contests/Index',
        sort: 1,
      },
      {
        id: 202,
        name: '报名管理',
        path: '/contests/registrations',
        icon: 'FormOutlined',
        component: 'contests/registrations/Index',
        sort: 2,
      },
      {
        id: 203,
        name: '作品管理',
        path: '/contests/works',
        icon: 'FileTextOutlined',
        component: 'contests/works/Index',
        sort: 3,
      },
      {
        id: 204,
        name: '评委管理',
        path: '/contests/judges',
        icon: 'SolutionOutlined',
        component: 'contests/judges/Index',
        sort: 4,
      },
      {
        id: 205,
        name: '评审规则',
        path: '/contests/reviews',
        icon: 'AuditOutlined',
        component: 'contests/reviews/Index',
        sort: 5,
      },
    ],
  },
  {
    id: 300,
    name: '用户管理',
    path: '/system/users',
    icon: 'TeamOutlined',
    component: 'system/users/Index',
    sort: 3,
    children: undefined,
  },
]

// ==================== Mock API 响应数据 ====================

/** 仪表盘统计 Mock */
export const MOCK_DASHBOARD = {
  totalContests: 5,
  ongoingContests: 2,
  totalRegistrations: 128,
  pendingRegistrations: 12,
  totalWorks: 96,
  todayRegistrations: 8,
  tenant: {
    id: 2,
    name: '广东省立中山图书馆',
    tenantType: 'library',
  },
  recentContests: [
    {
      id: 1,
      contestName: '少儿绘本创作大赛',
      startTime: '2026-03-01T00:00:00Z',
      endTime: '2026-06-30T23:59:59Z',
      status: 'ongoing',
      _count: { registrations: 45, works: 32 },
    },
    {
      id: 2,
      contestName: '春季阅读推广活动',
      startTime: '2026-04-01T00:00:00Z',
      endTime: '2026-05-31T23:59:59Z',
      status: 'ongoing',
      _count: { registrations: 83, works: 64 },
    },
  ],
}

/** 活动列表 Mock */
export const MOCK_CONTESTS = {
  list: [
    {
      id: 1,
      contestName: '少儿绘本创作大赛',
      contestType: 'individual',
      stage: 'registering',
      contestState: 'published',
      startTime: '2026-03-01T00:00:00Z',
      endTime: '2026-06-30T23:59:59Z',
      _count: { registrations: 45, works: 32, judges: 5 },
      reviewedCount: 20,
      totalWorksCount: 32,
    },
    {
      id: 2,
      contestName: '春季阅读推广活动',
      contestType: 'team',
      stage: 'submitting',
      contestState: 'published',
      startTime: '2026-04-01T00:00:00Z',
      endTime: '2026-05-31T23:59:59Z',
      _count: { registrations: 83, works: 64, judges: 3 },
      reviewedCount: 0,
      totalWorksCount: 64,
    },
    {
      id: 3,
      contestName: '环保主题绘画比赛',
      contestType: 'individual',
      stage: 'unpublished',
      contestState: 'unpublished',
      startTime: '2026-05-01T00:00:00Z',
      endTime: '2026-08-31T23:59:59Z',
      _count: { registrations: 0, works: 0, judges: 0 },
      reviewedCount: 0,
      totalWorksCount: 0,
    },
  ],
  total: 3,
  page: 1,
  pageSize: 10,
}

/** 活动统计 Mock */
export const MOCK_CONTEST_STATS = {
  total: 3,
  unpublished: 1,
  registering: 1,
  submitting: 1,
  reviewing: 0,
  finished: 0,
}

/** 报名列表 Mock */
export const MOCK_REGISTRATIONS = {
  list: [
    {
      id: 1,
      contestId: 1,
      contestName: '少儿绘本创作大赛',
      participantName: '张小明',
      participantType: 'individual',
      status: 'approved',
      createdAt: '2026-03-15T10:30:00Z',
      phone: '138****0001',
    },
    {
      id: 2,
      contestId: 1,
      contestName: '少儿绘本创作大赛',
      participantName: '李小红',
      participantType: 'individual',
      status: 'pending',
      createdAt: '2026-03-16T14:20:00Z',
      phone: '139****0002',
    },
    {
      id: 3,
      contestId: 2,
      contestName: '春季阅读推广活动',
      participantName: '创意小队',
      participantType: 'team',
      status: 'approved',
      createdAt: '2026-04-02T09:00:00Z',
      phone: '137****0003',
    },
  ],
  total: 3,
  page: 1,
  pageSize: 10,
}

/** 作品列表 Mock */
export const MOCK_WORKS = {
  list: [
    {
      id: 1,
      title: '我的梦想家园',
      contestId: 1,
      contestName: '少儿绘本创作大赛',
      authorName: '张小明',
      status: 'submitted',
      submittedAt: '2026-03-20T15:00:00Z',
      coverUrl: 'https://via.placeholder.com/200',
    },
    {
      id: 2,
      title: '森林探险记',
      contestId: 1,
      contestName: '少儿绘本创作大赛',
      authorName: '李小红',
      status: 'reviewing',
      submittedAt: '2026-03-22T10:30:00Z',
      coverUrl: 'https://via.placeholder.com/200',
    },
  ],
  total: 2,
  page: 1,
  pageSize: 10,
}

/** 评审规则 Mock */
export const MOCK_REVIEW_RULES = {
  list: [
    {
      id: 1,
      name: '标准评审规则',
      description: '适用于一般绘本创作活动的评审标准',
      scoreDimensions: [
        { name: '创意性', weight: 30, maxScore: 100 },
        { name: '绘画技巧', weight: 30, maxScore: 100 },
        { name: '故事性', weight: 25, maxScore: 100 },
        { name: '完整性', weight: 15, maxScore: 100 },
      ],
      calculationMethod: 'average',
      createdAt: '2026-01-15T00:00:00Z',
    },
  ],
  total: 1,
  page: 1,
  pageSize: 10,
}

/** 用户列表 Mock */
export const MOCK_USERS = {
  list: [
    {
      id: 1,
      username: 'admin',
      nickname: '测试管理员',
      phone: '13800000001',
      email: 'admin@test.com',
      status: 1,
      userType: 'tenant_admin',
      createdAt: '2026-01-01T00:00:00Z',
      roles: [{ id: 1, name: '租户管理员', code: 'tenant_admin' }],
    },
    {
      id: 2,
      username: 'worker01',
      nickname: '工作人员A',
      phone: '13800000002',
      email: 'worker@test.com',
      status: 1,
      userType: 'tenant_staff',
      createdAt: '2026-02-01T00:00:00Z',
      roles: [{ id: 2, name: '工作人员', code: 'tenant_staff' }],
    },
    {
      id: 3,
      username: 'judge01',
      nickname: '评委老师A',
      phone: '13800000003',
      email: 'judge@test.com',
      status: 0,
      userType: 'judge',
      createdAt: '2026-02-15T00:00:00Z',
      roles: [{ id: 3, name: '评委', code: 'judge' }],
    },
  ],
  total: 3,
  page: 1,
  pageSize: 10,
}

// ==================== Fixture 类型定义 ====================

type AdminFixtures = {
  /** 已注入登录态的管理端页面 */
  adminPage: Page
}

// ==================== 辅助函数 ====================

/**
 * 设置所有需要的 API Mock 路由
 */
export async function setupApiMocks(page: Page): Promise<void> {
  // 登录接口
  await page.route('**/api/auth/login', async (route) => {
    const request = route.request()
    const postData = request.postDataJSON()

    if (!postData?.username || !postData?.password) {
      await route.fulfill({
        status: 200,
        contentType: 'application/json',
        body: JSON.stringify({ code: 400, message: '用户名和密码不能为空', data: null, timestamp: Date.now(), path: '/api/auth/login' }),
      })
      return
    }

    if (postData.username === 'wrong') {
      await route.fulfill({
        status: 200,
        contentType: 'application/json',
        body: JSON.stringify({ code: 401, message: '用户名或密码错误', data: null, timestamp: Date.now(), path: '/api/auth/login' }),
      })
      return
    }

    await route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify({
        code: 200,
        message: 'success',
        data: { token: MOCK_TOKEN, user: MOCK_USER },
        timestamp: Date.now(),
        path: '/api/auth/login',
      }),
    })
  })

  // 获取用户信息
  await page.route('**/api/auth/user-info', async (route) => {
    await route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify({ code: 200, message: 'success', data: MOCK_USER, timestamp: Date.now(), path: '/api/auth/user-info' }),
    })
  })

  // 登出
  await page.route('**/api/auth/logout', async (route) => {
    await route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify({ code: 200, message: 'success', data: null, timestamp: Date.now(), path: '/api/auth/logout' }),
    })
  })

  // 获取用户菜单
  await page.route('**/api/menus/user-menus', async (route) => {
    await route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify({ code: 200, message: 'success', data: MOCK_MENUS, timestamp: Date.now(), path: '/api/menus/user-menus' }),
    })
  })

  // 仪表盘数据
  await page.route('**/api/contests/dashboard', async (route) => {
    await route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify({ code: 200, message: 'success', data: MOCK_DASHBOARD, timestamp: Date.now(), path: '/api/contests/dashboard' }),
    })
  })

  // 活动列表
  await page.route('**/api/contests?**', async (route) => {
    await route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify({ code: 200, message: 'success', data: MOCK_CONTESTS, timestamp: Date.now(), path: '/api/contests' }),
    })
  })

  // 活动统计
  await page.route('**/api/contests/stats', async (route) => {
    await route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify({ code: 200, message: 'success', data: MOCK_CONTEST_STATS, timestamp: Date.now(), path: '/api/contests/stats' }),
    })
  })

  // 活动详情
  await page.route('**/api/contests/*', async (route) => {
    const url = route.request().url()
    if (url.includes('/stats') || url.includes('/dashboard') || url.includes('/registrations') || url.includes('/works') || url.includes('/reviews')) {
      await route.fallback()
      return
    }
    await route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify({
        code: 200,
        message: 'success',
        data: { ...MOCK_CONTESTS.list[0], organizers: '广东省立中山图书馆', contestTenants: [2] },
        timestamp: Date.now(),
        path: '/api/contests/1',
      }),
    })
  })

  // 创建活动
  await page.route('**/api/contests', async (route) => {
    if (route.request().method() === 'POST') {
      await route.fulfill({
        status: 200,
        contentType: 'application/json',
        body: JSON.stringify({ code: 200, message: 'success', data: { id: 10 }, timestamp: Date.now(), path: '/api/contests' }),
      })
    } else {
      await route.fulfill({
        status: 200,
        contentType: 'application/json',
        body: JSON.stringify({ code: 200, message: 'success', data: MOCK_CONTESTS, timestamp: Date.now(), path: '/api/contests' }),
      })
    }
  })

  // 报名列表
  await page.route('**/api/contests/registrations**', async (route) => {
    await route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify({ code: 200, message: 'success', data: MOCK_REGISTRATIONS, timestamp: Date.now(), path: '/api/contests/registrations' }),
    })
  })

  // 作品列表
  await page.route('**/api/contests/works**', async (route) => {
    await route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify({ code: 200, message: 'success', data: MOCK_WORKS, timestamp: Date.now(), path: '/api/contests/works' }),
    })
  })

  // 评审规则列表
  await page.route('**/api/contests/reviews/rules**', async (route) => {
    await route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify({ code: 200, message: 'success', data: MOCK_REVIEW_RULES, timestamp: Date.now(), path: '/api/contests/reviews/rules' }),
    })
  })

  // 用户列表
  await page.route('**/api/users**', async (route) => {
    if (route.request().method() === 'POST') {
      // 创建用户
      await route.fulfill({
        status: 200,
        contentType: 'application/json',
        body: JSON.stringify({ code: 200, message: 'success', data: { id: 100 }, timestamp: Date.now(), path: '/api/users' }),
      })
    } else {
      await route.fulfill({
        status: 200,
        contentType: 'application/json',
        body: JSON.stringify({ code: 200, message: 'success', data: MOCK_USERS, timestamp: Date.now(), path: '/api/users' }),
      })
    }
  })

  // 租户信息
  await page.route('**/api/tenants/my-tenant', async (route) => {
    await route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify({
        code: 200,
        message: 'success',
        data: { id: 2, name: '广东省立中山图书馆', code: TENANT_CODE, tenantType: 'library' },
        timestamp: Date.now(),
        path: '/api/tenants/my-tenant',
      }),
    })
  })

  // 评审任务列表（评委端）
  await page.route('**/api/activities/review**', async (route) => {
    await route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify({
        code: 200,
        message: 'success',
        data: {
          list: [
            { id: 1, contestName: '少儿绘本创作大赛', totalWorks: 10, reviewedWorks: 5, status: 'in_progress' },
          ],
          total: 1,
        },
        timestamp: Date.now(),
      }),
    })
  })

  // 评审规则下拉选项（创建活动页使用）
  await page.route('**/api/contests/review-rules/select**', async (route) => {
    await route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify({
        code: 200,
        message: 'success',
        data: [
          { id: 1, ruleName: '标准评审规则' },
        ],
        timestamp: Date.now(),
        path: '/api/contests/review-rules/select',
      }),
    })
  })

  // 兜底拦截：防止未 mock 的请求到达真实后端（返回空数据而非 401）
  await page.route('**/api/**', async (route) => {
    const url = route.request().url()
    const method = route.request().method()
    // 只拦截未被更具体 mock 处理的请求
    await route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify({
        code: 200,
        message: 'success',
        data: method === 'GET' ? { list: [], total: 0, page: 1, pageSize: 10 } : { id: 0 },
        timestamp: Date.now(),
        path: new URL(url).pathname,
      }),
    })
  })
}

/**
 * 注入登录态到浏览器
 * 通过设置 Cookie 模拟已登录状态
 */
export async function injectAuthState(page: Page): Promise<void> {
  // 先访问页面以便能设置 Cookie
  await page.goto('/p/login')

  // 注入 Cookie（与 setToken 函数一致，path 为 '/'）
  await page.evaluate((token) => {
    document.cookie = `token=${encodeURIComponent(token)}; path=/; max-age=${7 * 24 * 60 * 60}`
  }, MOCK_TOKEN)
}

/**
 * 导航到管理端页面（已注入登录态后）
 * 等待路由守卫完成和页面渲染
 */
export async function navigateToAdmin(page: Page, path: string = ''): Promise<void> {
  const targetUrl = `/${TENANT_CODE}${path}`
  await page.goto(targetUrl)

  // 等待页面基本加载完成（BasicLayout 渲染）
  await page.waitForSelector('.layout, .login-container', { timeout: 15_000 })
}

/**
 * 等待 Ant Design 表格加载完成
 */
export async function waitForTable(page: Page): Promise<void> {
  await page.waitForSelector('.ant-table', { timeout: 10_000 })
  // 等待表格数据加载
  await page.waitForSelector('.ant-table-tbody tr', { timeout: 10_000 })
}

// ==================== 组件预热 ====================

/** 标记是否已完成组件预热（Vite 编译缓存只需触发一次） */
let componentsWarmedUp = false

/**
 * 预热管理端页面组件
 * 通过导航到活动列表页触发 Vite 编译，避免首个测试因组件加载慢而失败
 */
async function warmupComponents(page: Page): Promise<void> {
  if (componentsWarmedUp) return
  try {
    // 展开活动管理子菜单
    const submenu = page.locator('.ant-menu-submenu').filter({ hasText: '活动管理' }).first()
    await submenu.click()
    await page.waitForTimeout(500)
    // 点击活动列表触发组件加载
    await submenu.locator('.ant-menu-item').filter({ hasText: '活动列表' }).first().click()
    await page.waitForSelector('.contests-page', { timeout: 15_000 })
    // 导航回工作台
    await page.locator('.ant-menu-item').filter({ hasText: '工作台' }).first().click()
    await page.waitForTimeout(500)
    componentsWarmedUp = true
  } catch {
    // 预热失败不影响测试（可能组件已被缓存）
  }
}

// ==================== 扩展 Fixture ====================

export const test = base.extend<AdminFixtures>({
  adminPage: async ({ page }, use) => {
    // 设置 API Mock
    await setupApiMocks(page)
    // 注入登录态
    await injectAuthState(page)
    // 导航到管理端首页
    await navigateToAdmin(page)
    // 等待侧边栏加载
    await page.waitForSelector('.custom-sider', { timeout: 15_000 })
    // 预热组件（首次运行时触发 Vite 编译）
    await warmupComponents(page)
    await use(page)
  },
})

export { expect }
