import path from "node:path";

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

import { mockCoreApis } from "@e2e/fixtures/api-mocks";
import { apiEnvelope } from "@e2e/fixtures/data/common";
import { e2eEmailUser } from "@e2e/fixtures/data/auth";
import { chatCharactersResponse } from "@e2e/fixtures/data/character";
import {
  emptyChatHistoryResponse,
  emptyChatPreviewsResponse,
} from "@e2e/fixtures/data/chat";
import { paymentPlansResponse } from "@e2e/fixtures/data/payment";
import { userEntitlementsResponse } from "@e2e/fixtures/data/user";
import {
  defaultCharacterChatPath,
  defaultCharacterSplashPath,
  seedEmailSession,
} from "@e2e/fixtures/helpers/auth";

const previewDirectory = path.join(
  process.cwd(),
  ".codex",
  "artifacts",
  "frontend-preview",
  "2026-07-22",
  "favorite-menu",
);

test.beforeEach(async ({ page }) => {
  await registerFavoriteMenuMocks(page);
});

test("favorite entry and three-tab Menu navigation render on the real pages", async ({
  page,
}, testInfo) => {
  const mobile = testInfo.project.name.includes("mobile");
  await page.setViewportSize(
    mobile ? { width: 390, height: 844 } : { width: 1440, height: 900 },
  );
  const browserErrors = collectBrowserErrors(page);

  await seedEmailSession(page);
  await page.goto(defaultCharacterChatPath);

  await expect(page.getByRole("button", { name: "Save CozSweet" })).toBeVisible();
  await expect(
    page.getByRole("button", { name: /First recharge offer, 50% off/i }),
  ).toBeVisible();
  await expect(page.getByRole("button", { name: "Profile" })).toHaveCount(0);
  await savePreview(page, `${mobile ? "mobile" : "desktop"}-chat.png`);

  await page.goto(defaultCharacterSplashPath);
  await expect(page.getByRole("button", { name: "Save CozSweet" })).toBeVisible();
  await expect(page.getByRole("button", { name: "Menu" })).toBeVisible();
  const navigation = page.getByRole("navigation", {
    name: "Primary navigation",
  });
  await expect(navigation).toContainText("Chat");
  await expect(navigation).toContainText("Private Zone");
  await expect(navigation).toContainText("Menu");
  await savePreview(page, `${mobile ? "mobile" : "desktop"}-splash.png`);

  await page.goto("/characters/elio/private-zone");
  await expect(page.getByRole("button", { name: "Save CozSweet" })).toBeVisible();
  await expect(page.getByRole("button", { name: "Menu" })).toBeVisible();
  await savePreview(
    page,
    `${mobile ? "mobile" : "desktop"}-private-zone.png`,
  );

  await page.getByRole("button", { name: "Menu" }).click();
  await expect(page.getByRole("heading", { name: "Menu" })).toBeVisible();
  await expect(page.getByRole("button", { name: "Save CozSweet" })).toBeVisible();
  await expect(page.getByRole("button", { name: "Menu" })).toHaveAttribute(
    "aria-current",
    "page",
  );
  await savePreview(page, `${mobile ? "mobile" : "desktop"}-menu.png`);

  if (mobile) {
    await page.evaluate(() => {
      localStorage.setItem("cozsweet:favorite_entry", "1");
    });
    await page.goto(defaultCharacterChatPath);
    await expect(page.getByRole("button", { name: "Download" })).toBeVisible();
    await savePreview(page, "mobile-android-external-chat-download.png");

    await page.addInitScript(() => {
      Object.defineProperty(navigator, "userAgent", {
        configurable: true,
        value:
          "Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Mobile/15E148 Safari/604.1",
      });
    });
    await page.goto(defaultCharacterChatPath);
    const iosDownload = page.getByRole("button", { name: "Download" });
    await expect(iosDownload).toBeVisible();
    await iosDownload.click();
    await expect(
      page.getByRole("heading", { name: "Add CozSweet to Home Screen" }),
    ).toBeVisible();
    await expect(page.getByRole("dialog")).toContainText(
      "In Safari, tap the Share button, then choose “Add to Home Screen”.",
    );
    await savePreview(page, "mobile-ios-external-chat-download.png");
  }

  expect(browserErrors.filter(isFeatureRuntimeError)).toEqual([]);
});

async function registerFavoriteMenuMocks(page: Page): Promise<void> {
  await mockCoreApis(page);
  await page.route("**/api/payment/plans**", async (route) => {
    await route.fulfill({
      json: apiEnvelope({
        ...paymentPlansResponse,
        isFirstRecharge: true,
      }),
    });
  });
  await page.route("**/api/private-zone/albums**", async (route) => {
    await route.fulfill({
      json: apiEnvelope({ items: [], creditBalance: 80 }),
    });
  });
  await page.context().route("**/api/**", async (route) => {
    const pathname = new URL(route.request().url()).pathname;
    if (pathname === "/api/auth/session") {
      await route.fulfill({
        json: {
          expires: "2099-12-31T23:59:59.000Z",
          user: { email: null, image: null, name: null },
        },
      });
      return;
    }
    if (pathname === "/api/characters") {
      await route.fulfill({ json: apiEnvelope(chatCharactersResponse) });
      return;
    }
    if (pathname === "/api/user/profile") {
      await route.fulfill({ json: apiEnvelope(e2eEmailUser) });
      return;
    }
    if (pathname === "/api/user/entitlements") {
      await route.fulfill({ json: apiEnvelope(userEntitlementsResponse) });
      return;
    }
    if (pathname === "/api/chat/history") {
      await route.fulfill({ json: apiEnvelope(emptyChatHistoryResponse) });
      return;
    }
    if (pathname === "/api/chat/previews") {
      await route.fulfill({ json: apiEnvelope(emptyChatPreviewsResponse) });
      return;
    }
    if (pathname === "/api/payment/plans") {
      await route.fulfill({
        json: apiEnvelope({
          ...paymentPlansResponse,
          isFirstRecharge: true,
        }),
      });
      return;
    }
    if (pathname === "/api/payment/vip-status") {
      await route.fulfill({
        json: apiEnvelope({ isVip: false, expiresAt: null }),
      });
      return;
    }
    if (pathname === "/api/private-zone/albums") {
      await route.fulfill({
        json: apiEnvelope({ items: [], creditBalance: 80 }),
      });
      return;
    }
    await route.fallback();
  });
}

function collectBrowserErrors(page: Page): string[] {
  const errors: string[] = [];
  page.on("pageerror", (error) => errors.push(error.message));
  page.on("console", (message) => {
    if (message.type() === "error") errors.push(message.text());
  });
  return errors;
}

function isFeatureRuntimeError(message: string): boolean {
  const isKnownLocalPreviewDependencyError =
    message.includes("[ApiLoggingInterceptor]") ||
    message.includes("[next-auth][error][CLIENT_FETCH_ERROR]") ||
    message.includes("[Result] Result.wrap caught exception");
  return !isKnownLocalPreviewDependencyError;
}

async function savePreview(page: Page, fileName: string): Promise<void> {
  if (process.env.FRONTEND_PREVIEW_SCREENSHOTS !== "1") return;
  await page.screenshot({
    path: path.join(previewDirectory, fileName),
    animations: "disabled",
  });
}
