import type { DifficultyTier } from "@initiative/domain";
import { ArrowLeftRight } from "lucide-react";
import { useRef } from "react";
import { useEncounterContext } from "../contexts/encounter-context.js";
import { useClickOutside } from "../hooks/use-click-outside.js";
import {
	type BreakdownCombatant,
	useDifficultyBreakdown,
} from "../hooks/use-difficulty-breakdown.js";
import { CrPicker } from "./cr-picker.js";
import { Button } from "./ui/button.js";

const TIER_LABELS: Record<DifficultyTier, { label: string; color: string }> = {
	trivial: { label: "Trivial", color: "text-muted-foreground" },
	low: { label: "Low", color: "text-green-500" },
	moderate: { label: "Moderate", color: "text-yellow-500" },
	high: { label: "High", color: "text-red-500" },
};

function formatXp(xp: number): string {
	return xp.toLocaleString();
}

function PcRow({ entry }: { entry: BreakdownCombatant }) {
	return (
		<div className="col-span-4 grid grid-cols-subgrid items-center text-xs">
			<span className="min-w-0 truncate" title={entry.combatant.name}>
				{entry.combatant.name}
			</span>
			<span />
			<span className="text-muted-foreground">
				{entry.level === undefined ? "\u2014" : `Lv ${entry.level}`}
			</span>
			<span className="text-right tabular-nums">{"\u2014"}</span>
		</div>
	);
}

function NpcRow({
	entry,
	onToggleSide,
}: {
	entry: BreakdownCombatant;
	onToggleSide: () => void;
}) {
	const { setCr } = useEncounterContext();
	const isParty = entry.side === "party";
	const targetSide = isParty ? "enemy" : "party";

	let xpDisplay: string;
	if (entry.xp == null) {
		xpDisplay = "\u2014";
	} else if (isParty && entry.cr) {
		xpDisplay = `\u2212${formatXp(entry.xp)}`;
	} else {
		xpDisplay = formatXp(entry.xp);
	}

	return (
		<div className="col-span-4 grid grid-cols-subgrid items-center text-xs">
			<span className="min-w-0 truncate" title={entry.combatant.name}>
				{entry.combatant.name}
			</span>
			<Button
				variant="ghost"
				size="icon-sm"
				onClick={onToggleSide}
				aria-label={`Move ${entry.combatant.name} to ${targetSide} side`}
			>
				<ArrowLeftRight className="h-3 w-3" />
			</Button>
			<span>
				{entry.editable ? (
					<CrPicker
						value={entry.cr}
						onChange={(cr) => setCr(entry.combatant.id, cr)}
					/>
				) : (
					<span className="text-muted-foreground">
						{entry.cr ? `CR ${entry.cr}` : "\u2014"}
					</span>
				)}
			</span>
			<span className="text-right tabular-nums">{xpDisplay}</span>
		</div>
	);
}

export function DifficultyBreakdownPanel({ onClose }: { onClose: () => void }) {
	const ref = useRef<HTMLDivElement>(null);
	useClickOutside(ref, onClose);
	const { setSide } = useEncounterContext();

	const breakdown = useDifficultyBreakdown();
	if (!breakdown) return null;

	const tierConfig = TIER_LABELS[breakdown.tier];

	const handleToggle = (entry: BreakdownCombatant) => {
		const newSide = entry.side === "party" ? "enemy" : "party";
		setSide(entry.combatant.id, newSide);
	};

	const isPC = (entry: BreakdownCombatant) =>
		entry.combatant.playerCharacterId != null;

	return (
		<div
			ref={ref}
			className="absolute top-full right-0 z-50 mt-1 w-80 rounded-lg border border-border bg-card p-3 shadow-lg max-sm:fixed max-sm:top-12 max-sm:right-3 max-sm:left-3 max-sm:w-auto"
		>
			<div className="mb-2 font-medium text-sm">
				Encounter Difficulty:{" "}
				<span className={tierConfig.color}>{tierConfig.label}</span>
			</div>

			<div className="mb-2 border-border border-t pt-2">
				<div className="mb-1 text-muted-foreground text-xs">
					Party Budget ({breakdown.pcCount}{" "}
					{breakdown.pcCount === 1 ? "PC" : "PCs"})
				</div>
				<div className="flex gap-3 text-xs">
					<span>
						Low: <strong>{formatXp(breakdown.partyBudget.low)}</strong>
					</span>
					<span>
						Mod: <strong>{formatXp(breakdown.partyBudget.moderate)}</strong>
					</span>
					<span>
						High: <strong>{formatXp(breakdown.partyBudget.high)}</strong>
					</span>
				</div>
			</div>

			<div className="border-border border-t pt-2 pb-2 text-muted-foreground text-xs italic">
				Allied NPC XP is subtracted from encounter difficulty
			</div>

			<div className="border-border border-t pt-2">
				<div className="mb-1 flex justify-between text-muted-foreground text-xs">
					<span>Party</span>
					<span>XP</span>
				</div>
				<div className="grid grid-cols-[1fr_auto_auto_3.5rem] gap-x-2 gap-y-1">
					{breakdown.partyCombatants.map((entry) =>
						isPC(entry) ? (
							<PcRow key={entry.combatant.id} entry={entry} />
						) : (
							<NpcRow
								key={entry.combatant.id}
								entry={entry}
								onToggleSide={() => handleToggle(entry)}
							/>
						),
					)}
				</div>
			</div>

			<div className="mt-2 border-border border-t pt-2">
				<div className="mb-1 flex justify-between text-muted-foreground text-xs">
					<span>Enemy</span>
					<span>XP</span>
				</div>
				<div className="grid grid-cols-[1fr_auto_auto_3.5rem] gap-x-2 gap-y-1">
					{breakdown.enemyCombatants.map((entry) =>
						isPC(entry) ? (
							<PcRow key={entry.combatant.id} entry={entry} />
						) : (
							<NpcRow
								key={entry.combatant.id}
								entry={entry}
								onToggleSide={() => handleToggle(entry)}
							/>
						),
					)}
				</div>
			</div>

			<div className="mt-2 flex justify-between border-border border-t pt-2 font-medium text-xs">
				<span>Net Monster XP</span>
				<span className="tabular-nums">
					{formatXp(breakdown.totalMonsterXp)}
				</span>
			</div>
		</div>
	);
}
