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

let counter = 500;
function shortUid() {
  counter++;
  return `${counter}${Math.random().toString(36).slice(2, 7)}`;
}

const TEST_PASS = 'testpass123';
const BASE = 'http://localhost:8000';

/** Register user via API, return { username, tokens, user_id } */
async function registerUser(prefix = 'ex') {
  const uid = shortUid();
  const username = `${prefix}${uid}`;
  const email = `${prefix}${uid}@t.co`;
  const resp = await fetch(`${BASE}/api/v1/auth/register`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ username, email, password: TEST_PASS }),
  });
  expect(resp.ok).toBeTruthy();
  const body = await resp.json();
  return { username, email, tokens: body.tokens, user_id: body.user.id };
}

/** Login as pre-existing admin (username: admin, password: admin123) */
async function loginAdmin() {
  const resp = await fetch(`${BASE}/api/v1/auth/login`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ username: 'admin', password: 'admin123' }),
  });
  expect(resp.ok).toBeTruthy();
  const body = await resp.json();
  return body.tokens.access as string;
}

/** Generate video (POST /api/v1/video/generate) */
async function generate(accessToken: string, duration = 10) {
  const form = new FormData();
  form.append('prompt', 'stress test');
  form.append('mode', 'universal');
  form.append('model', 'seedance_2.0');
  form.append('aspect_ratio', '16:9');
  form.append('duration', String(duration));
  return fetch(`${BASE}/api/v1/video/generate`, {
    method: 'POST',
    headers: { 'Authorization': `Bearer ${accessToken}` },
    body: form,
  });
}

/** Set user quota via admin API */
async function setQuota(adminToken: string, userId: number, daily: number, monthly: number) {
  return fetch(`${BASE}/api/v1/admin/users/${userId}/quota`, {
    method: 'PUT',
    headers: {
      'Authorization': `Bearer ${adminToken}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ daily_seconds_limit: daily, monthly_seconds_limit: monthly }),
  });
}

/** Set user active status via admin API */
async function setUserStatus(adminToken: string, userId: number, isActive: boolean) {
  return fetch(`${BASE}/api/v1/admin/users/${userId}/status`, {
    method: 'PATCH',
    headers: {
      'Authorization': `Bearer ${adminToken}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ is_active: isActive }),
  });
}

// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 1. 配额耗尽极限测试
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

test.describe('极限测试: 日配额耗尽拦截', () => {
  test('连续生成直至日配额耗尽，超限请求应返回 429', async () => {
    const adminToken = await loginAdmin();
    const user = await registerUser('dq');

    // 管理员将用户日配额设为 30s（方便快速耗尽）
    const quotaResp = await setQuota(adminToken, user.user_id, 30, 6000);
    expect(quotaResp.ok).toBeTruthy();

    // 第 1 次: 10s → 累计 10s, 剩余 20s → 应成功
    const r1 = await generate(user.tokens.access, 10);
    expect(r1.status).toBe(202);
    const d1 = await r1.json();
    expect(d1.seconds_consumed).toBe(10);
    expect(d1.remaining_seconds_today).toBe(20);

    // 第 2 次: 10s → 累计 20s, 剩余 10s → 应成功
    const r2 = await generate(user.tokens.access, 10);
    expect(r2.status).toBe(202);
    const d2 = await r2.json();
    expect(d2.remaining_seconds_today).toBe(10);

    // 第 3 次: 10s → 累计 30s, 剩余 0s → 应成功（刚好用完）
    const r3 = await generate(user.tokens.access, 10);
    expect(r3.status).toBe(202);
    const d3 = await r3.json();
    expect(d3.remaining_seconds_today).toBe(0);

    // 第 4 次: 再请求 10s → 超限 → 应返回 429
    const r4 = await generate(user.tokens.access, 10);
    expect(r4.status).toBe(429);
    const d4 = await r4.json();
    expect(d4.error).toBe('quota_exceeded');
    expect(d4.message).toContain('今日');
    expect(d4.daily_seconds_used).toBe(30);

    // 第 5 次: 即使只请求 1s 也应被拦截
    const r5 = await generate(user.tokens.access, 1);
    expect(r5.status).toBe(429);
  });

  test('单次请求超出剩余额度应被拦截', async () => {
    const adminToken = await loginAdmin();
    const user = await registerUser('ds');

    // 日配额设为 15s
    await setQuota(adminToken, user.user_id, 15, 6000);

    // 先消耗 10s
    const r1 = await generate(user.tokens.access, 10);
    expect(r1.status).toBe(202);

    // 再请求 10s（剩余仅 5s）→ 应被拦截
    const r2 = await generate(user.tokens.access, 10);
    expect(r2.status).toBe(429);
    const d2 = await r2.json();
    expect(d2.error).toBe('quota_exceeded');

    // 但请求 5s 应该成功（刚好用完）
    const r3 = await generate(user.tokens.access, 5);
    expect(r3.status).toBe(202);
    expect((await r3.json()).remaining_seconds_today).toBe(0);
  });
});

