// @vitest-environment jsdom
import { act, renderHook } from "@testing-library/react";
import type { ReactNode } from "react";
import { beforeAll, describe, expect, it, vi } from "vitest";
import { createTestAdapters } from "../../__tests__/adapters/in-memory-adapters.js";
import { AllProviders } from "../../__tests__/test-providers.js";
import { useBulkImport } from "../use-bulk-import.js";

beforeAll(() => {
	Object.defineProperty(globalThis, "matchMedia", {
		writable: true,
		value: vi.fn().mockImplementation((query: string) => ({
			matches: false,
			media: query,
			onchange: null,
			addListener: vi.fn(),
			removeListener: vi.fn(),
			addEventListener: vi.fn(),
			removeEventListener: vi.fn(),
			dispatchEvent: vi.fn(),
		})),
	});
});

const adapters = createTestAdapters();
adapters.bestiaryIndex = {
	...adapters.bestiaryIndex,
	getAllSourceCodes: () => ["MM", "VGM", "XGE"],
	getDefaultFetchUrl: (code: string, baseUrl?: string) =>
		`${baseUrl}${code}.json`,
};

function wrapper({ children }: { children: ReactNode }) {
	return <AllProviders adapters={adapters}>{children}</AllProviders>;
}

/** Flush microtasks so the internal async IIFE inside startImport settles. */
function flushMicrotasks(): Promise<void> {
	return new Promise((resolve) => {
		setTimeout(resolve, 0);
	});
}

describe("useBulkImport", () => {
	it("starts in idle state with all counters at 0", () => {
		const { result } = renderHook(() => useBulkImport(), { wrapper });
		expect(result.current.state).toEqual({
			status: "idle",
			total: 0,
			completed: 0,
			failed: 0,
		});
	});

	it("reset returns to idle state", async () => {
		const { result } = renderHook(() => useBulkImport(), { wrapper });

		const isSourceCached = vi.fn().mockResolvedValue(true);
		const fetchAndCacheSource = vi.fn();
		const refreshCache = vi.fn();

		await act(async () => {
			result.current.startImport(
				"https://example.com/",
				fetchAndCacheSource,
				isSourceCached,
				refreshCache,
			);
			await flushMicrotasks();
		});

		act(() => result.current.reset());
		expect(result.current.state.status).toBe("idle");
	});

	it("goes straight to complete when all sources are cached", async () => {
		const { result } = renderHook(() => useBulkImport(), { wrapper });

		const isSourceCached = vi.fn().mockResolvedValue(true);
		const fetchAndCacheSource = vi.fn();
		const refreshCache = vi.fn();

		await act(async () => {
			result.current.startImport(
				"https://example.com/",
				fetchAndCacheSource,
				isSourceCached,
				refreshCache,
			);
			await flushMicrotasks();
		});

		expect(result.current.state.status).toBe("complete");
		expect(result.current.state.completed).toBe(3);
		expect(fetchAndCacheSource).not.toHaveBeenCalled();
	});

	it("fetches uncached sources and completes", async () => {
		const { result } = renderHook(() => useBulkImport(), { wrapper });

		const isSourceCached = vi.fn().mockResolvedValue(false);
		const fetchAndCacheSource = vi.fn().mockResolvedValue(undefined);
		const refreshCache = vi.fn().mockResolvedValue(undefined);

		await act(async () => {
			result.current.startImport(
				"https://example.com/",
				fetchAndCacheSource,
				isSourceCached,
				refreshCache,
			);
			await flushMicrotasks();
		});

		expect(result.current.state.status).toBe("complete");
		expect(result.current.state.completed).toBe(3);
		expect(result.current.state.failed).toBe(0);
		expect(fetchAndCacheSource).toHaveBeenCalledTimes(3);
		expect(refreshCache).toHaveBeenCalled();
	});

	it("reports partial-failure when some sources fail", async () => {
		const { result } = renderHook(() => useBulkImport(), { wrapper });

		const isSourceCached = vi.fn().mockResolvedValue(false);
		const fetchAndCacheSource = vi
			.fn()
			.mockResolvedValueOnce(undefined)
			.mockRejectedValueOnce(new Error("fail"))
			.mockResolvedValueOnce(undefined);
		const refreshCache = vi.fn().mockResolvedValue(undefined);

		await act(async () => {
			result.current.startImport(
				"https://example.com/",
				fetchAndCacheSource,
				isSourceCached,
				refreshCache,
			);
			await flushMicrotasks();
		});

		expect(result.current.state.status).toBe("partial-failure");
		expect(result.current.state.completed).toBe(2);
		expect(result.current.state.failed).toBe(1);
		expect(refreshCache).toHaveBeenCalled();
	});

	it("calls refreshCache after all batches complete", async () => {
		const { result } = renderHook(() => useBulkImport(), { wrapper });

		const isSourceCached = vi.fn().mockResolvedValue(false);
		const fetchAndCacheSource = vi.fn().mockResolvedValue(undefined);
		const refreshCache = vi.fn().mockResolvedValue(undefined);

		await act(async () => {
			result.current.startImport(
				"https://example.com/",
				fetchAndCacheSource,
				isSourceCached,
				refreshCache,
			);
			await flushMicrotasks();
		});

		expect(refreshCache).toHaveBeenCalledOnce();
	});
});
