"use client";

import * as React from "react";
import { Check, ChevronsUpDown, LucideIcon } from "lucide-react";

import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import {
  Command,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
} from "@/components/ui/command";
import {
  Popover,
  PopoverContent,
  PopoverTrigger,
} from "@/components/ui/popover";

export interface AutocompleteOption {
  value: string;
  label?: string;
  badge?: string;
  isDefault?: boolean;
}

interface AutocompleteProps {
  value: string;
  onChange: (value: string) => void;
  options: (string | AutocompleteOption)[];
  placeholder?: string;
  searchPlaceholder?: string;
  emptyMessage?: string;
  className?: string;
  disabled?: boolean;
  icon?: LucideIcon;
  allowCreate?: boolean;
  createLabel?: (value: string) => string;
  "data-testid"?: string;
  itemTestIdPrefix?: string;
}

function normalizeOption(opt: string | AutocompleteOption): AutocompleteOption {
  if (typeof opt === "string") {
    return { value: opt, label: opt };
  }
  return { ...opt, label: opt.label ?? opt.value };
}

export function Autocomplete({
  value,
  onChange,
  options,
  placeholder = "Select an option...",
  searchPlaceholder = "Search...",
  emptyMessage = "No results found.",
  className,
  disabled = false,
  icon: Icon,
  allowCreate = false,
  createLabel = (v) => `Create "${v}"`,
  "data-testid": testId,
  itemTestIdPrefix = "option",
}: AutocompleteProps) {
  const [open, setOpen] = React.useState(false);
  const [inputValue, setInputValue] = React.useState("");
  const [triggerWidth, setTriggerWidth] = React.useState<number>(0);
  const triggerRef = React.useRef<HTMLButtonElement>(null);

  const normalizedOptions = React.useMemo(
    () => options.map(normalizeOption),
    [options]
  );

  // Update trigger width when component mounts or value changes
  React.useEffect(() => {
    if (triggerRef.current) {
      const updateWidth = () => {
        setTriggerWidth(triggerRef.current?.offsetWidth || 0);
      };

      updateWidth();

      const resizeObserver = new ResizeObserver(updateWidth);
      resizeObserver.observe(triggerRef.current);

      return () => {
        resizeObserver.disconnect();
      };
    }
  }, [value]);

  // Filter options based on input
  const filteredOptions = React.useMemo(() => {
    if (!inputValue) return normalizedOptions;
    const lower = inputValue.toLowerCase();
    return normalizedOptions.filter(
      (opt) =>
        opt.value.toLowerCase().includes(lower) ||
        opt.label?.toLowerCase().includes(lower)
    );
  }, [normalizedOptions, inputValue]);

  // Check if user typed a new value that doesn't exist
  const isNewValue =
    allowCreate &&
    inputValue.trim() &&
    !normalizedOptions.some(
      (opt) => opt.value.toLowerCase() === inputValue.toLowerCase()
    );

  // Get display value
  const displayValue = React.useMemo(() => {
    if (!value) return null;
    const found = normalizedOptions.find((opt) => opt.value === value);
    return found?.label ?? value;
  }, [value, normalizedOptions]);

  return (
    <Popover open={open} onOpenChange={setOpen}>
      <PopoverTrigger asChild>
        <Button
          ref={triggerRef}
          variant="outline"
          role="combobox"
          aria-expanded={open}
          disabled={disabled}
          className={cn(
            "w-full justify-between",
            Icon && "font-mono text-sm",
            className
          )}
          data-testid={testId}
        >
          <span className="flex items-center gap-2 truncate">
            {Icon && (
              <Icon className="w-4 h-4 shrink-0 text-muted-foreground" />
            )}
            {displayValue || placeholder}
          </span>
          <ChevronsUpDown className="opacity-50 shrink-0" />
        </Button>
      </PopoverTrigger>
      <PopoverContent
        className="p-0"
        style={{
          width: Math.max(triggerWidth, 200),
        }}
        data-testid={testId ? `${testId}-list` : undefined}
      >
        <Command shouldFilter={false}>
          <CommandInput
            placeholder={searchPlaceholder}
            className="h-9"
            value={inputValue}
            onValueChange={setInputValue}
          />
          <CommandList>
            <CommandEmpty>
              {isNewValue ? (
                <div className="py-2 px-3 text-sm">
                  Press enter to create{" "}
                  <code className="bg-muted px-1 rounded">{inputValue}</code>
                </div>
              ) : (
                emptyMessage
              )}
            </CommandEmpty>
            <CommandGroup>
              {/* Show "Create new" option if typing a new value */}
              {isNewValue && (
                <CommandItem
                  value={inputValue}
                  onSelect={() => {
                    onChange(inputValue);
                    setInputValue("");
                    setOpen(false);
                  }}
                  className="text-[var(--status-success)]"
                  data-testid={`${itemTestIdPrefix}-create-new`}
                >
                  {Icon && <Icon className="w-4 h-4 mr-2" />}
                  {createLabel(inputValue)}
                  <span className="ml-auto text-xs text-muted-foreground">
                    (new)
                  </span>
                </CommandItem>
              )}
              {filteredOptions.map((option) => (
                <CommandItem
                  key={option.value}
                  value={option.value}
                  onSelect={(currentValue) => {
                    onChange(currentValue === value ? "" : currentValue);
                    setInputValue("");
                    setOpen(false);
                  }}
                  data-testid={`${itemTestIdPrefix}-${option.value.toLowerCase().replace(/[\s/\\]+/g, "-")}`}
                >
                  {Icon && <Icon className="w-4 h-4 mr-2" />}
                  {option.label}
                  <Check
                    className={cn(
                      "ml-auto",
                      value === option.value ? "opacity-100" : "opacity-0"
                    )}
                  />
                  {option.badge && (
                    <span className="ml-2 text-xs text-muted-foreground">
                      ({option.badge})
                    </span>
                  )}
                </CommandItem>
              ))}
            </CommandGroup>
          </CommandList>
        </Command>
      </PopoverContent>
    </Popover>
  );
}
