/**
 * 项目流程 E2E 测试
 *
 * 覆盖：创建项目、列出项目、获取项目详情
 * 每个测试独立，先注册登录获取 auth token。
 */
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)}`;
}

/** 登录操作，遇到 429 限流自动等待重试 */
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 },
  });
}

/** 注册并登录，返回 { headers, email, username, userId } */
async function createAuthedUser(request: APIRequestContext, label: string) {
  const email = uniqueEmail(label);
  const username = uniqueUsername(label);

  const reg = await request.post(`${apiBase}/auth/register`, {
    data: { email, password: PASSWORD, username, display_name: `E2E ${label}` },
  });
  expect(reg.ok(), `注册应成功: ${await reg.text()}`).toBeTruthy();
  const regData = await reg.json();

  const login = await loginWithRetry(request, email, PASSWORD);
  expect(login.ok(), `登录应成功: ${await login.text()}`).toBeTruthy();
  const loginData = await login.json();

  return {
    headers: { Authorization: `Bearer ${loginData.access_token}` },
    email,
    username,
    userId: regData.user_id,
  };
}

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

  test("创建项目", async ({ request }) => {
    const { headers } = await createAuthedUser(request, "proj-create");
    const projectName = `E2E 测试项目 ${Date.now()}`;

    const response = await request.post(`${apiBase}/projects`, {
      headers,
      data: {
        name: projectName,
        description: "Playwright E2E 回归测试创建",
      },
    });

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

    const data = await response.json();
    expect(data.id, "应返回项目 ID").toBeTruthy();
    expect(data.name).toBe(projectName);
    expect(data.owner_user_id, "应返回所有者 ID").toBeTruthy();
  });

  test("创建项目名称为空 - 反向", async ({ request }) => {
    const { headers } = await createAuthedUser(request, "proj-empty");

    const response = await request.post(`${apiBase}/projects`, {
      headers,
      data: { name: "", description: "Should fail" },
    });

    // name 有 min_length=1 约束，应返回 422
    expect([400, 422]).toContain(response.status());
  });

  test("列出项目", async ({ request }) => {
    const { headers } = await createAuthedUser(request, "proj-list");

    // 先创建 2 个项目
    await request.post(`${apiBase}/projects`, {
      headers,
      data: { name: `List Proj A ${Date.now()}` },
    });
    await request.post(`${apiBase}/projects`, {
      headers,
      data: { name: `List Proj B ${Date.now()}` },
    });

    // 列出
    const response = await request.get(`${apiBase}/projects`, { headers });

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

    const data = await response.json();
    const items = data.items || data.projects || data || [];
    expect(Array.isArray(items)).toBeTruthy();
    expect(items.length, "应至少有 2 个项目").toBeGreaterThanOrEqual(2);
  });

  test("获取项目详情", async ({ request }) => {
    const { headers } = await createAuthedUser(request, "proj-detail");

    // 先创建
    const created = await request.post(`${apiBase}/projects`, {
      headers,
      data: { name: `Detail Proj ${Date.now()}`, description: "Detail test" },
    });
    expect(created.ok(), `创建应成功: ${await created.text()}`).toBeTruthy();
    const { id: projectId } = await created.json();

    // 获取详情
    const response = await request.get(`${apiBase}/projects/${projectId}`, {
      headers,
    });

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

    const data = await response.json();
    expect(data.id).toBe(projectId);
    expect(data.name).toBeTruthy();
    expect(data.owner_user_id).toBeTruthy();
  });

  test("获取不存在的项目 - 反向", async ({ request }) => {
    const { headers } = await createAuthedUser(request, "proj-404");

    const response = await request.get(
      `${apiBase}/projects/nonexistent-project-id-999`,
      { headers },
    );

    expect(response.status(), "不存在的项目应返回 404").toBe(404);
  });

  test("未登录列出项目 - 反向", async ({ request }) => {
    const response = await request.get(`${apiBase}/projects`);

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

  test("未授权访问他人项目 - 反向", async ({ request }) => {
    // 用户 A 创建项目
    const { headers: headersA } = await createAuthedUser(request, "proj-owner");
    const created = await request.post(`${apiBase}/projects`, {
      headers: headersA,
      data: { name: `Owner Proj ${Date.now()}`, description: "Owner test" },
    });
    expect(created.ok(), "用户 A 创建项目应成功").toBeTruthy();
    const { id: projectId } = await created.json();

    // 用户 B 尝试访问用户 A 的项目
    const { headers: headersB } = await createAuthedUser(
      request,
      "proj-intruder",
    );
    const response = await request.get(`${apiBase}/projects/${projectId}`, {
      headers: headersB,
    });

    // 应返回 403 (Forbidden) 或 404 (Not Found) — 不应泄露资源存在性
    expect([403, 404]).toContain(response.status());
  });

  test("未授权删除他人项目 - 反向", async ({ request }) => {
    // 用户 A 创建项目
    const { headers: headersA } = await createAuthedUser(
      request,
      "proj-del-owner",
    );
    const created = await request.post(`${apiBase}/projects`, {
      headers: headersA,
      data: { name: `Delete Test Proj ${Date.now()}` },
    });
    expect(created.ok(), "用户 A 创建项目应成功").toBeTruthy();
    const { id: projectId } = await created.json();

    // 用户 B 尝试删除用户 A 的项目
    const { headers: headersB } = await createAuthedUser(
      request,
      "proj-del-attempt",
    );
    const response = await request.delete(`${apiBase}/projects/${projectId}`, {
      headers: headersB,
    });

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

  test("使用无效项目 ID 获取详情 - 反向", async ({ request }) => {
    const { headers } = await createAuthedUser(request, "proj-badid");

    const response = await request.get(`${apiBase}/projects/`, { headers });

    // 空 ID 或无效格式应返回 404 或 422
    expect([400, 404, 422]).toContain(response.status());
  });
});
