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

import type { Creature, CreatureId } from "@initiative/domain";
import { cleanup, fireEvent, render, screen } from "@testing-library/react";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { StatBlockPanel } from "../components/stat-block-panel";

const CREATURE_ID = "srd:goblin" as CreatureId;
const CREATURE: Creature = {
	id: CREATURE_ID,
	name: "Goblin",
	source: "SRD",
	sourceDisplayName: "SRD",
	size: "Small",
	type: "humanoid",
	alignment: "neutral evil",
	ac: 15,
	hp: { average: 7, formula: "2d6" },
	speed: "30 ft.",
	abilities: { str: 8, dex: 14, con: 10, int: 10, wis: 8, cha: 8 },
	cr: "1/4",
	initiativeProficiency: 0,
	proficiencyBonus: 2,
	passive: 9,
};

function mockMatchMedia(matches: boolean) {
	Object.defineProperty(window, "matchMedia", {
		writable: true,
		value: vi.fn().mockImplementation((query: string) => ({
			matches,
			media: query,
			onchange: null,
			addListener: vi.fn(),
			removeListener: vi.fn(),
			addEventListener: vi.fn(),
			removeEventListener: vi.fn(),
			dispatchEvent: vi.fn(),
		})),
	});
}

interface PanelProps {
	creatureId?: CreatureId | null;
	creature?: Creature | null;
	panelRole?: "browse" | "pinned";
	isCollapsed?: boolean;
	onToggleCollapse?: () => void;
	onPin?: () => void;
	onUnpin?: () => void;
	showPinButton?: boolean;
	side?: "left" | "right";
	onDismiss?: () => void;
	bulkImportMode?: boolean;
}

function renderPanel(overrides: PanelProps = {}) {
	const props = {
		creatureId: CREATURE_ID,
		creature: CREATURE,
		isSourceCached: vi.fn().mockResolvedValue(true),
		fetchAndCacheSource: vi.fn(),
		uploadAndCacheSource: vi.fn(),
		refreshCache: vi.fn(),
		panelRole: "browse" as const,
		isCollapsed: false,
		onToggleCollapse: vi.fn(),
		onPin: vi.fn(),
		onUnpin: vi.fn(),
		showPinButton: false,
		side: "right" as const,
		onDismiss: vi.fn(),
		...overrides,
	};

	render(<StatBlockPanel {...props} />);
	return props;
}

