// @vitest-environment jsdom
import "@testing-library/jest-dom/vitest";

import { cleanup, render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { afterEach, describe, expect, it, vi } from "vitest";

vi.mock("../../adapters/bestiary-cache.js", () => ({
	getCachedSources: vi.fn(),
	clearSource: vi.fn(),
	clearAll: vi.fn(),
}));

// Mock the context module
vi.mock("../../contexts/bestiary-context.js", () => ({
	useBestiaryContext: vi.fn(),
}));

import * as bestiaryCache from "../../adapters/bestiary-cache.js";
import { useBestiaryContext } from "../../contexts/bestiary-context.js";
import { SourceManager } from "../source-manager.js";

const mockGetCachedSources = vi.mocked(bestiaryCache.getCachedSources);
const mockClearSource = vi.mocked(bestiaryCache.clearSource);
const mockClearAll = vi.mocked(bestiaryCache.clearAll);
const mockUseBestiaryContext = vi.mocked(useBestiaryContext);

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

function setupMockContext() {
	const refreshCache = vi.fn().mockResolvedValue(undefined);
	mockUseBestiaryContext.mockReturnValue({
		refreshCache,
		search: vi.fn().mockReturnValue([]),
		getCreature: vi.fn(),
		isLoaded: true,
		isSourceCached: vi.fn().mockResolvedValue(false),
		fetchAndCacheSource: vi.fn(),
		uploadAndCacheSource: vi.fn(),
	} as ReturnType<typeof useBestiaryContext>);
	return { refreshCache };
}

describe("SourceManager", () => {
	it("shows 'No cached sources' empty state when no sources", async () => {
		setupMockContext();
		mockGetCachedSources.mockResolvedValue([]);
		render(<SourceManager />);
		await waitFor(() => {
			expect(screen.getByText("No cached sources")).toBeInTheDocument();
		});
	});

	it("lists cached sources with display name and creature count", async () => {
		setupMockContext();
		mockGetCachedSources.mockResolvedValue([
			{
				sourceCode: "mm",
				displayName: "Monster Manual",
				creatureCount: 300,
				cachedAt: Date.now(),
			},
			{
				sourceCode: "vgm",
				displayName: "Volo's Guide",
				creatureCount: 100,
				cachedAt: Date.now(),
			},
		]);
		render(<SourceManager />);
		await waitFor(() => {
			expect(screen.getByText("Monster Manual")).toBeInTheDocument();
		});
		expect(screen.getByText("300 creatures")).toBeInTheDocument();
		expect(screen.getByText("Volo's Guide")).toBeInTheDocument();
		expect(screen.getByText("100 creatures")).toBeInTheDocument();
	});

	it("Clear All button calls cache clear and refreshCache", async () => {
		const user = userEvent.setup();
		const { refreshCache } = setupMockContext();
		mockGetCachedSources
			.mockResolvedValueOnce([
				{
					sourceCode: "mm",
					displayName: "Monster Manual",
					creatureCount: 300,
					cachedAt: Date.now(),
				},
			])
			.mockResolvedValue([]);
		mockClearAll.mockResolvedValue(undefined);
		render(<SourceManager />);

		await waitFor(() => {
			expect(screen.getByText("Monster Manual")).toBeInTheDocument();
		});

		await user.click(screen.getByRole("button", { name: "Clear All" }));
		await waitFor(() => {
			expect(mockClearAll).toHaveBeenCalled();
		});
		expect(refreshCache).toHaveBeenCalled();
	});

	it("individual source delete button calls clear for that source", async () => {
		const user = userEvent.setup();
		const { refreshCache } = setupMockContext();
		mockGetCachedSources
			.mockResolvedValueOnce([
				{
					sourceCode: "mm",
					displayName: "Monster Manual",
					creatureCount: 300,
					cachedAt: Date.now(),
				},
				{
					sourceCode: "vgm",
					displayName: "Volo's Guide",
					creatureCount: 100,
					cachedAt: Date.now(),
				},
			])
			.mockResolvedValue([
				{
					sourceCode: "vgm",
					displayName: "Volo's Guide",
					creatureCount: 100,
					cachedAt: Date.now(),
				},
			]);
		mockClearSource.mockResolvedValue(undefined);

		render(<SourceManager />);
		await waitFor(() => {
			expect(screen.getByText("Monster Manual")).toBeInTheDocument();
		});

		await user.click(
			screen.getByRole("button", { name: "Remove Monster Manual" }),
		);
		await waitFor(() => {
			expect(mockClearSource).toHaveBeenCalledWith("mm");
		});
		expect(refreshCache).toHaveBeenCalled();
	});
});
