// @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, beforeAll, describe, expect, it, vi } from "vitest";
import { App } from "../App.js";
import { AllProviders } from "./test-providers.js";

// DOM API stubs — jsdom doesn't implement these
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(),
		})),
	});
	Element.prototype.scrollIntoView = vi.fn();
});

afterEach(cleanup);

async function addCombatant(
	user: ReturnType<typeof userEvent.setup>,
	name: string,
	opts?: { maxHp?: string },
) {
	const inputs = screen.getAllByPlaceholderText("+ Add combatants");
	const input = inputs.at(-1) ?? inputs[0];
	await user.type(input, name);

	if (opts?.maxHp) {
		const maxHpInput = screen.getByPlaceholderText("MaxHP");
		await user.type(maxHpInput, opts.maxHp);
	}

	const addButton = screen.getByRole("button", { name: "Add" });
	await user.click(addButton);
}

describe("App integration", () => {
	it("adds a combatant and removes it, returning to empty state", async () => {
		const user = userEvent.setup();
		render(<App />, { wrapper: AllProviders });

		// Empty state: centered input visible, no TurnNavigation
		expect(screen.getByPlaceholderText("+ Add combatants")).toBeInTheDocument();
		expect(screen.queryByText("R1")).not.toBeInTheDocument();

		// Add a combatant
		await addCombatant(user, "Goblin");

		// Verify combatant appears and TurnNavigation shows
		expect(screen.getByRole("button", { name: "Goblin" })).toBeInTheDocument();
		expect(screen.getByText("R1")).toBeInTheDocument();
		expect(screen.getAllByText("Goblin").length).toBeGreaterThanOrEqual(2);

		// Remove combatant via ConfirmButton (two clicks)
		const removeBtn = screen.getByRole("button", {
			name: "Remove combatant",
		});
		await user.click(removeBtn);
		const confirmBtn = screen.getByRole("button", {
			name: "Confirm remove combatant",
		});
		await user.click(confirmBtn);

		// Back to empty state (R1 badge may linger due to exit animation in jsdom)
		expect(
			screen.queryByRole("button", { name: "Goblin" }),
		).not.toBeInTheDocument();
		expect(screen.getByPlaceholderText("+ Add combatants")).toBeInTheDocument();
	});

	it("advances and retreats turns across two combatants", async () => {
		const user = userEvent.setup();
		render(<App />, { wrapper: AllProviders });

		await addCombatant(user, "Fighter");
		await addCombatant(user, "Wizard");

		// Initial state — R1, Fighter active (Previous turn disabled)
		expect(screen.getByText("R1")).toBeInTheDocument();
		expect(
			screen.getByRole("button", { name: "Previous turn" }),
		).toBeDisabled();

		// Advance turn — Wizard becomes active
		await user.click(screen.getByRole("button", { name: "Next turn" }));
		expect(screen.getByText("R1")).toBeInTheDocument();
		expect(screen.getByRole("button", { name: "Previous turn" })).toBeEnabled();

		// Advance again — wraps to R2, Fighter active
		await user.click(screen.getByRole("button", { name: "Next turn" }));
		expect(screen.getByText("R2")).toBeInTheDocument();
		expect(screen.getByRole("button", { name: "Previous turn" })).toBeEnabled();

		// Retreat — back to R1, Wizard active
		await user.click(screen.getByRole("button", { name: "Previous turn" }));
		expect(screen.getByText("R1")).toBeInTheDocument();
	});

	it("adds a combatant with HP, applies damage, and shows unconscious state", async () => {
		const user = userEvent.setup();
		render(<App />, { wrapper: AllProviders });

		await addCombatant(user, "Ogre", { maxHp: "59" });

		// Verify HP displays — currentHp and maxHp both show "59"
		const hpButton = screen.getByRole("button", {
			name: "Current HP: 59 (healthy)",
		});
		expect(hpButton).toBeInTheDocument();

		// Click currentHp to open HpAdjustPopover, apply full damage
		await user.click(hpButton);
		const hpInput = screen.getByPlaceholderText("HP");
		expect(hpInput).toBeInTheDocument();
		await user.type(hpInput, "59");
		await user.click(screen.getByRole("button", { name: "Apply damage" }));

		// Verify HP decreased to 0 and unconscious state
		expect(
			screen.getByRole("button", { name: "Current HP: 0 (unconscious)" }),
		).toBeInTheDocument();
	});
});
