// @vitest-environment jsdom
import type { PlayerCharacter } from "@initiative/domain";
import { playerCharacterId } from "@initiative/domain";
import { cleanup, render, screen } from "@testing-library/react";
import { userEvent } from "@testing-library/user-event";
import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
import { polyfillDialog } from "../../__tests__/polyfill-dialog.js";
import { CreatePlayerModal } from "../create-player-modal.js";

beforeAll(() => {
	polyfillDialog();
});

afterEach(cleanup);

function renderModal(
	overrides: Partial<Parameters<typeof CreatePlayerModal>[0]> = {},
) {
	const defaults = {
		open: true,
		onClose: vi.fn(),
		onSave: vi.fn(),
	};
	const props = { ...defaults, ...overrides };
	return { ...render(<CreatePlayerModal {...props} />), ...props };
}

describe("CreatePlayerModal", () => {
	it("renders create form with defaults", () => {
		renderModal();
		expect(screen.getByText("Create Player")).toBeDefined();
		expect(screen.getByLabelText("Name")).toBeDefined();
		expect(screen.getByLabelText("AC")).toBeDefined();
		expect(screen.getByLabelText("Max HP")).toBeDefined();
		expect(screen.getByLabelText("Level")).toBeDefined();
		expect(screen.getByRole("button", { name: "Create" })).toBeDefined();
	});

	it("renders edit form when playerCharacter is provided", () => {
		const pc: PlayerCharacter = {
			id: playerCharacterId("pc-1"),
			name: "Gandalf",
			ac: 15,
			maxHp: 40,
			color: "blue",
			icon: "wand",
			level: 10,
		};
		renderModal({ playerCharacter: pc });
		expect(screen.getByText("Edit Player")).toBeDefined();
		expect(screen.getByLabelText("Name")).toHaveProperty("value", "Gandalf");
		expect(screen.getByLabelText("AC")).toHaveProperty("value", "15");
		expect(screen.getByLabelText("Max HP")).toHaveProperty("value", "40");
		expect(screen.getByLabelText("Level")).toHaveProperty("value", "10");
		expect(screen.getByRole("button", { name: "Save" })).toBeDefined();
	});

	it("calls onSave with valid data", async () => {
		const { onSave, onClose } = renderModal();
		const user = userEvent.setup();

		await user.type(screen.getByLabelText("Name"), "Aria");
		await user.clear(screen.getByLabelText("AC"));
		await user.type(screen.getByLabelText("AC"), "16");
		await user.clear(screen.getByLabelText("Max HP"));
		await user.type(screen.getByLabelText("Max HP"), "30");
		await user.type(screen.getByLabelText("Level"), "5");
		await user.click(screen.getByRole("button", { name: "Create" }));

		expect(onSave).toHaveBeenCalledWith(
			"Aria",
			16,
			30,
			undefined,
			undefined,
			5,
		);
		expect(onClose).toHaveBeenCalled();
	});

	it("shows error for empty name", async () => {
		const { onSave } = renderModal();
		const user = userEvent.setup();

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

		expect(screen.getByText("Name is required")).toBeDefined();
		expect(onSave).not.toHaveBeenCalled();
	});

	it("shows error for invalid AC", async () => {
		const { onSave } = renderModal();
		const user = userEvent.setup();

		await user.type(screen.getByLabelText("Name"), "Test");
		await user.clear(screen.getByLabelText("AC"));
		await user.type(screen.getByLabelText("AC"), "abc");
		await user.click(screen.getByRole("button", { name: "Create" }));

		expect(screen.getByText("AC must be a non-negative number")).toBeDefined();
		expect(onSave).not.toHaveBeenCalled();
	});

	it("shows error for invalid Max HP", async () => {
		const { onSave } = renderModal();
		const user = userEvent.setup();

		await user.type(screen.getByLabelText("Name"), "Test");
		await user.clear(screen.getByLabelText("Max HP"));
		await user.type(screen.getByLabelText("Max HP"), "0");
		await user.click(screen.getByRole("button", { name: "Create" }));

		expect(screen.getByText("Max HP must be at least 1")).toBeDefined();
		expect(onSave).not.toHaveBeenCalled();
	});

	it("shows error for invalid level", async () => {
		const { onSave } = renderModal();
		const user = userEvent.setup();

		await user.type(screen.getByLabelText("Name"), "Test");
		await user.type(screen.getByLabelText("Level"), "25");
		await user.click(screen.getByRole("button", { name: "Create" }));

		expect(screen.getByText("Level must be between 1 and 20")).toBeDefined();
		expect(onSave).not.toHaveBeenCalled();
	});

	it("clears error when name is edited", async () => {
		renderModal();
		const user = userEvent.setup();

		await user.click(screen.getByRole("button", { name: "Create" }));
		expect(screen.getByText("Name is required")).toBeDefined();

		await user.type(screen.getByLabelText("Name"), "A");
		expect(screen.queryByText("Name is required")).toBeNull();
	});

	it("calls onClose when cancel is clicked", async () => {
		const { onClose } = renderModal();
		const user = userEvent.setup();

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

		expect(onClose).toHaveBeenCalledOnce();
	});

	it("omits level when field is empty", async () => {
		const { onSave } = renderModal();
		const user = userEvent.setup();

		await user.type(screen.getByLabelText("Name"), "Aria");
		await user.click(screen.getByRole("button", { name: "Create" }));

		expect(onSave).toHaveBeenCalledWith(
			"Aria",
			10,
			10,
			undefined,
			undefined,
			undefined,
		);
	});
});
