/* @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";

import { ViewportCssVarsProvider } from "../viewport-css-vars-provider";

vi.mock("@/utils/browser-detect", () => ({
  BrowserDetector: {
    isFacebookInAppBrowser: vi.fn(() => true),
  },
}));

vi.mock("@/utils/platform-detect", () => ({
  PlatformDetector: {
    isIOS: vi.fn(() => true),
  },
}));

class MockVisualViewport extends EventTarget {
  height = 760;
  offsetLeft = 0;
  offsetTop = 0;
  scale = 1;
  width = 390;
}

function Harness({ children }: { children: ReactNode }) {
  return <ViewportCssVarsProvider>{children}</ViewportCssVarsProvider>;
}

describe("ViewportCssVarsProvider", () => {
  let container: HTMLDivElement;
  let root: Root;
  let visualViewport: MockVisualViewport;

  beforeEach(() => {
    vi.useFakeTimers();
    (globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean })
      .IS_REACT_ACT_ENVIRONMENT = true;
    visualViewport = new MockVisualViewport();
    Object.defineProperty(window, "innerHeight", {
      configurable: true,
      value: 800,
    });
    Object.defineProperty(window, "innerWidth", {
      configurable: true,
      value: 390,
    });
    Object.defineProperty(window, "visualViewport", {
      configurable: true,
      value: visualViewport,
    });
    vi.stubGlobal(
      "requestAnimationFrame",
      vi.fn((callback: FrameRequestCallback) =>
        window.setTimeout(() => callback(0), 0),
      ),
    );
    vi.stubGlobal(
      "cancelAnimationFrame",
      vi.fn((id: number) => window.clearTimeout(id)),
    );
    container = document.createElement("div");
    document.body.append(container);
    root = createRoot(container);
  });

  afterEach(() => {
    act(() => root.unmount());
    container.remove();
    document.documentElement.removeAttribute("style");
    delete document.documentElement.dataset.viewportHeight;
    delete document.documentElement.dataset.viewportWidth;
    vi.useRealTimers();
    vi.restoreAllMocks();
    vi.unstubAllGlobals();
  });

  it("keeps stable viewport variables while the iOS Facebook keyboard is open", () => {
    act(() => {
      root.render(
        <Harness>
          <input aria-label="message" />
        </Harness>,
      );
    });
    act(() => {
      vi.runOnlyPendingTimers();
    });
    expect(getRootVar("--app-viewport-height")).toBe("800px");
    expect(getRootVar("--app-visible-height")).toBe("760px");

    const input = container.querySelector("input");
    if (!input) throw new Error("input was not rendered");
    input.focus();
    visualViewport.height = 460;
    act(() => {
      visualViewport.dispatchEvent(new Event("resize"));
      vi.runOnlyPendingTimers();
    });

    expect(getRootVar("--app-viewport-height")).toBe("800px");
    expect(getRootVar("--app-visible-height")).toBe("760px");
  });
});

function getRootVar(name: string): string {
  return document.documentElement.style.getPropertyValue(name);
}
