import { describe, expect, it, vi, beforeEach } from "vitest";
import { render, screen, waitFor } from "@testing-library/react";
import { type ReactElement } from "react";
import { MemoryRouter, Route, Routes, useLocation } from "react-router-dom";
import { FluentProvider, webLightTheme } from "@fluentui/react-components";
import { AuthProvider } from "../AuthProvider";
import * as clientModule from "../../api/client";

function CurrentLocation(): ReactElement {
  const location = useLocation();
  return <div data-testid="location">{location.pathname}</div>;
}

describe("AuthProvider", () => {
  beforeEach(() => {
    sessionStorage.clear();
    vi.restoreAllMocks();
  });

  it("registers unauthorized handler on mount and clears on unmount", () => {
    const setSpy = vi.spyOn(clientModule, "setUnauthorizedHandler");

    const { unmount } = render(
      <FluentProvider theme={webLightTheme}>
        <MemoryRouter initialEntries={["/private"]}>
          <AuthProvider>
            <Routes>
              <Route path="*" element={<CurrentLocation />} />
            </Routes>
          </AuthProvider>
        </MemoryRouter>
      </FluentProvider>,
    );

    expect(setSpy).toHaveBeenCalledWith(expect.any(Function));

    unmount();
    expect(setSpy).toHaveBeenCalledWith(null);
  });

  it("calls handler to clear auth state and redirect to /login", async () => {
    sessionStorage.setItem("bangui_authenticated", "true");

    const capturedHandlers: Array<(() => void) | null> = [];
    vi.spyOn(clientModule, "setUnauthorizedHandler").mockImplementation(
      (handler: (() => void) | null) => {
        capturedHandlers.push(handler);
      },
    );

    render(
      <FluentProvider theme={webLightTheme}>
        <MemoryRouter initialEntries={["/private"]}>
          <AuthProvider>
            <Routes>
              <Route path="*" element={<CurrentLocation />} />
            </Routes>
          </AuthProvider>
        </MemoryRouter>
      </FluentProvider>,
    );

    // Invoke the handler that was captured during render
    const capturedHandler = capturedHandlers[0];
    if (typeof capturedHandler === "function") {
      capturedHandler();
    }

    await waitFor(() => {
      expect(screen.getByTestId("location")).toHaveTextContent("/login");
    });
    expect(sessionStorage.getItem("bangui_authenticated")).toBeNull();
  });

  it("listens to storage events from other tabs and logs out", async () => {
    sessionStorage.setItem("bangui_authenticated", "true");

    render(
      <FluentProvider theme={webLightTheme}>
        <MemoryRouter initialEntries={["/private"]}>
          <AuthProvider>
            <Routes>
              <Route path="*" element={<CurrentLocation />} />
            </Routes>
          </AuthProvider>
        </MemoryRouter>
      </FluentProvider>,
    );

    // Simulate another tab clearing the storage
    const storageEvent = new StorageEvent("storage", {
      key: "bangui_authenticated",
      newValue: null,
      storageArea: sessionStorage,
    });
    window.dispatchEvent(storageEvent);

    await waitFor(() => {
      expect(screen.getByTestId("location")).toHaveTextContent("/login");
    });
  });

  it("creates BroadcastChannel for cross-tab sync", () => {
    const mockBroadcastChannel = vi.fn().mockImplementation(() => ({
      onmessage: null,
      postMessage: vi.fn(),
      close: vi.fn(),
    }));

    global.BroadcastChannel = mockBroadcastChannel as any;

    render(
      <FluentProvider theme={webLightTheme}>
        <MemoryRouter initialEntries={["/private"]}>
          <AuthProvider>
            <Routes>
              <Route path="*" element={<CurrentLocation />} />
            </Routes>
          </AuthProvider>
        </MemoryRouter>
      </FluentProvider>,
    );

    // Verify BroadcastChannel was created with the correct name
    expect(mockBroadcastChannel).toHaveBeenCalledWith("bangui_auth");
  });

  it("broadcasts logout message on logout call", () => {
    const mockPostMessage = vi.fn();
    const mockClose = vi.fn();

    global.BroadcastChannel = vi.fn().mockImplementation(() => ({
      onmessage: null,
      postMessage: mockPostMessage,
      close: mockClose,
    })) as any;

    render(
      <FluentProvider theme={webLightTheme}>
        <MemoryRouter initialEntries={["/private"]}>
          <AuthProvider>
            <Routes>
              <Route path="*" element={<CurrentLocation />} />
            </Routes>
          </AuthProvider>
        </MemoryRouter>
      </FluentProvider>,
    );

    // Verify the BroadcastChannel was created
    expect(global.BroadcastChannel).toHaveBeenCalledWith("bangui_auth");
  });
});
