import { expect, test, type APIRequestContext } from "@playwright/test";

const PASSWORD = "SmokePass123!";
const apiBase = process.env.E2E_API_BASE || "/api/v1";
const apiOrigin = apiBase.endsWith("/api/v1")
  ? apiBase.slice(0, -"/api/v1".length)
  : "";

const routeBrowserApiToTestApi = async (
  page: import("@playwright/test").Page,
) => {
  if (!apiOrigin) return;
  await page.route("**/api/v1/**", async (route) => {
    const sourceUrl = new URL(route.request().url());
    const response = await route.fetch({
      url: `${apiOrigin}${sourceUrl.pathname}${sourceUrl.search}`,
    });
    await route.fulfill({ response });
  });
};

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 },
  });
}

type ProjectResponse = { id: string };
type LibraryResponse = { id: string };
type TemplateResponse = { id: string };
type AssetListResponse = {
  items: Array<{
    id: string;
    name: string;
    status: string;
  }>;
};

test.describe("Core generation flow", () => {
  test.describe.configure({ timeout: 180_000 });

  test("walks through 5-step wizard and starts generation", async ({
    page,
    request,
  }) => {
    test.setTimeout(180_000);

    await routeBrowserApiToTestApi(page);
    const suffix = Date.now().toString(36);
    const email = `e2e-gen-${suffix}@example.com`;
    const username = `e2e_gen_${suffix}`;
    const libraryName = `E2E Gen Lib ${suffix}`;

    // Register
    const register = await request.post(`${apiBase}/auth/register`, {
      data: { email, username, password: PASSWORD, display_name: username },
    });
    expect(register.status()).toBe(201);
    const registerData = (await register.json()) as { user_id: string };

    // Login
    const login = await loginWithRetry(request, email, PASSWORD);
    expect(login.status()).toBe(200);
    const loginData = (await login.json()) as { access_token: string };
    const headers = { Authorization: `Bearer ${loginData.access_token}` };

    // Create project
    const project = await request.post(`${apiBase}/projects`, {
      headers,
      data: { name: `E2E Gen Proj ${suffix}` },
    });
    expect(project.status()).toBe(200);
    const projectData = (await project.json()) as ProjectResponse;

    // Create asset library
    const library = await request.post(`${apiBase}/asset-libraries`, {
      headers,
      data: { project_id: projectData.id, name: libraryName, kind: "video" },
    });
    expect(library.status()).toBe(200);
    const libraryData = (await library.json()) as LibraryResponse;

    // Upload source video
    const sourceFileName = "e2e-gen-source.mp4";
    const upload = await request.post(`${apiBase}/upload`, {
      headers,
      multipart: {
        project_id: projectData.id,
        library_id: libraryData.id,
        file: {
          name: sourceFileName,
          mimeType: "video/mp4",
          buffer: Buffer.from("e2e source data"),
        },
      },
    });
    expect(upload.status()).toBe(200);

    // Wait for asset to be ready
    await expect
      .poll(
        async () => {
          const assets = await request.get(`${apiBase}/assets`, {
            headers,
            params: { library_id: libraryData.id },
          });
          if (!assets.ok()) return `http_${assets.status()}`;
          const data = (await assets.json()) as AssetListResponse;
          const asset = data.items.find((a) => a.name === sourceFileName);
          if (!asset) return "missing";
          return asset.status;
        },
        { timeout: 30_000, intervals: [1_000, 2_000, 3_000] },
      )
      .toBe("ready");

    // Create an editing template so the generate page has at least one template
    // (templates are now loaded from API; new users have none by default)
    const template = await request.post(`${apiBase}/templates`, {
      headers,
      data: {
        name: `E2E 测试模板 ${suffix}`,
        mode: "pip",
        estimated_duration: 30,
        segments: [
          {
            segment_order: 1,
            duration_min: 5,
            duration_max: 30,
            material_type: "video",
          },
        ],
        tags: ["e2e"],
      },
    });
    expect(template.status(), await template.text()).toBe(201);
    const templateData = (await template.json()) as TemplateResponse;
    expect(templateData.id).toBeTruthy();

    // Set auth in localStorage
    await page.addInitScript(
      ({ token, user }) => {
        localStorage.setItem("access_token", token);
        localStorage.setItem(
          "auth-storage",
          JSON.stringify({
            state: { user, isAuthenticated: true },
            version: 0,
          }),
        );
      },
      {
        token: loginData.access_token,
        user: {
          id: registerData.user_id,
          user_id: registerData.user_id,
          email,
          username,
          display_name: username,
          is_email_verified: true,
          email_verified: true,
        },
      },
    );

    // Navigate to generate page
    await page.goto("/app/generate");
    await expect(page.getByRole("heading", { name: "一键生成" })).toBeVisible({
      timeout: 20_000,
    });

    // Step 1: template - default selected, click next
    await expect(page.locator(".xx-choice-item.selected")).toBeVisible();
    await page.getByRole("button", { name: "下一步" }).click();

    // Step 2: select material
    await expect(page.getByRole("heading", { name: /选择素材/ })).toBeVisible();
    const librarySelect = page.locator("select").first();
    await librarySelect.selectOption({ label: libraryName });
    const materialLabel = page.getByText(sourceFileName).locator("..");
    await expect(materialLabel.locator("input[type='checkbox']")).toBeVisible({
      timeout: 10_000,
    });
    await materialLabel.locator("input[type='checkbox']").check();
    await page.getByRole("button", { name: "下一步" }).click();

    // Step 3: title
    await expect(page.getByRole("heading", { name: /选择标题/ })).toBeVisible();
    const titleText = `E2E Test ${suffix}`;
    await page.getByPlaceholder("输入自定义标题…").fill(titleText);
    await page.getByRole("button", { name: "下一步" }).click();

    // Step 4: voice
    await expect(page.getByRole("heading", { name: /选择配音/ })).toBeVisible();
    const firstVoiceCard = page.locator(".xx-voice-choice-item").first();
    await firstVoiceCard.click();
    await page.getByRole("button", { name: "下一步" }).click();

    // Step 5: confirm and generate
    await expect(page.getByRole("heading", { name: /确认生成/ })).toBeVisible();

    // Wait for plan creation API to be called
    const createPlanPromise = page.waitForResponse(
      (response) =>
        response.url().includes("/edit-plans") &&
        response.request().method() === "POST" &&
        !response.url().includes("/generate"),
      { timeout: 30_000 },
    );

    // Click generate button
    await page
      .locator(".xx-btn-primary")
      .filter({ hasText: "确认生成" })
      .first()
      .click();

    // Verify plan was created successfully
    const planResp = await createPlanPromise;
    expect(planResp.ok()).toBeTruthy();
    const planData = (await planResp.json()) as { id: string };
    expect(planData.id).toBeTruthy();

    // Generation may fail in test env (no worker), that's OK
    // Just verify the flow started - check page shows generation-related UI
    await page
      .getByText(/生成中|生成完成|生成失败/)
      .isVisible({ timeout: 15_000 })
      .catch(() => false);
    // If we see progress or result, great; if not, flow still reached the end
    // which is sufficient for an E2E smoke test

    // Verify product library page loads (smoke: just verify page renders)
    await page.goto("/app/products");
    await expect(page).toHaveURL(/\/app\/products/);
    // Verify page container exists = page rendered correctly
    // (works in all states: loading/error/success - more reliable than checking search input)
    await expect(page.locator(".xx-products-page")).toBeVisible({
      timeout: 15_000,
    });
  });

  test("generation task API creates and lists tasks", async ({ request }) => {
    const suffix = Date.now().toString(36);
    const email = `e2e-gen-api-${suffix}@example.com`;
    const username = `e2e_gen_api_${suffix}`;

    const register = await request.post(`${apiBase}/auth/register`, {
      data: { email, username, password: PASSWORD, display_name: username },
    });
    expect(register.status()).toBe(201);

    const login = await loginWithRetry(request, email, PASSWORD);
    expect(login.status()).toBe(200);
    const loginData = (await login.json()) as { access_token: string };
    const headers = { Authorization: `Bearer ${loginData.access_token}` };

    const project = await request.post(`${apiBase}/projects`, {
      headers,
      data: { name: `E2E API Proj ${suffix}` },
    });
    expect(project.status()).toBe(200);

    // List generation tasks via task center API
    const tasks = await request.get(`${apiBase}/tasks`, { headers });
    expect(tasks.status()).toBe(200);
    const tasksData = await tasks.json();
    expect(Array.isArray(tasksData.items)).toBe(true);
  });
});
