"use client";

import {
	Command,
	CommandEmpty,
	CommandGroup,
	CommandInput,
	CommandItem,
	CommandList,
} from "@mdxui/primitives/command";
import {
	Popover,
	PopoverContent,
	PopoverTrigger,
} from "@mdxui/primitives/popover";
import { ChevronsUpDown, X } from "lucide-react";
import type React from "react";
import { useState } from "react";
import { cx, focusInput } from "../lib/utils";
import { OnboardingButton } from "../primitives/onboarding-button";
import { OnboardingCheckbox } from "../primitives/onboarding-checkbox";
import { OnboardingLabel } from "../primitives/onboarding-label";

export interface MultiSelectOption {
	value: string;
	label: string;
	disabled?: boolean;
}

export interface MultiSelectField {
	/** Unique identifier for the field */
	id: string;
	/** Label displayed above the multi-select */
	label: string;
	/** Placeholder text when no values selected */
	placeholder?: string;
	/** Search input placeholder */
	searchPlaceholder?: string;
	/** Message shown when no options match search */
	emptyMessage?: string;
	/** Optional description/helper text */
	description?: string;
	/** Whether at least one value is required */
	required?: boolean;
	/** Minimum number of selections required */
	minSelections?: number;
	/** Maximum number of selections allowed */
	maxSelections?: number;
	/** Available options */
	options: MultiSelectOption[];
	/** Default selected values */
	defaultValue?: string[];
}

export interface MultiSelectStepProps {
	/** Title displayed at the top of the step */
	title?: string;
	/** Description text below the title */
	description?: string;
	/** Array of multi-select fields */
	fields: MultiSelectField[];
	/** Called when any value changes */
	onValuesChange?: (values: Record<string, string[]>) => void;
	/** Called when the user submits the form */
	onSubmit: (values: Record<string, string[]>) => void | Promise<void>;
	/** Text for the submit button */
	submitText?: string;
	/** Text shown while submitting */
	loadingText?: string;
	/** Optional back button config */
	backButton?: {
		text: string;
		onClick: () => void;
	};
}

function MultiSelectInput({
	field,
	value,
	onChange,
}: {
	field: MultiSelectField;
	value: string[];
	onChange: (value: string[]) => void;
}) {
	const [open, setOpen] = useState(false);

	const handleSelect = (optionValue: string) => {
		if (value.includes(optionValue)) {
			onChange(value.filter((v) => v !== optionValue));
		} else {
			if (field.maxSelections && value.length >= field.maxSelections) {
				return;
			}
			onChange([...value, optionValue]);
		}
	};

	const handleRemove = (optionValue: string, e: React.MouseEvent) => {
		e.stopPropagation();
		onChange(value.filter((v) => v !== optionValue));
	};

	return (
		<Popover open={open} onOpenChange={setOpen}>
			<PopoverTrigger asChild>
				<button
					type="button"
					role="combobox"
					aria-expanded={open}
					className={cx(
						"flex w-full select-none items-center justify-between gap-2 rounded-md border px-3 py-2 shadow-sm outline-none transition text-base sm:text-sm",
						"border-border",
						"text-foreground",
						"bg-background",
						"hover:bg-muted",
						"disabled:bg-muted disabled:text-muted-foreground",
						focusInput,
						value.length > 0 ? "h-auto min-h-[38px]" : "h-[38px]",
					)}
				>
					{value.length > 0 ? (
						<div className="flex flex-wrap gap-1 py-0.5">
							{value.map((v) => {
								const option = field.options.find((opt) => opt.value === v);
								return (
									<span
										key={v}
										className={cx(
											"inline-flex items-center gap-1 rounded-md px-2 py-0.5 text-xs font-medium",
											"bg-muted text-foreground",
											"border border-border",
										)}
									>
										{option?.label ?? v}
										<span
											role="button"
											tabIndex={0}
											className={cx(
												"rounded-sm outline-none transition-colors",
												"text-muted-foreground hover:text-foreground",
												"focus-visible:outline-2 focus-visible:outline-primary",
											)}
											onClick={(e) => handleRemove(v, e)}
											onKeyDown={(e) => {
												if (e.key === "Enter" || e.key === " ") {
													handleRemove(v, e as unknown as React.MouseEvent);
												}
											}}
										>
											<X className="size-3" />
											<span className="sr-only">
												Remove {option?.label ?? v}
											</span>
										</span>
									</span>
								);
							})}
						</div>
					) : (
						<span className="text-muted-foreground">
							{field.placeholder || "Select items..."}
						</span>
					)}
					<ChevronsUpDown className="size-4 shrink-0 text-muted-foreground" />
				</button>
			</PopoverTrigger>
			<PopoverContent
				className={cx(
					"p-0 overflow-hidden",
					"min-w-[var(--radix-popover-trigger-width)]",
					"w-[var(--radix-popover-trigger-width)]",
				)}
				align="start"
				sideOffset={8}
			>
				<Command>
					<CommandInput placeholder={field.searchPlaceholder || "Search..."} />
					<CommandList className="max-h-[200px]">
						<CommandEmpty>
							{field.emptyMessage || "No items found."}
						</CommandEmpty>
						<CommandGroup>
							{field.options.map((option) => {
								const isSelected = value.includes(option.value);
								const isDisabled =
									option.disabled ||
									(!isSelected &&
										!!field.maxSelections &&
										value.length >= field.maxSelections);

								return (
									<CommandItem
										key={option.value}
										value={option.value}
										disabled={isDisabled}
										onSelect={() => handleSelect(option.value)}
										className="cursor-pointer"
									>
										<OnboardingCheckbox
											checked={isSelected}
											className="pointer-events-none"
										/>
										{option.label}
									</CommandItem>
								);
							})}
						</CommandGroup>
					</CommandList>
				</Command>
			</PopoverContent>
		</Popover>
	);
}

