import React, { useEffect, useState } from "react";
import "@testing-library/jest-dom";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";

import { clearAuthClientState, setAuthUserKey } from "./auth";
import JobTrackerThemeProvider from "./ThemeProvider";
import { THEME_BOOTSTRAP_SCRIPT } from "./themeBootstrap";
import {
  getThemeModePref,
  resolveEffectiveThemeMode,
  setThemeModePref,
  subscribeToThemePreferenceChanges,
} from "./themePrefs";

function setSystemDark(matches: boolean) {
  Object.defineProperty(window, "matchMedia", {
    configurable: true,
    value: jest.fn().mockImplementation(() => ({
      matches,
      media: "(prefers-color-scheme: dark)",
      onchange: null,
      addEventListener: jest.fn(),
      removeEventListener: jest.fn(),
      addListener: jest.fn(),
      removeListener: jest.fn(),
      dispatchEvent: jest.fn(),
    })),
  });
}

describe("deterministic theme state", () => {
  beforeEach(() => {
    window.localStorage.clear();
    document.documentElement.removeAttribute("data-color-scheme");
    document.documentElement.style.colorScheme = "";
    setSystemDark(false);
  });

  it("uses user preference, then anonymous preference, then the documented System default", () => {
    expect(getThemeModePref()).toBe("system");
    setThemeModePref("light");
    expect(getThemeModePref()).toBe("light");

    setAuthUserKey("user-a", false);
    expect(getThemeModePref()).toBe("light");
    setThemeModePref("dark");
    expect(getThemeModePref()).toBe("dark");

    setAuthUserKey("user-b", false);
    expect(getThemeModePref()).toBe("light");
    window.localStorage.removeItem("themeMode:anon");
    expect(getThemeModePref()).toBe("system");
  });

  it("switches to the correct scoped preference on login and logout without a refresh", () => {
    window.localStorage.setItem("themeMode:anon", "light");
    window.localStorage.setItem("themeMode:user-a", "dark");
    const observed: string[] = [];
    const unsubscribe = subscribeToThemePreferenceChanges(() => observed.push(getThemeModePref()));

    setAuthUserKey("user-a", false);
    clearAuthClientState(false);

    expect(observed).toEqual(["dark", "light"]);
    unsubscribe();
  });

  it("synchronizes relevant cross-tab storage changes without writing them back", () => {
    setAuthUserKey("user-a", false);
    const sync = jest.fn();
    const unsubscribe = subscribeToThemePreferenceChanges(sync);

    window.dispatchEvent(new StorageEvent("storage", { key: "themeMode:user-a", newValue: "dark" }));
    window.dispatchEvent(new StorageEvent("storage", { key: "unrelated", newValue: "value" }));
    window.dispatchEvent(new StorageEvent("storage", { key: "authUserKey", newValue: "user-b" }));

    expect(sync).toHaveBeenCalledTimes(2);
    unsubscribe();
  });

  it("uses system preference only for System mode", () => {
    expect(resolveEffectiveThemeMode("light", true)).toBe("light");
    expect(resolveEffectiveThemeMode("dark", false)).toBe("dark");
    expect(resolveEffectiveThemeMode("system", true)).toBe("dark");
    expect(resolveEffectiveThemeMode("system", false)).toBe("light");
  });

  it("applies the saved preference before the client application renders", () => {
    window.localStorage.setItem("themeMode:anon", "light");
    setSystemDark(true);

    new Function(THEME_BOOTSTRAP_SCRIPT)();

    expect(document.documentElement).toHaveAttribute("data-color-scheme", "light");
    expect(document.documentElement.style.colorScheme).toBe("light");
  });

  it("changes color scheme without remounting application state", async () => {
    let mounts = 0;
    function StatefulProbe() {
      const [count, setCount] = useState(0);
      useEffect(() => { mounts += 1; }, []);
      return <button onClick={() => setCount((value) => value + 1)}>Count {count}</button>;
    }

    const view = render(
      <JobTrackerThemeProvider preference="light" systemPrefersDark>
        <StatefulProbe />
      </JobTrackerThemeProvider>,
    );
    fireEvent.click(screen.getByRole("button", { name: "Count 0" }));

    view.rerender(
      <JobTrackerThemeProvider preference="dark" systemPrefersDark={false}>
        <StatefulProbe />
      </JobTrackerThemeProvider>,
    );

    await waitFor(() => expect(document.documentElement).toHaveAttribute("data-color-scheme", "dark"));
    expect(screen.getByRole("button", { name: "Count 1" })).toBeInTheDocument();
    expect(mounts).toBe(1);
  });
});
