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

type MockOptions = {
  sessionStatus?: number;
  captureApprove?: (pendingRef: string | null) => void;
  captureUserMe?: () => void;
  captureVerify?: (path: string, body: Record<string, unknown>) => void;
};

async function seedTokenLogin(page: Page): Promise<void> {
  await page.addInitScript(() => {
    window.localStorage.setItem("baron_auth_token", "e30.e30.e30");
    window.localStorage.setItem("baron_auth_provider", "ory");
    window.localStorage.removeItem("baron_auth_cookie_mode");
    window.localStorage.removeItem("baron_auth_pending_provider");
  });
}

async function seedSessionTokenLogin(page: Page): Promise<void> {
  await page.addInitScript(() => {
    window.sessionStorage.setItem("baron_auth_token", "e30.e30.e30");
    window.sessionStorage.setItem("baron_auth_provider", "ory");
    window.sessionStorage.removeItem("baron_auth_cookie_mode");
    window.sessionStorage.removeItem("baron_auth_pending_provider");
    window.localStorage.removeItem("baron_auth_token");
    window.localStorage.removeItem("baron_auth_provider");
    window.localStorage.removeItem("baron_auth_cookie_mode");
    window.localStorage.removeItem("baron_auth_pending_provider");
  });
}

async function mockUserfrontApis(
  page: Page,
  options: MockOptions = {},
): Promise<void> {
  const sessionStatus = options.sessionStatus ?? 200;

  await page.context().route("**/api/v1/**", async (route: Route) => {
    const requestUrl = new URL(route.request().url());
    const path = requestUrl.pathname;

    if (path.endsWith("/api/v1/user/me")) {
      options.captureUserMe?.();
      if (sessionStatus === 200) {
        await route.fulfill({
          status: 200,
          contentType: "application/json",
          body: JSON.stringify({
            id: "e2e-user",
            email: "e2e@example.com",
            name: "E2E User",
            phone: "+821012341234",
            department: "QA",
            affiliationType: "employee",
            companyCode: "BARON",
            tenant: {
              id: "tenant-1",
              name: "Baron",
              slug: "baron",
              description: "E2E tenant",
            },
          }),
        });
        return;
      }

      await route.fulfill({
        status: sessionStatus,
        contentType: "application/json",
        body: JSON.stringify({ error: "unauthorized" }),
      });
      return;
    }

    if (path.endsWith("/api/v1/user/rp/linked")) {
      await route.fulfill({
        status: 200,
        contentType: "application/json",
        body: JSON.stringify({ items: [] }),
      });
      return;
    }

    if (path.endsWith("/api/v1/audit/auth/timeline")) {
      await route.fulfill({
        status: 200,
        contentType: "application/json",
        body: JSON.stringify({ items: [], next_cursor: "" }),
      });
      return;
    }

    if (path.endsWith("/api/v1/auth/qr/approve")) {
      if (route.request().method() === "POST") {
        let pendingRef: string | null = null;
        try {
          const body = (route.request().postDataJSON() ?? {}) as {
            pendingRef?: string;
          };
          pendingRef = body.pendingRef ?? null;
          console.log(`[E2E-MOCK] /api/v1/auth/qr/approve POST body:`, body);
        } catch (e) {
          console.log(
            `[E2E-MOCK] /api/v1/auth/qr/approve POST body parse error:`,
            e,
          );
          pendingRef = null;
        }
        options.captureApprove?.(pendingRef);
      } else {
        console.log(
          `[E2E-MOCK] /api/v1/auth/qr/approve ${route.request().method()} request`,
        );
      }
      await route.fulfill({
        status: 200,
        contentType: "application/json",
        body: JSON.stringify({ ok: true }),
      });
      return;
    }

    if (
      path.endsWith("/api/v1/auth/magic-link/verify") ||
      path.endsWith("/api/v1/auth/login/code/verify") ||
      path.endsWith("/api/v1/auth/login/code/verify-short")
    ) {
      let body: Record<string, unknown> = {};
      try {
        body = (route.request().postDataJSON() ?? {}) as Record<
          string,
          unknown
        >;
      } catch {
        body = {};
      }
      options.captureVerify?.(path, body);
      await route.fulfill({
        status: 200,
        contentType: "application/json",
        body: JSON.stringify({
          status: "approved",
          pendingRef: "e2e-approved",
        }),
      });
      return;
    }

    await route.fulfill({
      status: 200,
      contentType: "application/json",
      body: JSON.stringify({}),
    });
  });
}

function collectClientFailures(page: Page): string[] {
  const failures: string[] = [];
  page.on("pageerror", (error) => {
    const text = error.message.trim();
    if (text !== "") {
      failures.push(text);
    }
  });
  page.on("console", (message) => {
    const text = message.text().trim();
    if (text === "") {
      return;
    }
    if (
      message.type() === "error" ||
      (/exception|verify_failed|verification failed|인증 실패/i.test(text) &&
        !text.includes("Exception while loading service worker"))
    ) {
      failures.push(text);
    }
  });
  return failures;
}

