// @vitest-environment jsdom
import type { Creature, CreatureId, PlayerCharacter } from "@initiative/domain";
import { combatantId, creatureId, playerCharacterId } from "@initiative/domain";
import { renderHook, waitFor } from "@testing-library/react";
import type { ReactNode } from "react";
import { beforeAll, describe, expect, it, vi } from "vitest";
import { createTestAdapters } from "../../__tests__/adapters/in-memory-adapters.js";
import {
	buildCombatant,
	buildCreature,
	buildEncounter,
} from "../../__tests__/factories/index.js";
import { AllProviders } from "../../__tests__/test-providers.js";
import { useDifficulty } from "../use-difficulty.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(),
		})),
	});
});

const pcId1 = playerCharacterId("pc-1");
const goblinCreature = buildCreature({
	id: creatureId("srd:goblin"),
	name: "Goblin",
	cr: "1/4",
});

function makeWrapper(options: {
	encounter: ReturnType<typeof buildEncounter>;
	playerCharacters?: PlayerCharacter[];
	creatures?: Map<CreatureId, Creature>;
}) {
	const adapters = createTestAdapters({
		encounter: options.encounter,
		playerCharacters: options.playerCharacters ?? [],
		creatures: options.creatures,
	});
	return ({ children }: { children: ReactNode }) => (
		<AllProviders adapters={adapters}>{children}</AllProviders>
	);
}

describe("useDifficulty with custom combatant CRs", () => {
	it("includes custom combatant with cr field in monster XP", () => {
		const wrapper = makeWrapper({
			encounter: buildEncounter({
				combatants: [
					buildCombatant({
						id: combatantId("c-1"),
						name: "Hero",
						playerCharacterId: pcId1,
					}),
					buildCombatant({
						id: combatantId("c-2"),
						name: "Custom Thug",
						cr: "2",
					}),
				],
			}),
			playerCharacters: [
				{ id: pcId1, name: "Hero", ac: 15, maxHp: 30, level: 1 },
			],
		});

		const { result } = renderHook(() => useDifficulty(), { wrapper });

		expect(result.current).not.toBeNull();
		expect(result.current?.totalMonsterXp).toBe(450);
	});

	it("uses bestiary CR when combatant has both creatureId and cr", async () => {
		const wrapper = makeWrapper({
			encounter: buildEncounter({
				combatants: [
					buildCombatant({
						id: combatantId("c-1"),
						name: "Hero",
						playerCharacterId: pcId1,
					}),
					buildCombatant({
						id: combatantId("c-2"),
						name: "Goblin",
						creatureId: goblinCreature.id,
						cr: "5",
					}),
				],
			}),
			playerCharacters: [
				{ id: pcId1, name: "Hero", ac: 15, maxHp: 30, level: 1 },
			],
			creatures: new Map([[goblinCreature.id, goblinCreature]]),
		});

		const { result } = renderHook(() => useDifficulty(), { wrapper });

		await waitFor(() => {
			expect(result.current).not.toBeNull();
			// Should use bestiary CR 1/4 (50 XP), not the manual cr "5" (1800 XP)
			expect(result.current?.totalMonsterXp).toBe(50);
		});
	});

	it("mixes bestiary and custom-with-CR combatants correctly", async () => {
		const wrapper = makeWrapper({
			encounter: buildEncounter({
				combatants: [
					buildCombatant({
						id: combatantId("c-1"),
						name: "Hero",
						playerCharacterId: pcId1,
					}),
					buildCombatant({
						id: combatantId("c-2"),
						name: "Goblin",
						creatureId: goblinCreature.id,
					}),
					buildCombatant({
						id: combatantId("c-3"),
						name: "Custom",
						cr: "1",
					}),
				],
			}),
			playerCharacters: [
				{ id: pcId1, name: "Hero", ac: 15, maxHp: 30, level: 5 },
			],
			creatures: new Map([[goblinCreature.id, goblinCreature]]),
		});

		const { result } = renderHook(() => useDifficulty(), { wrapper });

		await waitFor(() => {
			expect(result.current).not.toBeNull();
			// CR 1/4 = 50 XP, CR 1 = 200 XP → total 250
			expect(result.current?.totalMonsterXp).toBe(250);
		});
	});

	it("custom combatant without CR is still excluded", () => {
		const wrapper = makeWrapper({
			encounter: buildEncounter({
				combatants: [
					buildCombatant({
						id: combatantId("c-1"),
						name: "Hero",
						playerCharacterId: pcId1,
					}),
					buildCombatant({
						id: combatantId("c-2"),
						name: "Custom Monster",
					}),
				],
			}),
			playerCharacters: [
				{ id: pcId1, name: "Hero", ac: 15, maxHp: 30, level: 1 },
			],
		});

		const { result } = renderHook(() => useDifficulty(), { wrapper });
		expect(result.current).toBeNull();
	});
});
