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 { clearBrowserState } from "@e2e/fixtures/test-helpers";

const characters = [
  {
    id: "elio",
    slug: "elio",
    displayName: "Elio Silvestri",
    shortName: "Elio",
    cover: "elio.png",
    splashCover: "elio.png",
  },
  {
    id: "maya-tan",
    slug: "maya",
    displayName: "Maya Tan",
    shortName: "Maya",
    cover: "maya.webp",
    splashCover: "maya-home.webp",
  },
  {
    id: "nayeli-cervantes",
    slug: "nayeli",
    displayName: "Nayeli Cervantes",
    shortName: "Nayeli",
    cover: "nayeli.webp",
    splashCover: "nayeli.webp",
  },
] as const;

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

test.beforeEach(async ({ baseURL, context, page }) => {
  await clearBrowserState(context, page, baseURL);
  await mockCoreApis(page);
  await registerMultiRoleCommercialMocks(page);
});

for (const character of characters) {
  test(`${character.displayName} homepage renders its assigned cover`, async ({
    page,
  }, testInfo) => {
    const mobile = testInfo.project.name.includes("mobile");
    await page.setViewportSize(
      mobile ? { width: 390, height: 844 } : { width: 1440, height: 900 },
    );

    await page.goto(`/characters/${character.slug}/splash`);

    const cover = page.locator(`img[src*="${character.splashCover}"]`);
    await expect(cover).toBeVisible();
    await expect(
      page.getByRole("navigation", { name: "Primary navigation" }),
    ).toContainText("Private Zone");
    await expect
      .poll(() =>
        cover.evaluate((image: HTMLImageElement) =>
          image.complete ? image.naturalWidth : 0,
        ),
      )
      .toBeGreaterThan(0);

    if (
      process.env.FRONTEND_PREVIEW_SCREENSHOTS === "1" &&
      character.id !== "elio"
    ) {
      await page.screenshot({
        path: path.join(
          previewDirectory,
          `${character.slug}-${mobile ? "mobile-390x844" : "desktop-1440x900"}.png`,
        ),
        animations: "disabled",
      });
    }
  });

  test(`${character.displayName} can open a character-scoped Private Zone`, async ({
    page,
  }) => {
    const momentsRequest = page.waitForRequest((request) => {
      const url = new URL(request.url());
      return (
        url.pathname === "/api/private-zone/posts" &&
        url.searchParams.get("characterId") === character.id
      );
    });
    const albumRequest = page.waitForRequest((request) => {
      const url = new URL(request.url());
      return (
        url.pathname === "/api/private-zone/albums" &&
        url.searchParams.get("characterId") === character.id
      );
    });

    await page.goto(`/characters/${character.slug}/private-zone`);

    await Promise.all([momentsRequest, albumRequest]);
    await expect(
      page.getByRole("heading", { name: "Private albums" }),
    ).toBeVisible();
    await expect(page.getByRole("tab")).toHaveText(["Albums", "Moments"]);
    await expect(page.getByRole("tab", { name: "Albums" })).toHaveAttribute(
      "aria-selected",
      "true",
    );
    await expect(page.getByText(character.displayName).last()).toBeVisible();
    const albumButton = page.getByRole("button", {
      name: `View locked collection with 8 images from ${character.displayName}`,
    });
    if (character.id === "maya-tan") {
      await expect(albumButton).toHaveCount(0);
      await expect(page.getByRole("button", { name: "Refresh" })).toHaveCount(0);
      await expect(page.getByText("No private albums yet.")).toHaveCount(0);
    } else {
      await expect(albumButton).toBeVisible();
    }
  });

  test(`${character.displayName} receives a character-scoped Tip catalog`, async ({
    page,
  }) => {
    const catalogRequest = page.waitForRequest((request) => {
      const url = new URL(request.url());
      return (
        url.pathname === "/api/payment/gift-products" &&
        url.searchParams.get("characterId") === character.id
      );
    });

    await page.goto(`/characters/${character.slug}/tip`);

    await catalogRequest;
    await expect(
      page.getByRole("heading", { name: `Buy ${character.shortName} a coffee` }),
    ).toBeVisible();
    await expect(page.getByRole("radio", { name: /Small Coffee/ })).toBeChecked();
    await expect(
      page.getByRole("button", { name: "Order and Buy" }),
    ).toBeEnabled();
    await expect(
      page.getByText("This character does not have any gifts available yet."),
    ).toHaveCount(0);
  });
}

test("a real Private Zone request failure still offers Retry", async ({
  page,
}) => {
  await page.route("**/api/private-zone/albums**", async (route) => {
    await route.fulfill({ status: 503, json: { message: "Albums unavailable" } });
  });

  await page.goto("/characters/maya/private-zone");

  await expect(page.getByRole("button", { name: "Retry" })).toBeVisible();
  await expect(page.getByRole("button", { name: "Refresh" })).toHaveCount(0);
});

async function registerMultiRoleCommercialMocks(page: Page): Promise<void> {
  await page.route("**/api/private-zone/posts**", async (route) => {
    const url = new URL(route.request().url());
    const characterId = url.searchParams.get("characterId") ?? "elio";
    await route.fulfill({
      json: apiEnvelope({
        characterId,
        items: [],
        nextCursor: null,
        hasMore: false,
        creditBalance: 1000,
        currency: "credits",
      }),
    });
  });

  await page.route("**/api/private-zone/albums**", async (route) => {
    const url = new URL(route.request().url());
    const characterId = url.searchParams.get("characterId") ?? "elio";
    const character =
      characters.find((candidate) => candidate.id === characterId) ?? characters[0];

    const hasCompleteAlbum = character.id !== "maya-tan";
    await route.fulfill({
      json: apiEnvelope({
        creditBalance: 1000,
        pendingImageCount: hasCompleteAlbum ? 0 : 7,
        hasIncompleteContent: !hasCompleteAlbum,
        items: hasCompleteAlbum ? [
          {
            albumId: `album-${character.id}`,
            title: `${character.shortName} private photos`,
            content: null,
            previewText: "Unlock this private album",
            imageCount: 8,
            images: [
              {
                url: `/images/private-zone/banner/${character.slug}.png`,
                locked: true,
                index: 0,
              },
            ],
            locked: true,
            unlocked: false,
            unlockCost: 320,
            publishedAt: "2026-07-22T10:00:00+08:00",
            lockDetail: { locked: true },
          },
        ] : [],
      }),
    });
  });

  await page.route("**/api/payment/gift-products**", async (route) => {
    const url = new URL(route.request().url());
    const characterId = url.searchParams.get("characterId") ?? "elio";
    const planPrefix =
      characterId === "elio"
        ? "tip"
        : `tip_${characterId.replaceAll("-", "_")}`;

    await route.fulfill({
      json: apiEnvelope({
        characterId,
        categories: [
          {
            category: "coffee",
            name: "Coffee",
            productCount: 1,
            imageUrl: null,
          },
        ],
        plans: [
          {
            planId: `${planPrefix}_coffee_usd_4_99`,
            planName: "Small Coffee",
            orderType: "tip",
            tipType: "coffee_small",
            category: "coffee",
            characterId,
            description: "A character-scoped coffee gift",
            imageUrl: null,
            amountCents: 499,
            currency: "USD",
            autoRenew: false,
            isFirstRechargeOffer: false,
            firstRechargeDiscountPercent: 0,
            promotionType: null,
          },
        ],
      }),
    });
  });
}
