/**
 * Tests for Provider Composition Builder
 *
 * Validates that the provider composition builder enforces correct ordering
 * at compile-time through TypeScript's discriminated union types.
 */

import { describe, it, expect, beforeEach, vi, afterEach } from "vitest";
import { render, screen } from "@testing-library/react";
import { webLightTheme } from "@fluentui/react-components";
import { createProviderComposition } from "../providerComposition";
import { useThemeMode } from "../ThemeProvider";
import { useNotification } from "../../services/notificationService";

// ---------------------------------------------------------------------------
// Test Suite: Provider Composition Builder
// ---------------------------------------------------------------------------

describe("ProviderCompositionBuilder", () => {
  beforeEach(() => {
    sessionStorage.clear();
    localStorage.clear();
    vi.clearAllMocks();

    // Mock window.matchMedia for theme preference
    Object.defineProperty(window, "matchMedia", {
      configurable: true,
      writable: true,
      value: vi.fn().mockReturnValue({
        matches: false,
        addEventListener: vi.fn(),
        removeEventListener: vi.fn(),
      }),
    });
  });

  afterEach(() => {
    vi.clearAllMocks();
  });

  // -----------------------------------------------------------------------
  // Test 1: Builder creates valid provider tree without TypeScript errors
  // -----------------------------------------------------------------------

  it("creates a valid provider tree with complete composition", () => {
    function TestComponent(): React.JSX.Element {
      const { colorMode } = useThemeMode();
      return <div data-testid="test-output">{colorMode}</div>;
    }

    const composition = createProviderComposition()
      .withTheme({ children: <TestComponent /> })
      .withFluent(webLightTheme)
      .withNotification()
      .withErrorBoundary()
      .withBrowserRouter()
      .withNavigationCancellation()
      .withAuth()
      .build(<div>Routes go here</div>);

    const { container } = render(composition);
    expect(container).toBeTruthy();
  });

  // -----------------------------------------------------------------------
  // Test 2: Builder enforces ThemeProvider must be first
  // -----------------------------------------------------------------------

  it("requires withTheme to be called first", () => {
    // This test is primarily a compile-time check
    // If TypeScript allows calling withFluent without withTheme first,
    // this test would fail (but TypeScript would prevent it)

    const builder = createProviderComposition();
    // @ts-expect-error withFluent should not be available on initial builder
    builder.withFluent(webLightTheme);
  });

  // -----------------------------------------------------------------------
  // Test 3: Builder enforces provider ordering
  // -----------------------------------------------------------------------

  it("ensures FluentProvider comes after ThemeProvider", () => {
    const builder = createProviderComposition().withTheme({ children: <div /> });
    // withFluent should now be available after withTheme
    const nextBuilder = builder.withFluent(webLightTheme);
    expect(nextBuilder).toBeTruthy();
  });

  it("ensures NotificationProvider comes after FluentProvider", () => {
    const builder = createProviderComposition()
      .withTheme({ children: <div /> })
      .withFluent(webLightTheme);
    // withNotification should now be available
    const nextBuilder = builder.withNotification();
    expect(nextBuilder).toBeTruthy();
  });

  // -----------------------------------------------------------------------
  // Test 4: Builder provides typed access to state
  // -----------------------------------------------------------------------

  it("allows building with intermediate providers (no Timezone)", () => {
    function TestComponent(): React.JSX.Element {
      const { colorMode } = useThemeMode();
      const notification = useNotification();

      return (
        <div>
          <div data-testid="theme">{colorMode}</div>
          <button
            type="button"
            data-testid="notify-btn"
            onClick={() => notification.success("Test")}
          >
            Notify
          </button>
        </div>
      );
    }

    const tree = createProviderComposition()
      .withTheme({ children: <TestComponent /> })
      .withFluent(webLightTheme)
      .withNotification()
      .withErrorBoundary()
      .withBrowserRouter()
      .withNavigationCancellation()
      .withAuth()
      .build(<div data-testid="routes">Routes</div>);

    render(tree);
    expect(screen.getByTestId("routes")).toBeInTheDocument();
  });

  // -----------------------------------------------------------------------
  // Test 5: Builder supports optional TimezoneProvider
  // -----------------------------------------------------------------------

  it("allows adding TimezoneProvider as the final step", () => {
    function ProtectedContent(): React.JSX.Element {
      return <div data-testid="protected">Protected</div>;
    }

    const tree = createProviderComposition()
      .withTheme({ children: <ProtectedContent /> })
      .withFluent(webLightTheme)
      .withNotification()
      .withErrorBoundary()
      .withBrowserRouter()
      .withNavigationCancellation()
      .withAuth()
      .withTimezone({ children: <div data-testid="timezone-content">Content</div> })
      .buildWithTimezone();

    const { container } = render(tree);
    expect(container).toBeTruthy();
  });

  // -----------------------------------------------------------------------
  // Test 6: Verify all providers are included in final tree
  // -----------------------------------------------------------------------

  it("includes all providers in correct nesting order", () => {
    function TestComponent(): React.JSX.Element {
      const notification = useNotification();

      // NotificationProvider should be accessible
      const canUseNotification = notification !== undefined;

      return (
        <div>
          <div data-testid="notification-works">{canUseNotification ? "yes" : "no"}</div>
        </div>
      );
    }

    // The builder composition creates the full provider tree
    const tree = createProviderComposition()
      .withTheme({ 
        children: <TestComponent />
      })
      .withFluent(webLightTheme)
      .withNotification()
      .withErrorBoundary()
      .withBrowserRouter()
      .withNavigationCancellation()
      .withAuth()
      .build(<div>Routes go here</div>);

    // Render the composition and verify at least one provider works
    const { container } = render(tree);
    expect(container).toBeTruthy();
    expect(container.querySelector('[class*="fui"]')).toBeTruthy(); // FluentProvider renders
  });

  // -----------------------------------------------------------------------
  // Test 7: Type safety prevents incorrect call sequences
  // -----------------------------------------------------------------------

  it("prevents calling methods out of order (TypeScript compile-time check)", () => {
    const builder = createProviderComposition()
      .withTheme({ children: <div /> })
      .withFluent(webLightTheme);

    // This would be a compile error in real code:
    // @ts-expect-error withTheme should not be available after withFluent
    builder.withTheme({ children: <div /> });

    // This should work:
    const nextBuilder = builder.withNotification();
    expect(nextBuilder).toBeTruthy();
  });

  // -----------------------------------------------------------------------
  // Test 8: Builder state is immutable
  // -----------------------------------------------------------------------

  it("creates new builder instances without mutating previous state", () => {
    const builder1 = createProviderComposition();
    const builder2 = builder1.withTheme({ children: <div /> });
    const builder3 = builder2.withFluent(webLightTheme);

    // builder1 should still be in initial state
    // @ts-expect-error builder1 should not have withFluent available
    expect(() => builder1.withFluent(webLightTheme)).toBeDefined();

    // builder2 should be in state 1
    const builder2_next = builder2.withFluent(webLightTheme);
    expect(builder2_next).toBeTruthy();

    // builder3 should be in state 2
    const builder3_next = builder3.withNotification();
    expect(builder3_next).toBeTruthy();
  });

  // -----------------------------------------------------------------------
  // Test 9: Verify builder doesn't allow skipping intermediate steps
  // -----------------------------------------------------------------------

  it("prevents skipping providers in the sequence", () => {
    const builder = createProviderComposition().withTheme({ children: <div /> });

    // Cannot call withNotification without withFluent first
    // @ts-expect-error withNotification should not be available yet
    expect(() => builder.withNotification()).toBeDefined();

    // Must call withFluent first
    const nextBuilder = builder.withFluent(webLightTheme);
    expect(nextBuilder).toBeTruthy();
  });

  // -----------------------------------------------------------------------
  // Test 10: Integration — Full composition with all steps
  // -----------------------------------------------------------------------

  it("supports full composition path with all providers including Timezone", () => {
    function FullContent(): React.JSX.Element {
      const { colorMode } = useThemeMode();
      const notification = useNotification();

      return (
        <div>
          <div data-testid="full-theme">{colorMode}</div>
          <button
            type="button"
            data-testid="full-notify"
            onClick={() => notification.success("Test")}
          >
            Test
          </button>
        </div>
      );
    }

    const composition = createProviderComposition()
      .withTheme({ children: <FullContent /> })
      .withFluent(webLightTheme)
      .withNotification()
      .withErrorBoundary()
      .withBrowserRouter()
      .withNavigationCancellation()
      .withAuth()
      .withTimezone({ children: <div data-testid="tz-content">TZ Content</div> })
      .buildWithTimezone();

    const { container } = render(composition);
    expect(container).toBeTruthy();
  });
});
