/* @vitest-environment jsdom */

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

import { TIP_SUPPORT_PROMPTS } from "@/lib/tip/tip_support_prompts";

import { useTipSupportPrompt } from "../use-tip-support-prompt";

describe("useTipSupportPrompt", () => {
  let callbacks: FrameRequestCallback[];
  let container: HTMLDivElement;
  let root: Root;

  beforeEach(() => {
    (globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean })
      .IS_REACT_ACT_ENVIRONMENT = true;
    callbacks = [];
    vi.stubGlobal("requestAnimationFrame", (callback: FrameRequestCallback) => {
      callbacks.push(callback);
      return callbacks.length;
    });
    vi.stubGlobal("cancelAnimationFrame", vi.fn());
    sessionStorage.clear();
    container = document.createElement("div");
    document.body.append(container);
    root = createRoot(container);
  });

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

  it("selects once per mount and stays stable across rerenders", () => {
    vi.spyOn(Math, "random").mockReturnValue(0.5);
    renderPrompt("first");
    expect(readPrompt()).toBe(TIP_SUPPORT_PROMPTS[0]);

    flushNextFrame();
    const selectedPrompt = readPrompt();
    expect(selectedPrompt).toBe(TIP_SUPPORT_PROMPTS[15]);

    renderPrompt("rerender");
    expect(readPrompt()).toBe(selectedPrompt);
    expect(Math.random).toHaveBeenCalledOnce();
  });

  it("avoids the previous prompt when the component remounts", () => {
    vi.spyOn(Math, "random").mockReturnValue(0);
    renderPrompt("first");
    flushNextFrame();
    expect(readPrompt()).toBe(TIP_SUPPORT_PROMPTS[0]);

    renderPrompt("second", "second-mount");
    flushNextFrame();
    expect(readPrompt()).toBe(TIP_SUPPORT_PROMPTS[1]);
  });

  it("continues rendering when Session Storage is unavailable", () => {
    vi.spyOn(Storage.prototype, "getItem").mockImplementation(() => {
      throw new DOMException("Storage blocked", "SecurityError");
    });
    vi.spyOn(Storage.prototype, "setItem").mockImplementation(() => {
      throw new DOMException("Storage blocked", "SecurityError");
    });
    vi.spyOn(Math, "random").mockReturnValue(0.25);

    renderPrompt("storage-error");
    expect(() => flushNextFrame()).not.toThrow();
    expect(TIP_SUPPORT_PROMPTS).toContain(readPrompt());
  });

  function renderPrompt(marker: string, key = "stable-mount"): void {
    act(() => root.render(<PromptProbe key={key} marker={marker} />));
  }

  function flushNextFrame(): void {
    const callback = callbacks.shift();
    if (!callback) throw new Error("Missing animation frame callback");
    act(() => callback(performance.now()));
  }

  function readPrompt(): string {
    return container.querySelector("output")?.textContent ?? "";
  }
});

function PromptProbe({ marker }: { marker: string }) {
  const state = useTipSupportPrompt();
  return (
    <output data-marker={marker} data-ready={state.isReady}>
      {state.prompt}
    </output>
  );
}