async function makeWindowCloseNavigateToRoot(page: Page): Promise<void> {
  await page.addInitScript(() => {
    window.close = () => {
      window.location.href = "/";
    };
  });
}

async function enableFlutterAccessibility(page: Page): Promise<void> {
  await page.waitForTimeout(300);
  const button = page.getByRole("button", { name: "Enable accessibility" });
  const placeholder = page.locator("flt-semantics-placeholder").first();

  await button.click({ force: true, timeout: 1_000 }).catch(async () => {
    await placeholder.click({ force: true, timeout: 1_000 }).catch(async () => {
      await placeholder.evaluate((node) => {
        (node as HTMLElement).click();
      });
    });
  });
  await page.waitForTimeout(500);
}

test.describe("UserFront WASM auth routing", () => {
  test.describe.configure({ mode: "default" });

  test("비로그인 /ko 진입 시 /ko/signin 으로 리다이렉트된다", async ({
    page,
  }) => {
    await mockUserfrontApis(page, { sessionStatus: 401 });

    await page.goto("/ko");

    await expect(page).toHaveURL(/\/ko\/signin(?:\?.*)?$/);
  });

  test("로그인 상태 /ko 진입 후 새로고침해도 /ko/dashboard 를 유지한다", async ({
    page,
  }) => {
    await seedTokenLogin(page);
    await mockUserfrontApis(page);

    await page.goto("/ko");
    await expect(page).toHaveURL(/\/ko\/dashboard$/);

    await page.reload();
    await expect(page).toHaveURL(/\/ko\/dashboard$/);
  });

  test("sessionStorage 기반 로그인 상태에서도 /ko/dashboard 를 유지한다", async ({
    page,
  }) => {
    await seedSessionTokenLogin(page);
    await mockUserfrontApis(page);

    await page.goto("/ko");
    await expect(page).toHaveURL(/\/ko\/dashboard$/);
  });

  test("비로그인 /ko/approve 는 signin(+notice)으로 이동한다", async ({
    page,
  }) => {
    await mockUserfrontApis(page, { sessionStatus: 401 });

    await page.goto("/ko/approve?ref=e2e-ref");

    await expect(page).toHaveURL(/\/ko\/signin\?notice=qr_login_required$/);
  });

  test("로그인 상태 /ko/approve 는 승인 API 호출 후 dashboard로 이동한다", async ({
    page,
  }) => {
    let approvedRef: string | null = null;

    await seedTokenLogin(page);
    await mockUserfrontApis(page, {
      captureApprove: (pendingRef) => {
        approvedRef = pendingRef;
      },
    });

    await page.goto("/ko/approve?ref=e2e-approve-ref");

    await expect(page).toHaveURL(/\/ko\/dashboard(?:\?.*)?$/, {
      timeout: 10_000,
    });
    expect(approvedRef).toBe("e2e-approve-ref");
  });

  test("verifyOnly 승인 완료 화면의 상단 액션은 signin으로 복귀시킨다", async ({
    page,
  }) => {
    let userMeCalls = 0;
    const clientFailures = collectClientFailures(page);
    const verifyRequests: Array<{
      path: string;
      body: Record<string, unknown>;
    }> = [];

    await mockUserfrontApis(page, {
      sessionStatus: 401,
      captureUserMe: () => {
        userMeCalls += 1;
      },
      captureVerify: (path, body) => {
        verifyRequests.push({ path, body });
      },
    });
    await makeWindowCloseNavigateToRoot(page);

    await page.goto("/ko/l/AB123456");

    await expect.poll(() => verifyRequests.length, { timeout: 10_000 }).toBe(1);
    expect(verifyRequests[0].path).toContain(
      "/api/v1/auth/login/code/verify-short",
    );
    expect(verifyRequests[0].body).toMatchObject({
      shortCode: "AB123456",
      verifyOnly: true,
    });

    await page.locator("flt-glass-pane").click({
      position: { x: 30, y: 28 },
      force: true,
    });
    await page.waitForTimeout(300);
    await expect(page).toHaveURL(/\/ko\/signin(?:\?.*)?$/);
    expect(userMeCalls).toBe(0);
    expect(
      clientFailures.filter(
        (failure) => !failure.includes("401 (Unauthorized)"),
      ),
    ).toEqual([]);
  });

  test("verifyOnly 승인 완료 버튼은 SMS 링크에서 로그인 창으로 이동하고 user/me 조회를 만들지 않는다", async ({
    page,
  }) => {
    let userMeCalls = 0;
    let verifyCalls = 0;
    const clientFailures = collectClientFailures(page);

    await mockUserfrontApis(page, {
      sessionStatus: 401,
      captureUserMe: () => {
        userMeCalls += 1;
      },
      captureVerify: () => {
        verifyCalls += 1;
      },
    });
    await makeWindowCloseNavigateToRoot(page);

    await page.goto("/ko/l/AB123456");

    await expect.poll(() => verifyCalls, { timeout: 10_000 }).toBe(1);
    await expect(page).toHaveURL(/\/ko\/verify-complete$/);
    expect(userMeCalls).toBe(0);

    await enableFlutterAccessibility(page);
    await page.getByRole("button", { name: "로그인 창으로 이동하기" }).click();

    expect(userMeCalls).toBe(0);
    await expect(page).toHaveURL(/\/ko\/signin(?:\?.*)?$/);
    expect(
      clientFailures.filter(
        (failure) => !failure.includes("401 (Unauthorized)"),
      ),
    ).toEqual([]);
  });

  test("verifyOnly 원격 승인 완료는 로그인 창 이동 CTA와 안내 문구를 표시한다", async ({
    page,
  }) => {
    let verifyCalls = 0;
    const clientFailures = collectClientFailures(page);

    await mockUserfrontApis(page, {
      sessionStatus: 401,
      captureVerify: () => {
        verifyCalls += 1;
      },
    });
    await makeWindowCloseNavigateToRoot(page);

    await page.goto("/ko/l/AB123456");

    await expect.poll(() => verifyCalls, { timeout: 10_000 }).toBe(1);
    await expect(page).toHaveURL(/\/ko\/verify-complete$/);
    await enableFlutterAccessibility(page);

    await expect(page.getByText("로그인 승인 완료")).toBeVisible();
    await expect(
      page.getByText("요청하신 로그인이 완료되었습니다"),
    ).toBeVisible();
    await expect(page.getByRole("button", { name: "창 닫기" })).toHaveCount(0);
    await expect(
      page.getByRole("button", { name: "로그인 창으로 이동하기" }),
    ).toBeVisible();

    await page.getByRole("button", { name: "로그인 창으로 이동하기" }).click();
    await expect(page).toHaveURL(/\/ko\/signin(?:\?.*)?$/);
    expect(clientFailures).toEqual([]);
  });

  test("루트/로그인에 붙은 인증 payload는 전용 verify 라우트에서만 소비하고 완료 URL을 정리한다", async ({
    page,
  }) => {
    let userMeCalls = 0;
    const verifyRequests: Array<{
      path: string;
      body: Record<string, unknown>;
    }> = [];
    const clientFailures = collectClientFailures(page);

    await mockUserfrontApis(page, {
      sessionStatus: 401,
      captureUserMe: () => {
        userMeCalls += 1;
      },
      captureVerify: (path, body) => {
        verifyRequests.push({ path, body });
      },
    });

    await page.goto(
      "/?loginId=e2e%40example.com&code=654321&pendingRef=pending-root&utm=drop",
    );
    await expect.poll(() => verifyRequests.length, { timeout: 10_000 }).toBe(1);
    await expect
      .poll(() => page.url(), { timeout: 10_000 })
      .toContain("/ko/verify-complete");
    expect(verifyRequests[0].path).toContain("/api/v1/auth/login/code/verify");
    expect(verifyRequests[0].body).toMatchObject({
      loginId: "e2e@example.com",
      code: "654321",
      pendingRef: "pending-root",
      verifyOnly: true,
    });
    expect(page.url()).not.toContain("loginId=");
    expect(page.url()).not.toContain("code=");
    expect(page.url()).not.toContain("pendingRef=");
    expect(page.url()).not.toContain("utm=");
    expect(userMeCalls).toBe(0);
    expect(clientFailures).toEqual([]);
  });

  test("로그인 페이지에 붙은 인증 payload도 전용 verify 라우트로 넘긴다", async ({
    page,
  }) => {
    let userMeCalls = 0;
    const verifyRequests: Array<{
      path: string;
      body: Record<string, unknown>;
    }> = [];
    const clientFailures = collectClientFailures(page);

    await mockUserfrontApis(page, {
      sessionStatus: 401,
      captureUserMe: () => {
        userMeCalls += 1;
      },
      captureVerify: (path, body) => {
        verifyRequests.push({ path, body });
      },
    });

    await page.goto("/ko/signin?loginId=e2e%40example.com&code=999999");
    await expect.poll(() => verifyRequests.length, { timeout: 10_000 }).toBe(1);
    await expect
      .poll(() => page.url(), { timeout: 10_000 })
      .toContain("/ko/verify-complete");
    expect(verifyRequests[0].body).toMatchObject({
      loginId: "e2e@example.com",
      code: "999999",
      verifyOnly: true,
    });
    expect(userMeCalls).toBe(0);
    expect(page.url()).not.toContain("loginId=");
    expect(page.url()).not.toContain("code=");
    expect(clientFailures).toEqual([]);
  });

  test("verifyOnly 승인 링크를 팝업에서 닫으면 창만 닫히고 부모는 이동하지 않는다", async ({
    page,
  }, testInfo) => {
    test.skip(
      testInfo.project.name === "webkit-mobile-webapp",
      "Mobile WebKit closes the opener page when this popup flow closes in headless mode.",
    );
    let userMeCalls = 0;
    let verifyCalls = 0;
    const clientFailures = collectClientFailures(page);

    await mockUserfrontApis(page, {
      sessionStatus: 401,
      captureUserMe: () => {
        userMeCalls += 1;
      },
      captureVerify: () => {
        verifyCalls += 1;
      },
    });

    const baseURL = testInfo.project.use.baseURL;
    if (typeof baseURL !== "string") throw new Error("baseURL is required");
    const popupURL = new URL("/ko/l/AB123456", baseURL).toString();
    const parentURL = new URL("/version.json", baseURL).toString();

    await page.goto(parentURL);
    await expect(page).toHaveURL(parentURL);

    const popupPromise = page.waitForEvent("popup");
    await page.evaluate((url) => {
      window.open(url, "_blank");
    }, popupURL);
    const popup = await popupPromise;

    await expect.poll(() => verifyCalls, { timeout: 10_000 }).toBe(1);
    await expect(popup).toHaveURL(/\/ko\/verify-complete$/);
    expect(userMeCalls).toBe(0);

    if (!popup.isClosed()) {
      const closePromise = popup.waitForEvent("close").catch(() => undefined);
      try {
        await enableFlutterAccessibility(popup);
        await popup
          .getByRole("button", { name: "로그인 창으로 이동하기" })
          .click();
      } catch (error) {
        if (!popup.isClosed()) {
          throw error;
        }
      }
      await closePromise;
    }

    expect(userMeCalls).toBe(0);
    await expect(page).toHaveURL(parentURL);
    expect(clientFailures).toEqual([]);
  });

  test("verifyOnly 승인 완료 버튼은 이메일 magic link에서도 로그인 창으로 이동하고 user/me 조회를 만들지 않는다", async ({
    page,
  }) => {
    let userMeCalls = 0;
    const clientFailures = collectClientFailures(page);
    const verifyRequests: Array<{
      path: string;
      body: Record<string, unknown>;
    }> = [];

    await mockUserfrontApis(page, {
      sessionStatus: 401,
      captureUserMe: () => {
        userMeCalls += 1;
      },
      captureVerify: (path, body) => {
        verifyRequests.push({ path, body });
      },
    });
    await makeWindowCloseNavigateToRoot(page);

    await page.goto("/ko/verify/e2e-email-token");

    await expect.poll(() => verifyRequests.length, { timeout: 10_000 }).toBe(1);
    expect(verifyRequests[0].path).toContain("/api/v1/auth/magic-link/verify");
    expect(verifyRequests[0].body).toMatchObject({
      token: "e2e-email-token",
      verifyOnly: true,
    });

    await enableFlutterAccessibility(page);
    await page.getByRole("button", { name: "로그인 창으로 이동하기" }).click();

    expect(userMeCalls).toBe(0);
    await expect(page).toHaveURL(/\/ko\/signin(?:\?.*)?$/);
    expect(clientFailures).toEqual([]);
  });

  test("verifyOnly 승인 완료 버튼은 이메일 code link에서도 로그인 창으로 이동하고 user/me 조회를 만들지 않는다", async ({
    page,
  }) => {
    let userMeCalls = 0;
    const clientFailures = collectClientFailures(page);
    const verifyRequests: Array<{
      path: string;
      body: Record<string, unknown>;
    }> = [];

    await mockUserfrontApis(page, {
      sessionStatus: 401,
      captureUserMe: () => {
        userMeCalls += 1;
      },
      captureVerify: (path, body) => {
        verifyRequests.push({ path, body });
      },
    });
    await makeWindowCloseNavigateToRoot(page);

    await page.goto(
      "/ko/verify?loginId=e2e%40example.com&code=654321&pendingRef=pending-email",
    );

    await expect.poll(() => verifyRequests.length, { timeout: 10_000 }).toBe(1);
    expect(verifyRequests[0].path).toContain("/api/v1/auth/login/code/verify");
    expect(verifyRequests[0].body).toMatchObject({
      loginId: "e2e@example.com",
      code: "654321",
      pendingRef: "pending-email",
      verifyOnly: true,
    });

    await enableFlutterAccessibility(page);
    await page.getByRole("button", { name: "로그인 창으로 이동하기" }).click();

    expect(userMeCalls).toBe(0);
    await expect(page).toHaveURL(/\/ko\/signin(?:\?.*)?$/);
    expect(clientFailures).toEqual([]);
  });
});
