"use client";

import { Check, ChevronsUpDown } from "lucide-react";
import { useMemo, useState } from "react";

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";
import { cn } from "@/lib/utils";
import { FactorioIcon } from "./factorio-icon";

export type SearchPickerOption = {
  value: string;
  label: string;
  icon: string;
  meta?: string;
  keywords?: string;
  group?: string;
  subgroup?: string;
  subgroupLabel?: string;
};

export type SearchPickerGroup = { id: string; label: string; icon: string };

type SearchPickerProps = {
  value: string;
  options: SearchPickerOption[];
  onChange: (value: string) => void;
  placeholder: string;
  searchPlaceholder?: string;
  emptyText?: string;
  groups?: SearchPickerGroup[];
  triggerOption?: SearchPickerOption;
  triggerContent?: React.ReactNode;
  triggerClassName?: string;
};

export function SearchPicker({
  value,
  options,
  onChange,
  placeholder,
  searchPlaceholder = "Search…",
  emptyText = "No match found.",
  groups = [],
  triggerOption,
  triggerContent,
  triggerClassName = "",
}: SearchPickerProps) {
  const [open, setOpen] = useState(false);
  const [search, setSearch] = useState("");
  const optionByValue = useMemo(
    () => new Map(options.map((option) => [option.value, option])),
    [options],
  );
  const selected =
    optionByValue.get(value) ??
    (triggerOption?.value === value ? triggerOption : undefined);
  const selectableGroups = useMemo(
    () => new Set(options.map((option) => option.group).filter(Boolean)),
    [options],
  );
  const preferredGroup =
    selected?.group && selectableGroups.has(selected.group)
      ? selected.group
      : options[0]?.group ?? groups[0]?.id ?? "";
  const [activeGroup, setActiveGroup] = useState(
    preferredGroup,
  );
  const resolvedActiveGroup = selectableGroups.has(activeGroup)
    ? activeGroup
    : preferredGroup;
  const visibleOptions = useMemo(
    () =>
      resolvedActiveGroup
        ? options.filter((option) => option.group === resolvedActiveGroup)
        : options,
    [options, resolvedActiveGroup],
  );
  const optionSections = useMemo(() => {
    const sections = new Map<string, SearchPickerOption[]>();
    for (const option of visibleOptions) {
      const key = option.subgroupLabel ?? option.subgroup ?? "";
      const entries = sections.get(key) ?? [];
      entries.push(option);
      sections.set(key, entries);
    }
    return [...sections.entries()];
  }, [visibleOptions]);

  return (
    <Popover open={open} onOpenChange={setOpen}>
      <PopoverTrigger asChild>
        <Button
          variant="outline"
          role="combobox"
          aria-label={searchPlaceholder}
          aria-expanded={open}
          className={`${triggerContent ? "h-auto min-h-12 flex-wrap py-2.5" : "h-12"} w-full justify-between border-white/10 bg-[#17191b] px-3 text-left font-normal hover:bg-[#1d2023] ${triggerClassName}`}
        >
          {triggerContent ?? (selected ? (
            <span className="flex min-w-0 items-center gap-2">
              <FactorioIcon src={selected.icon} alt="" size="md" />
              <span className="whitespace-normal break-words text-[15px] leading-tight text-zinc-100">{selected.label}</span>
            </span>
          ) : (
            <span className="text-zinc-500">{placeholder}</span>
          ))}
          <ChevronsUpDown className="size-4 shrink-0 text-zinc-500" />
        </Button>
      </PopoverTrigger>
      <PopoverContent
        align="start"
        className="w-[var(--radix-popover-trigger-width)] border-white/10 bg-[#17191b] p-0 text-zinc-100"
      >
        <Command className="bg-transparent" filter={(valueToFilter, search) => {
          const option = optionByValue.get(valueToFilter);
          const haystack = `${option?.label ?? ""} ${option?.keywords ?? ""} ${option?.meta ?? ""}`.toLowerCase();
          return haystack.includes(search.toLowerCase()) ? 1 : 0;
        }}>
          <CommandInput value={search} onValueChange={setSearch} placeholder={searchPlaceholder} className="text-zinc-100" />
          {groups.length ? (
            <div className="scrollbar-none flex gap-1 overflow-x-auto border-b border-white/8 px-2 pb-2" role="tablist">
              {groups.map((group) => (
                <button
                  type="button"
                  key={group.id}
                  role="tab"
                  aria-selected={resolvedActiveGroup === group.id}
                  title={group.label}
                  onClick={() => { setSearch(""); setActiveGroup(group.id); }}
                  className={`flex shrink-0 items-center gap-1.5 rounded-md border px-2 py-1.5 transition ${
                    resolvedActiveGroup === group.id
                      ? "border-[#f7a72d]/55 bg-[#f7a72d]/10"
                      : "border-white/7 bg-white/3 hover:border-white/20"
                  }`}
                >
                  <FactorioIcon src={group.icon} alt={group.label} size="md" />
                </button>
              ))}
            </div>
          ) : null}
          <CommandList className="max-h-[360px]">
            <CommandEmpty>{emptyText}</CommandEmpty>
            {optionSections.map(([subgroup, entries]) => (
            <CommandGroup key={subgroup || "all"} heading={subgroup || undefined}>
              {entries.map((option) => (
                <CommandItem
                  key={option.value}
                  value={option.value}
                  onSelect={() => {
                    onChange(option.value);
                    if (option.group) setActiveGroup(option.group);
                    setOpen(false);
                  }}
                  className="min-h-11 data-[selected=true]:bg-[#292d31]"
                >
                  <FactorioIcon src={option.icon} alt="" size="md" />
                  <span className="min-w-0 flex-1 whitespace-normal break-words leading-tight">{option.label}</span>
                  {option.meta ? (
                    <span className="shrink-0 font-mono text-[10px] uppercase tracking-wider text-zinc-500">
                      {option.meta}
                    </span>
                  ) : null}
                  <Check className={cn("size-4 text-[#f7a72d]", value === option.value ? "opacity-100" : "opacity-0")} />
                </CommandItem>
              ))}
            </CommandGroup>
            ))}
          </CommandList>
        </Command>
      </PopoverContent>
    </Popover>
  );
}
