/**
 * L1 — the DI provider resolves real vs mock gateways from `VITE_USE_MOCK`,
 * and `useGateways` exposes the injected set (and guards against misuse).
 */
import { describe, it, expect, afterEach, vi } from "vitest";
import { render, screen, renderHook } from "@testing-library/react";

import { MockSystemGateway } from "@/adapters/mock";
import { HttpSystemGateway } from "@/adapters/http/streamGateways";
import { TauriSystemGateway } from "@/adapters";
import {
  DIProvider,
  useGateways,
  resolveGateways,
  resolveTransport,
  shouldUseMock,
  shouldUseHttp,
} from "./di";
import { transportFromEnv } from "./transport";

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

describe("shouldUseMock / resolveGateways", () => {
  it("returns mock gateways when VITE_USE_MOCK=1", () => {
    vi.stubEnv("VITE_USE_MOCK", "1");
    expect(shouldUseMock()).toBe(true);
    expect(resolveGateways().system).toBeInstanceOf(MockSystemGateway);
  });

  it("returns mock gateways when VITE_USE_MOCK=true", () => {
    vi.stubEnv("VITE_USE_MOCK", "true");
    expect(shouldUseMock()).toBe(true);
  });

  it("returns real (non-mock) gateways otherwise", () => {
    vi.stubEnv("VITE_USE_MOCK", "");
    vi.stubEnv("VITE_TRANSPORT", "");
    expect(shouldUseMock()).toBe(false);
    // Real adapter is not the mock implementation.
    expect(resolveGateways().system).not.toBeInstanceOf(MockSystemGateway);
  });
});

describe("resolveTransport / web (HTTP+WS) selection (ticket #13, F1)", () => {
  it("selects the HTTP transport when VITE_TRANSPORT=http", () => {
    vi.stubEnv("VITE_USE_MOCK", "");
    vi.stubEnv("VITE_TRANSPORT", "http");
    expect(shouldUseHttp()).toBe(true);
    expect(resolveTransport()).toBe("http");
    expect(resolveGateways().system).toBeInstanceOf(HttpSystemGateway);
  });

  it("keeps Tauri as the default when no transport env is set", () => {
    vi.stubEnv("VITE_USE_MOCK", "");
    vi.stubEnv("VITE_TRANSPORT", "");
    expect(resolveTransport()).toBe("tauri");
    expect(resolveGateways().system).toBeInstanceOf(TauriSystemGateway);
  });

  it("lets mock win over the web transport", () => {
    vi.stubEnv("VITE_USE_MOCK", "1");
    vi.stubEnv("VITE_TRANSPORT", "http");
    expect(resolveTransport()).toBe("mock");
    expect(resolveGateways().system).toBeInstanceOf(MockSystemGateway);
  });
});

describe("transportFromEnv (ticket #74, F1)", () => {
  // The build config derives `__IDEA_TRANSPORT__` from this same predicate, so
  // pinning it here pins both the runtime transport and the build constant.
  it("selects http only for the exact opt-in value", () => {
    expect(transportFromEnv("http")).toBe("http");
  });

  it.each([undefined, "", "tauri", "HTTP", "http ", "web"])(
    "falls back to tauri for %o",
    (value) => {
      expect(transportFromEnv(value)).toBe("tauri");
    },
  );

  it("agrees with shouldUseHttp for the value the build reads", () => {
    vi.stubEnv("VITE_TRANSPORT", "http");
    expect(shouldUseHttp()).toBe(transportFromEnv("http") === "http");
    vi.stubEnv("VITE_TRANSPORT", "");
    expect(shouldUseHttp()).toBe(transportFromEnv("") === "http");
  });
});

describe("DIProvider / useGateways", () => {
  it("provides explicit gateways to consumers", () => {
    const gateways = resolveGatewaysMock();
    const { result } = renderHook(() => useGateways(), {
      wrapper: ({ children }) => (
        <DIProvider gateways={gateways}>{children}</DIProvider>
      ),
    });
    expect(result.current).toBe(gateways);
  });

  it("renders children inside the provider", () => {
    render(
      <DIProvider gateways={resolveGatewaysMock()}>
        <span>ok</span>
      </DIProvider>,
    );
    expect(screen.getByText("ok")).toBeTruthy();
  });

  it("throws when useGateways is used outside a provider", () => {
    expect(() => renderHook(() => useGateways())).toThrow(
      /must be used within a <DIProvider>/,
    );
  });
});

function resolveGatewaysMock() {
  vi.stubEnv("VITE_USE_MOCK", "1");
  const g = resolveGateways();
  vi.unstubAllEnvs();
  return g;
}
