import clsx from "clsx";
import { Check, X } from "lucide-react";
import type * as React from "react";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { SendouButton } from "~/components/elements/Button";
import { shortStageName } from "~/modules/in-game-lists/stage-ids";
import type { ModeShort, StageId } from "~/modules/in-game-lists/types";
import { stageImageUrl } from "~/utils/urls";
import { Divider } from "../Divider";
import { SendouTabPanel } from "../elements/Tabs";
import { ModeImage } from "../Image";
import styles from "./MatchActionPickBanTab.module.css";
import { TAB_KEYS } from "./MatchTabs";

export interface PickBanMapOption {
	stageId?: StageId;
	mode?: ModeShort;
	picker?: "US" | "THEM" | "BOTH";
	nth?: number;
}

interface PickBanSubmission {
	type: "PICK" | "BAN";
	map: PickBanMapOption;
}

interface MatchActionPickBanTabProps {
	options: PickBanMapOption[];
	type: "PICK" | "BAN";
	onSubmit?: (data: PickBanSubmission) => void;
	isSubmitting?: boolean;
	actionButtons?: React.ReactNode;
	secondaryAction?: React.ReactNode;
	waitingFor?: string;
}

export function MatchActionPickBanTab({
	options,
	type,
	onSubmit,
	isSubmitting,
	actionButtons,
	secondaryAction,
	waitingFor,
}: MatchActionPickBanTabProps) {
	const { t } = useTranslation(["q", "common", "game-misc"]);
	const [selected, setSelected] = useState<PickBanMapOption>();
	const isWaiting = waitingFor !== undefined;

	const hasStage = options.every((option) => option.stageId !== undefined);
	const hasMode = options.every((option) => option.mode !== undefined);
	const layout: "STAGE_BY_MODE" | "STAGE_ONLY" | "MODE_ONLY" =
		hasStage && hasMode
			? "STAGE_BY_MODE"
			: hasStage
				? "STAGE_ONLY"
				: "MODE_ONLY";

	const titleKey =
		layout === "MODE_ONLY"
			? type === "PICK"
				? "q:match.action.pickMode"
				: "q:match.action.banMode"
			: type === "PICK"
				? "q:match.action.pickStage"
				: "q:match.action.banStage";

	const selectedLabel = (() => {
		if (!selected) return null;
		const stageName =
			selected.stageId !== undefined
				? t(`game-misc:STAGE_${selected.stageId}`)
				: null;
		const modeName =
			selected.mode !== undefined
				? t(
						selected.stageId !== undefined
							? `game-misc:MODE_SHORT_${selected.mode}`
							: `game-misc:MODE_LONG_${selected.mode}`,
					)
				: null;
		if (stageName && modeName) return `${stageName} (${modeName})`;
		return stageName ?? modeName;
	})();

	return (
		<SendouTabPanel id={TAB_KEYS.ACTION}>
			<div className={styles.root}>
				<div className={styles.title}>{t(titleKey)}</div>
				{actionButtons ? (
					<div className={styles.actionButtons}>{actionButtons}</div>
				) : null}

				<div className={styles.options}>
					{layout === "STAGE_BY_MODE" ? (
						<StageByModeGrid
							options={options}
							type={type}
							selected={selected}
							onSelect={setSelected}
							disabled={isWaiting}
						/>
					) : layout === "STAGE_ONLY" ? (
						<StageOnlyGrid
							options={options}
							type={type}
							selected={selected}
							onSelect={setSelected}
							disabled={isWaiting}
						/>
					) : (
						<ModeOnlyGrid
							options={options}
							type={type}
							selected={selected}
							onSelect={setSelected}
							disabled={isWaiting}
						/>
					)}
				</div>

				{isWaiting ? (
					<p className={styles.waiting}>
						{t("q:match.action.pickBanWaiting", { teamName: waitingFor })}
					</p>
				) : (
					<>
						<p className={styles.prompt}>
							{selectedLabel ? (
								<>
									<span
										className={
											type === "PICK" ? styles.verbPick : styles.verbBan
										}
									>
										{type === "PICK"
											? t("q:match.action.picking")
											: t("q:match.action.banning")}
									</span>{" "}
									{selectedLabel}
								</>
							) : (
								t("q:match.action.pickBanPrompt")
							)}
						</p>

						<SendouButton
							variant="primary"
							className={styles.submit}
							isDisabled={!selected || isSubmitting}
							onPress={() => {
								if (!selected) return;
								onSubmit?.({ type, map: selected });
							}}
							testId="pick-ban-submit-button"
						>
							{t("common:actions.submit")}
						</SendouButton>
					</>
				)}
			</div>
			{secondaryAction}
		</SendouTabPanel>
	);
}