test.describe('极限测试: 月配额耗尽拦截', () => {
  test('月配额耗尽后应返回 429 并提示月度限制', async () => {
    const adminToken = await loginAdmin();
    const user = await registerUser('mq');

    // 日配额 100s，月配额仅 25s（月配额比日配额小，优先触发月限制）
    await setQuota(adminToken, user.user_id, 100, 25);

    // 消耗 10s
    const r1 = await generate(user.tokens.access, 10);
    expect(r1.status).toBe(202);

    // 消耗 10s（累计 20s）
    const r2 = await generate(user.tokens.access, 10);
    expect(r2.status).toBe(202);

    // 再请求 10s（20+10=30 > 25 月配额）→ 月配额拦截
    const r3 = await generate(user.tokens.access, 10);
    expect(r3.status).toBe(429);
    const d3 = await r3.json();
    expect(d3.error).toBe('quota_exceeded');
    expect(d3.message).toContain('本月');
    expect(d3.monthly_seconds_used).toBe(20);
  });
});

// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 2. 账号禁用/启用功能测试
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

test.describe('极限测试: 账号禁用后权限验证', () => {
  test('禁用账号后，使用已有 token 调用生成接口应被拒绝', async () => {
    const adminToken = await loginAdmin();
    const user = await registerUser('dis');

    // 先确认用户可以正常生成
    const r1 = await generate(user.tokens.access, 10);
    expect(r1.status).toBe(202);

    // 管理员禁用该用户
    const statusResp = await setUserStatus(adminToken, user.user_id, false);
    expect(statusResp.ok).toBeTruthy();
    const statusBody = await statusResp.json();
    expect(statusBody.is_active).toBe(false);

    // 使用已有 token 尝试生成 → 应被拒绝（401 或 403）
    const r2 = await generate(user.tokens.access, 10);
    expect([401, 403]).toContain(r2.status);

    // 使用已有 token 访问个人信息 → 也应被拒绝
    const meResp = await fetch(`${BASE}/api/v1/auth/me`, {
      headers: { 'Authorization': `Bearer ${user.tokens.access}` },
    });
    expect([401, 403]).toContain(meResp.status);
  });

  test('禁用账号后，重新登录应失败', async () => {
    const adminToken = await loginAdmin();
    const user = await registerUser('dl');

    // 管理员禁用该用户
    await setUserStatus(adminToken, user.user_id, false);

    // 尝试登录 → 应失败
    const loginResp = await fetch(`${BASE}/api/v1/auth/login`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ username: user.username, password: TEST_PASS }),
    });
    expect(loginResp.ok).toBeFalsy();
    expect(loginResp.status).toBe(401);
  });

  test('重新启用账号后，用户可以登录并正常生成', async () => {
    const adminToken = await loginAdmin();
    const user = await registerUser('re');

    // 禁用
    await setUserStatus(adminToken, user.user_id, false);

    // 确认无法登录
    const loginFail = await fetch(`${BASE}/api/v1/auth/login`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ username: user.username, password: TEST_PASS }),
    });
    expect(loginFail.status).toBe(401);

    // 重新启用
    const enableResp = await setUserStatus(adminToken, user.user_id, true);
    expect(enableResp.ok).toBeTruthy();
    expect((await enableResp.json()).is_active).toBe(true);

    // 重新登录 → 应成功
    const loginOk = await fetch(`${BASE}/api/v1/auth/login`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ username: user.username, password: TEST_PASS }),
    });
    expect(loginOk.ok).toBeTruthy();
    const newTokens = (await loginOk.json()).tokens;

    // 使用新 token 生成 → 应成功
    const r = await generate(newTokens.access, 10);
    expect(r.status).toBe(202);
  });
});

// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 3. 管理员动态调整额度测试
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

test.describe('极限测试: 管理员调整额度后立即生效', () => {
  test('调低额度后，已消耗量超新限制的请求应被拦截', async () => {
    const adminToken = await loginAdmin();
    const user = await registerUser('ql');

    // 默认日配额 600s，先消耗 50s
    for (let i = 0; i < 5; i++) {
      const r = await generate(user.tokens.access, 10);
      expect(r.status).toBe(202);
    }

    // 管理员将日配额调低为 40s（低于已消耗的 50s）
    await setQuota(adminToken, user.user_id, 40, 6000);

    // 再请求 → 应被拦截（已用 50s > 新限 40s）
    const r = await generate(user.tokens.access, 1);
    expect(r.status).toBe(429);
    const d = await r.json();
    expect(d.error).toBe('quota_exceeded');
    expect(d.daily_seconds_used).toBe(50);
  });

  test('调高额度后，之前被拦截的请求应可以通过', async () => {
    const adminToken = await loginAdmin();
    const user = await registerUser('qh');

    // 日配额设为 20s
    await setQuota(adminToken, user.user_id, 20, 6000);

    // 消耗 20s 用完
    const r1 = await generate(user.tokens.access, 10);
    expect(r1.status).toBe(202);
    const r2 = await generate(user.tokens.access, 10);
    expect(r2.status).toBe(202);

    // 确认被拦截
    const r3 = await generate(user.tokens.access, 10);
    expect(r3.status).toBe(429);

    // 管理员调高日配额到 100s
    await setQuota(adminToken, user.user_id, 100, 6000);

    // 现在应该可以继续生成（已用 20s，新限 100s，还剩 80s）
    const r4 = await generate(user.tokens.access, 10);
    expect(r4.status).toBe(202);
    const d4 = await r4.json();
    expect(d4.remaining_seconds_today).toBe(70); // 100 - 20 - 10 = 70
  });

  test('调整月配额同样立即生效', async () => {
    const adminToken = await loginAdmin();
    const user = await registerUser('qm');

    // 日配额 200s，月配额 30s
    await setQuota(adminToken, user.user_id, 200, 30);

    // 消耗 20s
    const r1 = await generate(user.tokens.access, 10);
    expect(r1.status).toBe(202);
    const r2 = await generate(user.tokens.access, 10);
    expect(r2.status).toBe(202);

    // 再请求 15s → 20+15=35 > 30 月配额 → 拦截
    const r3 = await generate(user.tokens.access, 15);
    expect(r3.status).toBe(429);

    // 调高月配额到 100s
    await setQuota(adminToken, user.user_id, 200, 100);

    // 现在可以继续
    const r4 = await generate(user.tokens.access, 15);
    expect(r4.status).toBe(202);
  });
});

// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 4. 边界条件测试
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

test.describe('极限测试: 边界条件', () => {
  test('配额恰好为 0s 时任何请求都应被拦截', async () => {
    const adminToken = await loginAdmin();
    const user = await registerUser('z0');

    // 日配额设为 0
    await setQuota(adminToken, user.user_id, 0, 6000);

    const r = await generate(user.tokens.access, 1);
    expect(r.status).toBe(429);
  });

  test('并发请求竞态条件检测（SQLite 无行锁，记录实际行为）', async () => {
    const adminToken = await loginAdmin();
    const user = await registerUser('cc');

    // 日配额 15s，并发发 3 个 10s 请求
    await setQuota(adminToken, user.user_id, 15, 6000);

    const results = await Promise.all([
      generate(user.tokens.access, 10),
      generate(user.tokens.access, 10),
      generate(user.tokens.access, 10),
    ]);

    const statuses = results.map(r => r.status);
    const successCount = statuses.filter(s => s === 202).length;

    // 记录并发行为：
    // - MySQL/PostgreSQL + select_for_update：最多成功 1 个
    // - SQLite 无行锁：可能全部成功（已知限制）
    // 此测试验证并发请求被正确处理（无 500 错误）
    expect(statuses.every(s => s === 202 || s === 429)).toBeTruthy();

    // 如果使用 MySQL 生产环境，取消下面注释启用严格断言:
    // expect(successCount).toBeLessThanOrEqual(1);

    console.log(`并发测试结果: ${successCount} 个成功, ${3 - successCount} 个被拦截 (SQLite 环境)`);
  });
});