describe("Stat Block Panel Collapse/Expand and Pin", () => {
	beforeEach(() => {
		mockMatchMedia(true); // desktop by default
	});

	afterEach(cleanup);

	describe("US1: Collapse and Expand", () => {
		it("shows collapse button instead of close button on desktop", () => {
			renderPanel();
			expect(
				screen.getByRole("button", { name: "Collapse stat block panel" }),
			).toBeInTheDocument();
			expect(
				screen.queryByRole("button", { name: /close/i }),
			).not.toBeInTheDocument();
		});

		it("does not show 'Stat Block' heading", () => {
			renderPanel();
			expect(screen.queryByText("Stat Block")).not.toBeInTheDocument();
		});

		it("renders collapsed tab with creature name when isCollapsed is true", () => {
			renderPanel({ isCollapsed: true });
			expect(screen.getByText("Goblin")).toBeInTheDocument();
			expect(
				screen.getByRole("button", { name: "Expand stat block panel" }),
			).toBeInTheDocument();
		});

		it("calls onToggleCollapse when collapse button is clicked", () => {
			const props = renderPanel();
			fireEvent.click(
				screen.getByRole("button", { name: "Collapse stat block panel" }),
			);
			expect(props.onToggleCollapse).toHaveBeenCalledTimes(1);
		});

		it("calls onToggleCollapse when collapsed tab is clicked", () => {
			const props = renderPanel({ isCollapsed: true });
			fireEvent.click(
				screen.getByRole("button", { name: "Expand stat block panel" }),
			);
			expect(props.onToggleCollapse).toHaveBeenCalledTimes(1);
		});

		it("applies translate-x class when collapsed (right side)", () => {
			renderPanel({ isCollapsed: true, side: "right" });
			const panel = screen
				.getByRole("button", { name: "Expand stat block panel" })
				.closest("div");
			expect(panel?.className).toContain("translate-x-[calc(100%-40px)]");
		});

		it("applies translate-x-0 when expanded", () => {
			renderPanel({ isCollapsed: false });
			const foldBtn = screen.getByRole("button", {
				name: "Collapse stat block panel",
			});
			const panel = foldBtn.closest("div.fixed") as HTMLElement;
			expect(panel?.className).toContain("translate-x-0");
		});
	});

	describe("US1: Mobile behavior", () => {
		beforeEach(() => {
			mockMatchMedia(false); // mobile
		});

		it("shows collapse button instead of X close button on mobile drawer", () => {
			renderPanel();
			expect(
				screen.getByRole("button", { name: "Collapse stat block panel" }),
			).toBeInTheDocument();
			// No X close icon button — only backdrop dismiss and collapse toggle
			const buttons = screen.getAllByRole("button");
			const buttonLabels = buttons.map((b) => b.getAttribute("aria-label"));
			expect(buttonLabels).not.toContain("Close");
		});

		it("calls onDismiss when backdrop is clicked on mobile", () => {
			const props = renderPanel();
			fireEvent.click(screen.getByRole("button", { name: "Close stat block" }));
			expect(props.onDismiss).toHaveBeenCalledTimes(1);
		});

		it("does not render pinned panel on mobile", () => {
			const { container } = render(
				<StatBlockPanel
					creatureId={CREATURE_ID}
					creature={CREATURE}
					isSourceCached={vi.fn().mockResolvedValue(true)}
					fetchAndCacheSource={vi.fn()}
					uploadAndCacheSource={vi.fn()}
					refreshCache={vi.fn()}
					panelRole="pinned"
					isCollapsed={false}
					onToggleCollapse={vi.fn()}
					onPin={vi.fn()}
					onUnpin={vi.fn()}
					showPinButton={false}
					side="left"
					onDismiss={vi.fn()}
				/>,
			);
			expect(container.innerHTML).toBe("");
		});
	});

	describe("US2: Pin and Unpin", () => {
		it("shows pin button when showPinButton is true on desktop", () => {
			renderPanel({ showPinButton: true });
			expect(
				screen.getByRole("button", { name: "Pin creature" }),
			).toBeInTheDocument();
		});

		it("hides pin button when showPinButton is false", () => {
			renderPanel({ showPinButton: false });
			expect(
				screen.queryByRole("button", { name: "Pin creature" }),
			).not.toBeInTheDocument();
		});

		it("calls onPin when pin button is clicked", () => {
			const props = renderPanel({ showPinButton: true });
			fireEvent.click(screen.getByRole("button", { name: "Pin creature" }));
			expect(props.onPin).toHaveBeenCalledTimes(1);
		});

		it("shows unpin button for pinned role", () => {
			renderPanel({ panelRole: "pinned", side: "left" });
			expect(
				screen.getByRole("button", { name: "Unpin creature" }),
			).toBeInTheDocument();
		});

		it("calls onUnpin when unpin button is clicked", () => {
			const props = renderPanel({ panelRole: "pinned", side: "left" });
			fireEvent.click(screen.getByRole("button", { name: "Unpin creature" }));
			expect(props.onUnpin).toHaveBeenCalledTimes(1);
		});

		it("positions pinned panel on the left side", () => {
			renderPanel({ panelRole: "pinned", side: "left" });
			const unpinBtn = screen.getByRole("button", {
				name: "Unpin creature",
			});
			const panel = unpinBtn.closest("div.fixed") as HTMLElement;
			expect(panel?.className).toContain("left-0");
			expect(panel?.className).toContain("border-r");
		});

		it("positions browse panel on the right side", () => {
			renderPanel({ panelRole: "browse", side: "right" });
			const foldBtn = screen.getByRole("button", {
				name: "Collapse stat block panel",
			});
			const panel = foldBtn.closest("div.fixed") as HTMLElement;
			expect(panel?.className).toContain("right-0");
			expect(panel?.className).toContain("border-l");
		});
	});

	describe("US3: Collapse independence with pinned panel", () => {
		it("pinned panel has no collapse button", () => {
			renderPanel({ panelRole: "pinned", side: "left" });
			expect(
				screen.queryByRole("button", { name: /collapse/i }),
			).not.toBeInTheDocument();
		});

		it("pinned panel is always expanded (no translate offset)", () => {
			renderPanel({ panelRole: "pinned", side: "left", isCollapsed: false });
			const unpinBtn = screen.getByRole("button", {
				name: "Unpin creature",
			});
			const panel = unpinBtn.closest("div.fixed") as HTMLElement;
			expect(panel?.className).toContain("translate-x-0");
		});
	});
});
