import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { type GeminiConfig, loadConfig, resetConfigCache } from "./config";

describe("Config loader", () => {
	beforeEach(() => {
		resetConfigCache();
		vi.restoreAllMocks();
	});

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

	it("loadConfig returns GeminiConfig with all required fields", async () => {
		const mockConfig: GeminiConfig = {
			geminiApiKey: "test-key-123",
			geminiModel: "gemini-2.5-flash",
			imageModel: "gemini-3.1-flash-preview-image",
		};
		vi.spyOn(globalThis, "fetch").mockResolvedValueOnce(
			new Response(JSON.stringify(mockConfig), { status: 200 }),
		);

		const result = await loadConfig();
		expect(result).toEqual(mockConfig);
		expect(result?.geminiApiKey).toBe("test-key-123");
		expect(result?.geminiModel).toBe("gemini-2.5-flash");
		expect(result?.imageModel).toBe("gemini-3.1-flash-preview-image");
	});

	it("loadConfig returns null when fetch fails (404)", async () => {
		vi.spyOn(globalThis, "fetch").mockResolvedValueOnce(
			new Response("Not Found", { status: 404 }),
		);

		const result = await loadConfig();
		expect(result).toBeNull();
	});

	it("loadConfig returns null when JSON is malformed", async () => {
		vi.spyOn(globalThis, "fetch").mockResolvedValueOnce(
			new Response("not-json{{{", { status: 200 }),
		);

		const result = await loadConfig();
		expect(result).toBeNull();
	});

	it("loadConfig returns null when required fields are missing", async () => {
		vi.spyOn(globalThis, "fetch").mockResolvedValueOnce(
			new Response(JSON.stringify({ geminiApiKey: "key" }), { status: 200 }),
		);

		const result = await loadConfig();
		expect(result).toBeNull();
	});

	it("loadConfig caches result after first successful call", async () => {
		const mockConfig: GeminiConfig = {
			geminiApiKey: "test-key",
			geminiModel: "gemini-2.5-flash",
			imageModel: "gemini-3.1-flash-preview-image",
		};
		const fetchSpy = vi
			.spyOn(globalThis, "fetch")
			.mockResolvedValueOnce(
				new Response(JSON.stringify(mockConfig), { status: 200 }),
			);

		const first = await loadConfig();
		const second = await loadConfig();

		expect(first).toEqual(mockConfig);
		expect(second).toEqual(mockConfig);
		expect(fetchSpy).toHaveBeenCalledTimes(1);
	});

	it("loadConfig returns null when fetch throws network error", async () => {
		vi.spyOn(globalThis, "fetch").mockRejectedValueOnce(
			new Error("Network error"),
		);

		const result = await loadConfig();
		expect(result).toBeNull();
	});
});