function StageByModeGrid({
	options,
	type,
	selected,
	onSelect,
	disabled,
}: {
	options: PickBanMapOption[];
	type: "PICK" | "BAN";
	selected?: PickBanMapOption;
	onSelect: (option: PickBanMapOption) => void;
	disabled?: boolean;
}) {
	const modesInOrder: ModeShort[] = [];
	const byMode = new Map<ModeShort, PickBanMapOption[]>();
	for (const option of options) {
		const mode = option.mode!;
		if (!byMode.has(mode)) {
			byMode.set(mode, []);
			modesInOrder.push(mode);
		}
		byMode.get(mode)!.push(option);
	}

	return (
		<>
			{modesInOrder.map((mode) => (
				<div key={mode} className={styles.modeGroup}>
					<Divider className={styles.divider}>
						<ModeImage mode={mode} size={32} />
					</Divider>
					<div className={styles.stageGrid}>
						{byMode.get(mode)!.map((option) => (
							<StageTile
								key={`${option.stageId}-${option.mode}`}
								option={option}
								type={type}
								isSelected={isSameOption(option, selected)}
								onSelect={() => onSelect(option)}
								disabled={disabled}
							/>
						))}
					</div>
				</div>
			))}
		</>
	);
}

function StageOnlyGrid({
	options,
	type,
	selected,
	onSelect,
	disabled,
}: {
	options: PickBanMapOption[];
	type: "PICK" | "BAN";
	selected?: PickBanMapOption;
	onSelect: (option: PickBanMapOption) => void;
	disabled?: boolean;
}) {
	return (
		<div className={styles.stageGrid}>
			{options.map((option) => (
				<StageTile
					key={option.stageId}
					option={option}
					type={type}
					isSelected={isSameOption(option, selected)}
					onSelect={() => onSelect(option)}
					disabled={disabled}
				/>
			))}
		</div>
	);
}

function ModeOnlyGrid({
	options,
	type,
	selected,
	onSelect,
	disabled,
}: {
	options: PickBanMapOption[];
	type: "PICK" | "BAN";
	selected?: PickBanMapOption;
	onSelect: (option: PickBanMapOption) => void;
	disabled?: boolean;
}) {
	return (
		<div className={styles.modeGrid}>
			{options.map((option) => (
				<ModeTile
					key={option.mode}
					option={option}
					type={type}
					isSelected={isSameOption(option, selected)}
					onSelect={() => onSelect(option)}
					disabled={disabled}
				/>
			))}
		</div>
	);
}

function StageTile({
	option,
	type,
	isSelected,
	onSelect,
	disabled,
}: {
	option: PickBanMapOption;
	type: "PICK" | "BAN";
	isSelected: boolean;
	onSelect: () => void;
	disabled?: boolean;
}) {
	const { t } = useTranslation(["q", "game-misc"]);

	return (
		<div className={styles.tileContainer}>
			<div className={styles.tileWrapper}>
				<button
					type="button"
					className={clsx(styles.tile, styles.stageTile, {
						[styles.tileSelected]: isSelected,
					})}
					style={{
						"--map-image-url": `url("${stageImageUrl(option.stageId!)}.avif")`,
					}}
					onClick={onSelect}
					disabled={disabled}
					data-testid="pick-ban-button"
					aria-label={`${type === "PICK" ? "Pick" : "Ban"} ${shortStageName(
						t(`game-misc:STAGE_${option.stageId!}`),
					)}`}
				/>
				{isSelected ? (
					type === "PICK" ? (
						<Check className={clsx(styles.tileIcon, styles.tileIconPick)} />
					) : (
						<X className={clsx(styles.tileIcon, styles.tileIconBan)} />
					)
				) : null}
				{option.nth ? (
					<span className={styles.tileNumber}>{option.nth}</span>
				) : null}
			</div>
			<div className={styles.tileLabel}>
				{shortStageName(t(`game-misc:STAGE_${option.stageId!}`))}
			</div>
			{option.picker ? (
				<span
					className={clsx(styles.tileFrom, {
						"text-theme": option.picker === "BOTH",
						"text-success": option.picker === "US",
						"text-error": option.picker === "THEM",
					})}
				>
					{option.picker === "US"
						? t("q:match.action.pickerUs")
						: option.picker === "THEM"
							? t("q:match.action.pickerThem")
							: t("q:match.action.pickerBoth")}
				</span>
			) : null}
		</div>
	);
}

function ModeTile({
	option,
	type,
	isSelected,
	onSelect,
	disabled,
}: {
	option: PickBanMapOption;
	type: "PICK" | "BAN";
	isSelected: boolean;
	onSelect: () => void;
	disabled?: boolean;
}) {
	const { t } = useTranslation(["game-misc"]);

	return (
		<div className={styles.tileContainer}>
			<div className={styles.tileWrapper}>
				<button
					type="button"
					className={clsx(styles.tile, styles.modeTile, {
						[styles.tileSelected]: isSelected,
					})}
					onClick={onSelect}
					disabled={disabled}
					data-testid="pick-ban-button"
				>
					<ModeImage mode={option.mode!} size={48} />
				</button>
				{isSelected ? (
					type === "PICK" ? (
						<Check className={clsx(styles.tileIcon, styles.tileIconPick)} />
					) : (
						<X className={clsx(styles.tileIcon, styles.tileIconBan)} />
					)
				) : null}
			</div>
			<div className={styles.tileLabel}>
				{t(`game-misc:MODE_LONG_${option.mode!}`)}
			</div>
		</div>
	);
}

function isSameOption(a: PickBanMapOption, b: PickBanMapOption | undefined) {
	if (!b) return false;
	return a.stageId === b.stageId && a.mode === b.mode;
}
