import { act } from "react";
import { renderHook, waitFor } from "@testing-library/react";
import type { ReactNode } from "react";
import { AuthProvider, resetAuthProbeForTests, useAuth } from "@/auth/store";

const STORAGE_KEY = "nxdns_auth_required";

function wrapper({ children }: { children: ReactNode }) {
	return <AuthProvider>{children}</AuthProvider>;
}

function jsonResponse(payload: unknown, status = 200, headers: Record<string, string> = {}): Response {
	return new Response(JSON.stringify(payload), {
		status,
		headers: { "content-type": "application/json", ...headers },
	});
}

beforeEach(() => {
	sessionStorage.clear();
	resetAuthProbeForTests();
});

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

test("mounting with nothing stored probes and settles authRequired true on 401", async () => {
	const fetchMock = vi.fn(async (input: RequestInfo | URL) => {
		if (String(input) === "/api/auth/login") return jsonResponse({ error: "password required" }, 401);
		return jsonResponse({ error: "not stubbed" }, 404);
	});
	vi.stubGlobal("fetch", fetchMock);

	const { result } = renderHook(() => useAuth(), { wrapper });
	expect(result.current.authRequired).toBeNull();

	await waitFor(() => expect(result.current.authRequired).toBe(true));
	expect(sessionStorage.getItem(STORAGE_KEY)).toBe("true");
});

test("mounting with nothing stored probes and settles authRequired false when auth is off", async () => {
	vi.stubGlobal(
		"fetch",
		vi.fn(async () => jsonResponse({ authenticated: true, auth_required: false })),
	);

	const { result } = renderHook(() => useAuth(), { wrapper });
	await waitFor(() => expect(result.current.authRequired).toBe(false));
	expect(sessionStorage.getItem(STORAGE_KEY)).toBe("false");
});

test("a stored value is the fast path: no probe fires on mount", async () => {
	sessionStorage.setItem(STORAGE_KEY, "true");
	const fetchMock = vi.fn();
	vi.stubGlobal("fetch", fetchMock);

	const { result } = renderHook(() => useAuth(), { wrapper });
	expect(result.current.authRequired).toBe(true);

	await act(async () => {});
	expect(fetchMock).not.toHaveBeenCalled();
});

test("logout swallows a 401 from an already-dead session", async () => {
	sessionStorage.setItem(STORAGE_KEY, "true");
	vi.stubGlobal(
		"fetch",
		vi.fn(async () => jsonResponse({ error: "unauthorized" }, 401)),
	);

	const { result } = renderHook(() => useAuth(), { wrapper });
	await expect(result.current.logout()).resolves.toBeUndefined();
});

test("logout rethrows non-401 errors such as 429", async () => {
	sessionStorage.setItem(STORAGE_KEY, "true");
	vi.stubGlobal(
		"fetch",
		vi.fn(async () => jsonResponse({ error: "rate limited" }, 429, { "retry-after": "7" })),
	);

	const { result } = renderHook(() => useAuth(), { wrapper });
	await expect(result.current.logout()).rejects.toMatchObject({
		name: "ApiError",
		status: 429,
		retryAfter: 7,
	});
});
