// @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 type { ReactNode } from "react";
import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
import { createTestAdapters } from "../../__tests__/adapters/in-memory-adapters.js";
import { AllProviders } from "../../__tests__/test-providers.js";
import type { CachedSourceInfo } from "../../adapters/ports.js";
import { SourceManager } from "../source-manager.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(),
		})),
	});
});

afterEach(cleanup);

function renderWithSources(sources: CachedSourceInfo[] = []): void {
	const adapters = createTestAdapters();
	// Wire getCachedSources to return the provided sources initially,
	// then empty after clear operations
	let currentSources = [...sources];
	adapters.bestiaryCache = {
		...adapters.bestiaryCache,
		getCachedSources: () => Promise.resolve(currentSources),
		clearSource(_system, sourceCode) {
			currentSources = currentSources.filter(
				(s) => s.sourceCode !== sourceCode,
			);
			return Promise.resolve();
		},
		clearAll() {
			currentSources = [];
			return Promise.resolve();
		},
	};

	render(<SourceManager />, {
		wrapper: ({ children }: { children: ReactNode }) => (
			<AllProviders adapters={adapters}>{children}</AllProviders>
		),
	});
}

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

	it("lists cached sources with display name and creature count", async () => {
		renderWithSources([
			{
				sourceCode: "mm",
				displayName: "Monster Manual",
				creatureCount: 300,
				cachedAt: Date.now(),
			},
			{
				sourceCode: "vgm",
				displayName: "Volo's Guide",
				creatureCount: 100,
				cachedAt: Date.now(),
			},
		]);
		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 removes all sources", async () => {
		const user = userEvent.setup();
		renderWithSources([
			{
				sourceCode: "mm",
				displayName: "Monster Manual",
				creatureCount: 300,
				cachedAt: Date.now(),
			},
		]);

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

		await user.click(screen.getByRole("button", { name: "Clear All" }));

		await waitFor(() => {
			expect(screen.getByText("No cached sources")).toBeInTheDocument();
		});
	});

	it("individual source delete button removes that source", async () => {
		const user = userEvent.setup();
		renderWithSources([
			{
				sourceCode: "mm",
				displayName: "Monster Manual",
				creatureCount: 300,
				cachedAt: Date.now(),
			},
			{
				sourceCode: "vgm",
				displayName: "Volo's Guide",
				creatureCount: 100,
				cachedAt: Date.now(),
			},
		]);

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

		await user.click(
			screen.getByRole("button", { name: "Remove Monster Manual" }),
		);

		await waitFor(() => {
			expect(screen.queryByText("Monster Manual")).not.toBeInTheDocument();
		});
		expect(screen.getByText("Volo's Guide")).toBeInTheDocument();
	});
});
