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

type GotoDashboardRouteOptions = {
  timeoutMs?: number;
  waitUntil?: "commit" | "domcontentloaded" | "load" | "networkidle";
};

const DEFAULT_TIMEOUT_MS = 300_000;
const APP_ROUTE_PATTERN = /\/(login|dashboard)(\/[^?#]*)?([?#].*)?$/;
const E2E_PASSWORD =
  process.env.OMNIROUTE_E2E_PASSWORD || process.env.INITIAL_PASSWORD || "omniroute-e2e-password";

async function waitForAppRoute(page: Page, timeoutMs: number) {
  await page.waitForURL(APP_ROUTE_PATTERN, { timeout: timeoutMs });
  await page.locator("body").waitFor({ state: "visible", timeout: timeoutMs });
}

async function finishOnboardingIfNeeded(page: Page, timeoutMs: number) {
  if (!page.url().includes("/dashboard/onboarding")) return;

  const skipWizardButton = page.getByRole("button", {
    name: /skip wizard|skip/i,
  });
  await expect(skipWizardButton).toBeVisible({ timeout: timeoutMs });
  await skipWizardButton.click();
  await page.waitForURL(/\/dashboard(\/.*)?$/, { timeout: timeoutMs });
  await page.locator("body").waitFor({ state: "visible", timeout: timeoutMs });
}

async function loginIfNeeded(page: Page, timeoutMs: number) {
  if (!page.url().includes("/login")) return;

  const passwordInput = page.locator('input[type="password"]').first();
  await expect(passwordInput).toBeVisible({ timeout: timeoutMs });
  await passwordInput.fill(E2E_PASSWORD);

  const submitButton = page.locator("form").getByRole("button").first();
  await expect(submitButton).toBeEnabled({ timeout: timeoutMs });
  await Promise.all([
    page.waitForURL(/\/dashboard(\/.*)?$/, { timeout: timeoutMs }),
    submitButton.click(),
  ]);
  await page.locator("body").waitFor({ state: "visible", timeout: timeoutMs });
}

async function getDashboardAuthState(page: Page) {
  return await page.evaluate(async () => {
    const safeJson = async (response: Response) => {
      try {
        return (await response.json()) as any;
      } catch {
        return null;
      }
    };

    const [requireLoginResponse, settingsResponse] = await Promise.all([
      fetch("/api/settings/require-login", {
        credentials: "include",
        cache: "no-store",
      }),
      fetch("/api/settings", {
        credentials: "include",
        cache: "no-store",
      }),
    ]);

    const requireLoginPayload = await safeJson(requireLoginResponse);

    return {
      requireLogin: requireLoginPayload?.requireLogin === true,
      settingsStatus: settingsResponse.status,
    };
  });
}

function isAtRequestedRoute(page: Page, requestedUrl: string) {
  const current = new URL(page.url());
  const requested = new URL(requestedUrl, current.origin);
  return current.pathname === requested.pathname && current.search === requested.search;
}

export async function gotoDashboardRoute(
  page: Page,
  url: string,
  options: GotoDashboardRouteOptions = {}
) {
  const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
  const waitUntil = options.waitUntil ?? "commit";
  let lastError: unknown;

  for (let attempt = 0; attempt < 2; attempt += 1) {
    try {
      await page.goto(url, { waitUntil, timeout: timeoutMs });
      await waitForAppRoute(page, timeoutMs);
      await finishOnboardingIfNeeded(page, timeoutMs);

      if (page.url().includes("/login")) {
        await loginIfNeeded(page, timeoutMs);
      }

      if (page.url().includes("/dashboard/onboarding") || page.url().includes("/login")) {
        await page.goto(url, { waitUntil, timeout: timeoutMs });
        await waitForAppRoute(page, timeoutMs);
        await finishOnboardingIfNeeded(page, timeoutMs);
        await loginIfNeeded(page, timeoutMs);
      }

      const authState = await getDashboardAuthState(page);
      if (authState.requireLogin && authState.settingsStatus === 401) {
        await page.goto("/login", { waitUntil, timeout: timeoutMs });
        await waitForAppRoute(page, timeoutMs);
        await loginIfNeeded(page, timeoutMs);
        await page.goto(url, { waitUntil, timeout: timeoutMs });
        await waitForAppRoute(page, timeoutMs);
        await finishOnboardingIfNeeded(page, timeoutMs);
      }

      if (!isAtRequestedRoute(page, url)) {
        await page.goto(url, { waitUntil, timeout: timeoutMs });
        await waitForAppRoute(page, timeoutMs);
        await finishOnboardingIfNeeded(page, timeoutMs);
      }

      await page.locator("body").waitFor({ state: "visible", timeout: timeoutMs });
      return;
    } catch (error: any) {
      lastError = error;
      await page.waitForTimeout(1000);
    }
  }

  throw lastError instanceof Error ? lastError : new Error(`Failed to open protected route ${url}`);
}
