// @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 { HpAdjustPopover } from "../hp-adjust-popover";

afterEach(cleanup);

function renderPopover(
	overrides: Partial<{
		onAdjust: (delta: number) => void;
		onSetTempHp: (value: number) => void;
		onClose: () => void;
	}> = {},
) {
	const onAdjust = overrides.onAdjust ?? vi.fn();
	const onSetTempHp = overrides.onSetTempHp ?? vi.fn();
	const onClose = overrides.onClose ?? vi.fn();
	const result = render(
		<HpAdjustPopover
			onAdjust={onAdjust}
			onSetTempHp={onSetTempHp}
			onClose={onClose}
		/>,
	);
	return { ...result, onAdjust, onSetTempHp, onClose };
}

describe("HpAdjustPopover", () => {
	it("renders input with placeholder 'HP'", () => {
		renderPopover();
		expect(screen.getByPlaceholderText("HP")).toBeInTheDocument();
	});

	it("damage and heal buttons are disabled when input is empty", () => {
		renderPopover();
		expect(screen.getByRole("button", { name: "Apply damage" })).toBeDisabled();
		expect(
			screen.getByRole("button", { name: "Apply healing" }),
		).toBeDisabled();
	});

	it("damage and heal buttons are disabled when input is '0'", async () => {
		const user = userEvent.setup();
		renderPopover();
		await user.type(screen.getByPlaceholderText("HP"), "0");
		expect(screen.getByRole("button", { name: "Apply damage" })).toBeDisabled();
		expect(
			screen.getByRole("button", { name: "Apply healing" }),
		).toBeDisabled();
	});

	it("typing a valid number enables both buttons", async () => {
		const user = userEvent.setup();
		renderPopover();
		await user.type(screen.getByPlaceholderText("HP"), "5");
		expect(
			screen.getByRole("button", { name: "Apply damage" }),
		).not.toBeDisabled();
		expect(
			screen.getByRole("button", { name: "Apply healing" }),
		).not.toBeDisabled();
	});

	it("clicking damage button calls onAdjust with negative value and onClose", async () => {
		const user = userEvent.setup();
		const { onAdjust, onClose } = renderPopover();
		await user.type(screen.getByPlaceholderText("HP"), "7");
		await user.click(screen.getByRole("button", { name: "Apply damage" }));
		expect(onAdjust).toHaveBeenCalledWith(-7);
		expect(onClose).toHaveBeenCalled();
	});

	it("clicking heal button calls onAdjust with positive value and onClose", async () => {
		const user = userEvent.setup();
		const { onAdjust, onClose } = renderPopover();
		await user.type(screen.getByPlaceholderText("HP"), "3");
		await user.click(screen.getByRole("button", { name: "Apply healing" }));
		expect(onAdjust).toHaveBeenCalledWith(3);
		expect(onClose).toHaveBeenCalled();
	});

	it("Enter key applies damage (negative)", async () => {
		const user = userEvent.setup();
		const { onAdjust, onClose } = renderPopover();
		const input = screen.getByPlaceholderText("HP");
		await user.type(input, "4");
		await user.keyboard("{Enter}");
		expect(onAdjust).toHaveBeenCalledWith(-4);
		expect(onClose).toHaveBeenCalled();
	});

	it("Shift+Enter applies healing (positive)", async () => {
		const user = userEvent.setup();
		const { onAdjust, onClose } = renderPopover();
		const input = screen.getByPlaceholderText("HP");
		await user.type(input, "6");
		await user.keyboard("{Shift>}{Enter}{/Shift}");
		expect(onAdjust).toHaveBeenCalledWith(6);
		expect(onClose).toHaveBeenCalled();
	});

	it("Escape key calls onClose", async () => {
		const user = userEvent.setup();
		const { onClose } = renderPopover();
		const input = screen.getByPlaceholderText("HP");
		await user.type(input, "2");
		await user.keyboard("{Escape}");
		expect(onClose).toHaveBeenCalled();
	});

	it("only accepts digit characters in input", async () => {
		const user = userEvent.setup();
		renderPopover();
		const input = screen.getByPlaceholderText("HP");
		await user.type(input, "12abc34");
		expect(input).toHaveValue("1234");
	});

	describe("temp HP", () => {
		it("shield button calls onSetTempHp with entered value and closes", async () => {
			const user = userEvent.setup();
			const { onSetTempHp, onClose } = renderPopover();
			await user.type(screen.getByPlaceholderText("HP"), "8");
			await user.click(screen.getByRole("button", { name: "Set temp HP" }));
			expect(onSetTempHp).toHaveBeenCalledWith(8);
			expect(onClose).toHaveBeenCalled();
		});

		it("shield button is disabled when input is empty", () => {
			renderPopover();
			expect(
				screen.getByRole("button", { name: "Set temp HP" }),
			).toBeDisabled();
		});

		it("shield button is disabled when input is '0'", async () => {
			const user = userEvent.setup();
			renderPopover();
			await user.type(screen.getByPlaceholderText("HP"), "0");
			expect(
				screen.getByRole("button", { name: "Set temp HP" }),
			).toBeDisabled();
		});
	});
});
