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

import { cleanup, render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { afterEach, describe, expect, it, vi } from "vitest";
import { createTestAdapters } from "../../__tests__/adapters/in-memory-adapters.js";
import { AdapterProvider } from "../../contexts/adapter-context.js";
import { BulkImportPrompt } from "../bulk-import-prompt.js";

const THREE_SOURCES_REGEX = /3 sources/;
const GITHUB_URL_REGEX = /raw\.githubusercontent/;
const LOADING_PROGRESS_REGEX = /Loading sources\.\.\. 4\/10/;
const SEVEN_OF_TEN_REGEX = /7\/10 sources/;
const THREE_FAILED_REGEX = /3 failed/;

afterEach(cleanup);

const mockFetchAndCacheSource = vi.fn();
const mockIsSourceCached = vi.fn().mockResolvedValue(false);
const mockRefreshCache = vi.fn();
const mockStartImport = vi.fn();
const mockReset = vi.fn();
const mockDismissPanel = vi.fn();

let mockImportState = {
	status: "idle" as string,
	total: 0,
	completed: 0,
	failed: 0,
};

// Uses context mocks because the bulk import state machine (idle → loading →
// complete → partial-failure) is impractical to drive through user interactions
// without real network calls. Consider migrating if adapter injection expands
// to cover these state transitions.
vi.mock("../../contexts/bestiary-context.js", () => ({
	useBestiaryContext: () => ({
		fetchAndCacheSource: mockFetchAndCacheSource,
		isSourceCached: mockIsSourceCached,
		refreshCache: mockRefreshCache,
	}),
}));

vi.mock("../../contexts/bulk-import-context.js", () => ({
	useBulkImportContext: () => ({
		state: mockImportState,
		startImport: mockStartImport,
		reset: mockReset,
	}),
}));

vi.mock("../../contexts/side-panel-context.js", () => ({
	useSidePanelContext: () => ({
		dismissPanel: mockDismissPanel,
	}),
}));

function createAdaptersWithSources() {
	const adapters = createTestAdapters();
	adapters.bestiaryIndex = {
		...adapters.bestiaryIndex,
		getAllSourceCodes: () => ["MM", "VGM", "XGE"],
	};
	return adapters;
}

function renderWithAdapters() {
	const adapters = createAdaptersWithSources();
	return render(
		<AdapterProvider adapters={adapters}>
			<BulkImportPrompt />
		</AdapterProvider>,
	);
}

describe("BulkImportPrompt", () => {
	afterEach(() => {
		vi.clearAllMocks();
		mockImportState = { status: "idle", total: 0, completed: 0, failed: 0 };
	});

	it("idle: shows base URL input, source count, Load All button", () => {
		renderWithAdapters();
		expect(screen.getByText(THREE_SOURCES_REGEX)).toBeInTheDocument();
		expect(screen.getByDisplayValue(GITHUB_URL_REGEX)).toBeInTheDocument();
		expect(
			screen.getByRole("button", { name: "Load All" }),
		).toBeInTheDocument();
	});

	it("idle: clearing URL disables the button", async () => {
		const user = userEvent.setup();
		renderWithAdapters();

		const input = screen.getByDisplayValue(GITHUB_URL_REGEX);
		await user.clear(input);
		expect(screen.getByRole("button", { name: "Load All" })).toBeDisabled();
	});

	it("idle: clicking Load All calls startImport with URL", async () => {
		const user = userEvent.setup();
		renderWithAdapters();

		await user.click(screen.getByRole("button", { name: "Load All" }));
		expect(mockStartImport).toHaveBeenCalledWith(
			expect.stringContaining("raw.githubusercontent"),
			mockFetchAndCacheSource,
			mockIsSourceCached,
			mockRefreshCache,
		);
	});

	it("loading: shows progress text and progress bar", () => {
		mockImportState = {
			status: "loading",
			total: 10,
			completed: 3,
			failed: 1,
		};
		renderWithAdapters();
		expect(screen.getByText(LOADING_PROGRESS_REGEX)).toBeInTheDocument();
	});

	it("complete: shows success message and Done button", () => {
		mockImportState = {
			status: "complete",
			total: 10,
			completed: 10,
			failed: 0,
		};
		renderWithAdapters();
		expect(screen.getByText("All sources loaded")).toBeInTheDocument();
		expect(screen.getByRole("button", { name: "Done" })).toBeInTheDocument();
	});

	it("complete: Done calls dismissPanel and reset", async () => {
		mockImportState = {
			status: "complete",
			total: 10,
			completed: 10,
			failed: 0,
		};
		const user = userEvent.setup();
		renderWithAdapters();

		await user.click(screen.getByRole("button", { name: "Done" }));
		expect(mockDismissPanel).toHaveBeenCalled();
		expect(mockReset).toHaveBeenCalled();
	});

	it("partial-failure: shows loaded/failed counts", () => {
		mockImportState = {
			status: "partial-failure",
			total: 10,
			completed: 7,
			failed: 3,
		};
		renderWithAdapters();
		expect(screen.getByText(SEVEN_OF_TEN_REGEX)).toBeInTheDocument();
		expect(screen.getByText(THREE_FAILED_REGEX)).toBeInTheDocument();
	});
});
