/* @vitest-environment jsdom */

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

const instances = vi.hoisted(() => ({
  chat: 0,
  payment: 0,
  privateZone: 0,
}));

vi.mock("@/stores/chat/chat-context", async () => {
  const { createElement, useState } = await import("react");
  return {
    ChatProvider({
      characterId,
      children,
    }: {
      characterId: string;
      children: ReactNode;
    }) {
      const [instanceId] = useState(() => ++instances.chat);
      return createElement(
        "div",
        {
          "data-character-id": characterId,
          "data-instance-id": instanceId,
          "data-testid": "chat-provider",
        },
        children,
      );
    },
  };
});

vi.mock("@/stores/payment/payment-context", async () => {
  const { createElement, useState } = await import("react");
  return {
    PaymentProvider({ children }: { children: ReactNode }) {
      const [instanceId] = useState(() => ++instances.payment);
      return createElement(
        "div",
        {
          "data-instance-id": instanceId,
          "data-testid": "payment-provider",
        },
        children,
      );
    },
  };
});

vi.mock("@/stores/private-zone", async () => {
  const { createElement, useState } = await import("react");
  return {
    PrivateZoneProvider({
      characterId,
      children,
    }: {
      characterId: string;
      children: ReactNode;
    }) {
      const [instanceId] = useState(() => ++instances.privateZone);
      return createElement(
        "div",
        {
          "data-character-id": characterId,
          "data-instance-id": instanceId,
          "data-testid": "private-zone-provider",
        },
        children,
      );
    },
  };
});

vi.mock("@/stores/sync/chat-auth-sync", () => ({ ChatAuthSync: () => null }));
vi.mock("@/stores/sync/chat-payment-success-sync", () => ({
  ChatPaymentSuccessSync: () => null,
}));
vi.mock("@/stores/sync/payment-success-sync", () => ({
  PaymentSuccessSync: () => null,
}));

import { ChatRouteProviders } from "@/providers/chat-route-providers";
import { PaymentRouteProvider } from "@/providers/payment-route-provider";
import { PrivateZoneRouteProvider } from "@/providers/private-zone-route-provider";

describe("character actor route providers", () => {
  let container: HTMLDivElement;
  let root: Root;

  beforeEach(() => {
    instances.chat = 0;
    instances.payment = 0;
    instances.privateZone = 0;
    (globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean })
      .IS_REACT_ACT_ENVIRONMENT = true;
    container = document.createElement("div");
    document.body.append(container);
    root = createRoot(container);
  });

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

  it("recreates chat and chat payment actors when the character changes", () => {
    renderChat("elio");
    const firstChatInstance = instanceId("chat-provider");
    const firstPaymentInstance = instanceId("payment-provider");

    renderChat("maya-tan");

    expect(instanceId("chat-provider")).not.toBe(firstChatInstance);
    expect(instanceId("payment-provider")).not.toBe(firstPaymentInstance);
    expect(testNode("chat-provider").dataset.characterId).toBe(
      "maya-tan",
    );
  });

  it("recreates the private-zone actor when the character changes", () => {
    renderPrivateZone("elio");
    const firstInstance = instanceId("private-zone-provider");

    renderPrivateZone("maya-tan");

    expect(instanceId("private-zone-provider")).not.toBe(firstInstance);
    expect(testNode("private-zone-provider").dataset.characterId).toBe(
      "maya-tan",
    );
  });

  it("recreates the route payment actor when its character scope changes", () => {
    renderPayment("elio");
    const firstInstance = instanceId("payment-provider");

    renderPayment("maya-tan");

    expect(instanceId("payment-provider")).not.toBe(firstInstance);
  });

  function renderChat(characterId: string): void {
    act(() => {
      root.render(
        <ChatRouteProviders
          characterId={characterId}
          emptyChatGreeting={`Hello from ${characterId}`}
        >
          <span>chat</span>
        </ChatRouteProviders>,
      );
    });
  }

  function renderPrivateZone(characterId: string): void {
    act(() => {
      root.render(
        <PrivateZoneRouteProvider characterId={characterId}>
          <span>private zone</span>
        </PrivateZoneRouteProvider>,
      );
    });
  }

  function renderPayment(scopeKey: string): void {
    act(() => {
      root.render(
        <PaymentRouteProvider scopeKey={scopeKey}>
          <span>payment</span>
        </PaymentRouteProvider>,
      );
    });
  }

  function testNode(testId: string): HTMLElement {
    const node = container.querySelector<HTMLElement>(
      `[data-testid="${testId}"]`,
    );
    if (!node) throw new Error(`Missing ${testId}`);
    return node;
  }

  function instanceId(testId: string): string | undefined {
    return testNode(testId).dataset.instanceId;
  }
});
