/**
 * 认证流程 E2E 测试
 *
 * 覆盖：注册（正向/反向）、登录（正向/反向）、登出、获取当前用户信息
 * 每个测试独立，使用随机邮箱避免冲突。
 */
import { expect, test, type APIRequestContext } from "@playwright/test";

const PASSWORD = "Test123456!";
const apiBase = process.env.E2E_API_BASE || "/api/v1";

function uniqueEmail(prefix: string): string {
  return `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2, 8)}@example.com`;
}

function uniqueUsername(prefix: string): string {
  return `${prefix}_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 6)}`;
}

/** 从错误响应中提取错误消息文本，兼容新老格式 */
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function extractErrorMessage(body: any): string {
  if (!body) return "";
  // 新格式: { error: { code: "...", message: "..." } }
  if (body.error && typeof body.error === "object" && body.error.message) {
    return String(body.error.message);
  }
  // 老格式: { detail: "..." } 或 { message: "..." } 或 { error: "..." }
  return String(body.detail || body.message || body.error || "");
}

/** 登录操作，遇到 429 限流自动等待重试（最多等 65s） */
async function loginWithRetry(
  request: APIRequestContext,
  email: string,
  password: string,
  maxRetries = 2,
) {
  for (let i = 0; i <= maxRetries; i++) {
    const response = await request.post(`${apiBase}/auth/login`, {
      data: { email, password },
    });
    if (response.status() !== 429) return response;
    // 被限流了，等窗口重置
    console.log(`[login] 触发限流，等待 65s 后重试 (${i + 1}/${maxRetries})`);
    await new Promise((r) => setTimeout(r, 65000));
  }
  // 最后一次直接返回
  return request.post(`${apiBase}/auth/login`, {
    data: { email, password },
  });
}

