import { act, type Dispatch } from "react";
import { createRoot, type Root } from "react-dom/client";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";

import { getCharacterBySlug } from "@/data/constants/character";
import type { LoginStatus } from "@/data/schemas/auth";
import { CharacterProvider } from "@/providers/character-provider";
import { getCharacterRoutes } from "@/router/routes";
import type { PrivateZoneEvent } from "@/stores/private-zone";
import type { PrivateZoneUnlockPaywallRequest } from "@/stores/private-zone/private-zone-state";

import { usePrivateZoneUnlockPaywallNavigation } from "../use-private-zone-flow";

const mocks = vi.hoisted(() => ({
  openAuth: vi.fn(),
  openSubscription: vi.fn(),
  paywallShown: vi.fn(),
}));

vi.mock("@/router/use-app-navigator", () => ({
  useAppNavigator: () => ({
    openAuth: mocks.openAuth,
    openSubscription: mocks.openSubscription,
  }),
}));

vi.mock("@/lib/analytics", () => ({
  behaviorAnalytics: {
    paywallShown: mocks.paywallShown,
  },
}));

const unlockPaywallRequest: PrivateZoneUnlockPaywallRequest = {
  albumId: "album-1",
  reason: "insufficient_credits",
  requiredCredits: 10,
  currentCredits: 3,
  shortfallCredits: 7,
};

describe("Private Zone paywall navigation", () => {
  let container: HTMLDivElement;
  let root: Root;

  beforeEach(() => {
    (globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean })
      .IS_REACT_ACT_ENVIRONMENT = true;
    mocks.openAuth.mockReset();
    mocks.openSubscription.mockReset();
    mocks.paywallShown.mockReset();
    container = document.createElement("div");
    document.body.appendChild(container);
    root = createRoot(container);
  });

  afterEach(() => {
    act(() => root.unmount());
    container.remove();
  });

  it("opens VIP and credit options for a non-VIP user", () => {
    const roomDispatch = vi.fn<Dispatch<PrivateZoneEvent>>();

    renderHarness(root, "email", roomDispatch);

    expect(mocks.openSubscription).toHaveBeenCalledWith({
      type: "vip",
      returnTo: "private-zone",
      analytics: {
        entryPoint: "private_album_unlock",
        triggerReason: "insufficient_credits",
      },
    });
    expect(roomDispatch).toHaveBeenCalledWith({
      type: "PrivateZoneUnlockPaywallConsumed",
    });
  });

  it("keeps guest users on the private zone auth return path", () => {
    const roomDispatch = vi.fn<Dispatch<PrivateZoneEvent>>();

    renderHarness(root, "guest", roomDispatch);

    expect(mocks.openAuth).toHaveBeenCalledWith(
      getCharacterRoutes("maya").privateZone,
    );
    expect(mocks.openSubscription).not.toHaveBeenCalled();
  });
});

function renderHarness(
  root: Root,
  loginStatus: LoginStatus,
  roomDispatch: Dispatch<PrivateZoneEvent>,
): void {
  const character = getCharacterBySlug("maya");
  if (!character) throw new Error("Missing Maya character fixture");
  act(() => {
    root.render(
      <CharacterProvider character={character}>
        <Harness loginStatus={loginStatus} roomDispatch={roomDispatch} />
      </CharacterProvider>,
    );
  });
}

function Harness({
  loginStatus,
  roomDispatch,
}: {
  loginStatus: LoginStatus;
  roomDispatch: Dispatch<PrivateZoneEvent>;
}) {
  usePrivateZoneUnlockPaywallNavigation({
    loginStatus,
    roomDispatch,
    unlockPaywallRequest,
    isVip: false,
  });
  return null;
}