export function MultiSelectStep({
	title = "Select your options",
	description = "Choose the options that apply.",
	fields,
	onValuesChange,
	onSubmit,
	submitText = "Continue",
	loadingText = "Submitting...",
	backButton,
}: MultiSelectStepProps) {
	const [values, setValues] = useState<Record<string, string[]>>(() => {
		const initial: Record<string, string[]> = {};
		fields.forEach((field) => {
			initial[field.id] = field.defaultValue ?? [];
		});
		return initial;
	});
	const [loading, setLoading] = useState(false);

	const handleChange = (fieldId: string, value: string[]) => {
		const newValues = { ...values, [fieldId]: value };
		setValues(newValues);
		onValuesChange?.(newValues);
	};

	const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
		e.preventDefault();
		setLoading(true);
		try {
			await onSubmit(values);
		} finally {
			setLoading(false);
		}
	};

	const isValid = fields.every((field) => {
		const fieldValue = values[field.id];
		if (field.required && fieldValue.length === 0) return false;
		if (field.minSelections && fieldValue.length < field.minSelections)
			return false;
		return true;
	});

	return (
		<main className="mx-auto p-4">
			<div
				style={{ animationDuration: "500ms" }}
				className="motion-safe:animate-in motion-safe:fade-in motion-safe:slide-in-from-bottom-4"
			>
				<h1 className="text-2xl font-semibold text-foreground sm:text-xl">
					{title}
				</h1>
				<p className="mt-6 text-muted-foreground sm:text-sm">{description}</p>
			</div>

			<form onSubmit={handleSubmit} className="mt-8">
				<div className="flex flex-col gap-6">
					{fields.map((field, index) => (
						<div
							key={field.id}
							className="motion-safe:animate-in motion-safe:fade-in motion-safe:slide-in-from-bottom-4"
							style={{
								animationDuration: "500ms",
								animationDelay: `${100 + index * 100}ms`,
								animationFillMode: "backwards",
							}}
						>
							<OnboardingLabel
								className="mb-2 block text-sm font-medium text-foreground"
								htmlFor={field.id}
							>
								{field.label}
								{field.required && (
									<span className="ml-1 text-destructive">*</span>
								)}
							</OnboardingLabel>

							{field.description && (
								<p className="mb-2 text-sm text-muted-foreground">
									{field.description}
								</p>
							)}

							<MultiSelectInput
								field={field}
								value={values[field.id]}
								onChange={(value) => handleChange(field.id, value)}
							/>

							{field.minSelections &&
								values[field.id].length < field.minSelections && (
									<p className="mt-1 text-xs text-muted-foreground">
										Select at least {field.minSelections} option
										{field.minSelections > 1 ? "s" : ""}
									</p>
								)}

							{field.maxSelections && (
								<p className="mt-1 text-xs text-muted-foreground">
									{values[field.id].length}/{field.maxSelections} selected
								</p>
							)}
						</div>
					))}
				</div>

				<div
					className={cx(
						"mt-8 flex",
						backButton ? "justify-between" : "justify-end",
					)}
				>
					{backButton && (
						<OnboardingButton
							type="button"
							variant="ghost"
							onClick={backButton.onClick}
						>
							{backButton.text}
						</OnboardingButton>
					)}
					<OnboardingButton
						className="disabled:bg-muted disabled:text-muted-foreground"
						type="submit"
						disabled={!isValid || loading}
						aria-disabled={!isValid || loading}
						isLoading={loading}
					>
						{loading ? loadingText : submitText}
					</OnboardingButton>
				</div>
			</form>
		</main>
	);
}