test.describe("认证流程", () => {
  // 登录限流 10次/60s，测试可能触发限流等待，给足够超时
  test.describe.configure({ timeout: 180_000 });

  // ─── 注册 ────────────────────────────────────────────

  test("注册新用户 - 正向", async ({ request }) => {
    const email = uniqueEmail("reg-ok");
    const username = uniqueUsername("regok");

    const response = await request.post(`${apiBase}/auth/register`, {
      data: {
        email,
        password: PASSWORD,
        username,
        display_name: "E2E 注册测试",
      },
    });

    expect(
      response.ok(),
      `注册应返回 2xx，实际: ${response.status()} ${await response.text()}`,
    ).toBeTruthy();

    const data = await response.json();
    expect(data.user_id, "应返回 user_id").toBeTruthy();
    expect(data.email).toBe(email);
    expect(data.username).toBe(username);
  });

  test("注册已存在邮箱 - 反向", async ({ request }) => {
    const email = uniqueEmail("reg-dup");
    const username1 = uniqueUsername("regdup1");
    const username2 = uniqueUsername("regdup2");

    // 第一次注册
    const first = await request.post(`${apiBase}/auth/register`, {
      data: {
        email,
        password: PASSWORD,
        username: username1,
        display_name: "User 1",
      },
    });
    expect(first.ok(), "第一次注册应成功").toBeTruthy();

    // 第二次使用相同邮箱
    const second = await request.post(`${apiBase}/auth/register`, {
      data: {
        email,
        password: PASSWORD,
        username: username2,
        display_name: "User 2",
      },
    });

    expect(second.status(), "重复邮箱注册应返回 4xx").toBeGreaterThanOrEqual(
      400,
    );
    expect(second.status()).toBeLessThan(500);

    const body = await second.json();
    // 错误信息应包含"已注册"或"exists"相关提示
    const detail = extractErrorMessage(body).toLowerCase();
    expect(
      detail.includes("已") ||
        detail.includes("exist") ||
        detail.includes("registered") ||
        detail.includes("duplicate"),
      `错误信息应提示邮箱已注册，实际: "${detail}"`,
    ).toBeTruthy();
  });

  test("注册无效邮箱格式 - 反向", async ({ request }) => {
    const response = await request.post(`${apiBase}/auth/register`, {
      data: {
        email: "not-an-email",
        password: PASSWORD,
        username: uniqueUsername("bademail"),
        display_name: "Bad Email",
      },
    });

    // 422 是 FastAPI 参数校验失败的标准状态码
    expect([400, 422]).toContain(response.status());
  });

  test("注册弱密码 - 反向", async ({ request }) => {
    const response = await request.post(`${apiBase}/auth/register`, {
      data: {
        email: uniqueEmail("weakpwd"),
        password: "123",
        username: uniqueUsername("weakpwd"),
        display_name: "Weak",
      },
    });

    expect([400, 422]).toContain(response.status());
  });

  test("注册用户名为空 - 反向", async ({ request }) => {
    const response = await request.post(`${apiBase}/auth/register`, {
      data: {
        email: uniqueEmail("emptyuser"),
        password: PASSWORD,
        username: "",
        display_name: "Empty Username",
      },
    });

    expect([400, 422], "用户名为空应返回 4xx 校验错误").toContain(
      response.status(),
    );
  });

  test("注册缺用户名字段 - 反向", async ({ request }) => {
    const response = await request.post(`${apiBase}/auth/register`, {
      data: {
        email: uniqueEmail("nouser"),
        password: PASSWORD,
        display_name: "No Username Field",
      },
    });

    expect(
      [400, 422],
      "缺少用户名字段应返回 4xx 校验错误",
    ).toContain(response.status());
  });

  // ─── 登录 ────────────────────────────────────────────

  test("登录成功 - 正向", async ({ request }) => {
    const email = uniqueEmail("login-ok");
    const username = uniqueUsername("loginok");

    // 先注册
    const reg = await request.post(`${apiBase}/auth/register`, {
      data: { email, password: PASSWORD, username, display_name: "Login Test" },
    });
    expect(reg.ok(), "注册应成功").toBeTruthy();

    // 登录（带限流重试）
    const response = await loginWithRetry(request, email, PASSWORD);

    expect(
      response.ok(),
      `登录应返回 2xx，实际: ${response.status()} ${await response.text()}`,
    ).toBeTruthy();

    const data = await response.json();
    expect(data.access_token, "应返回 access_token").toBeTruthy();
    expect(data.token_type).toBe("bearer");
    expect(data.email).toBe(email);
  });

  test("登录错误密码 - 反向", async ({ request }) => {
    const email = uniqueEmail("login-bad");
    const username = uniqueUsername("loginbad");

    // 先注册
    await request.post(`${apiBase}/auth/register`, {
      data: { email, password: PASSWORD, username, display_name: "Bad Login" },
    });

    // 使用错误密码登录（带限流重试）
    const response = await loginWithRetry(request, email, "WrongPassword999!");

    expect(response.status(), "错误密码应返回 401").toBe(401);
  });

  test("登录不存在的邮箱 - 反向", async ({ request }) => {
    // 带限流重试的反向登录测试
    let response;
    for (let attempt = 0; attempt < 3; attempt++) {
      response = await request.post(`${apiBase}/auth/login`, {
        data: { email: `ghost_${Date.now()}@nonexist.com`, password: PASSWORD },
      });
      if (response.status() !== 429) break;
      console.log(`[反向登录测试] 触发限流，等待 65s 后重试 (${attempt + 1}/2)`);
      await new Promise((r) => setTimeout(r, 65_000));
    }

    expect(response.status(), "不存在的用户应返回 401").toBe(401);
  });

  // ─── 登出 ────────────────────────────────────────────

  test("登出成功", async ({ request }) => {
    const email = uniqueEmail("logout");
    const username = uniqueUsername("logout");

    // 注册 & 登录
    await request.post(`${apiBase}/auth/register`, {
      data: {
        email,
        password: PASSWORD,
        username,
        display_name: "Logout Test",
      },
    });
    const login = await loginWithRetry(request, email, PASSWORD);
    const { access_token } = await login.json();
    const headers = { Authorization: `Bearer ${access_token}` };

    // 登出
    const logout = await request.post(`${apiBase}/auth/logout`, { headers });
    expect(
      logout.ok(),
      `登出应返回 2xx，实际: ${logout.status()}`,
    ).toBeTruthy();

    const body = await logout.json();
    expect(body.message).toBeTruthy();

    // 登出后 token 应失效，尝试访问 /auth/me
    const me = await request.get(`${apiBase}/auth/me`, { headers });
    expect([401, 403]).toContain(me.status());
  });

  // ─── 获取当前用户信息 ─────────────────────────────────

  test("获取当前用户信息 - 正向", async ({ request }) => {
    const email = uniqueEmail("me-ok");
    const username = uniqueUsername("meok");

    await request.post(`${apiBase}/auth/register`, {
      data: { email, password: PASSWORD, username, display_name: "Me Test" },
    });
    const login = await loginWithRetry(request, email, PASSWORD);
    const { access_token } = await login.json();

    const response = await request.get(`${apiBase}/auth/me`, {
      headers: { Authorization: `Bearer ${access_token}` },
    });

    expect(
      response.ok(),
      `获取用户信息应返回 2xx，实际: ${response.status()}`,
    ).toBeTruthy();

    const data = await response.json();
    expect(data.user_id).toBeTruthy();
    expect(data.email).toBe(email);
    expect(data.username).toBe(username);
  });

  test("无 token 获取用户信息 - 反向", async ({ request }) => {
    const response = await request.get(`${apiBase}/auth/me`);
    // HTTPBearer 无凭证返回 403
    expect([401, 403]).toContain(response.status());
  });

  test("无效 token 获取用户信息 - 反向", async ({ request }) => {
    const response = await request.get(`${apiBase}/auth/me`, {
      headers: { Authorization: "Bearer invalid.token.here" },
    });
    expect(response.status()).toBe(401);
  });

  test("过期 token 获取用户信息 - 反向", async ({ request }) => {
    // 使用一个伪造的过期 JWT（header.payload.signature）
    // eyJhbGciOiJIUzI1NiJ9 = {"alg":"HS256"}
    // eyJleHAiOjF9 = {"exp":1}  (1970-01-01 过期)
    const expiredToken =
      "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjEsInN1YiI6InRlc3QtdXNlciJ9.expired_signature";

    const response = await request.get(`${apiBase}/auth/me`, {
      headers: { Authorization: `Bearer ${expiredToken}` },
    });

    expect([401, 403]).toContain(response.status());
  });

  test("token 格式错误 - 反向", async ({ request }) => {
    const response = await request.get(`${apiBase}/auth/me`, {
      headers: { Authorization: "Bearer not-a-jwt" },
    });

    expect([401, 403]).toContain(response.status());
  });

  test("空 Bearer token - 反向", async ({ request }) => {
    const response = await request.get(`${apiBase}/auth/me`, {
      headers: { Authorization: "Bearer " },
    });

    expect([401, 403]).toContain(response.status());
  });
});
