"use client";

import * as React from "react";
import { Search } from "lucide-react";
import { Controller, type Control, type FieldPath, type FieldValues } from "react-hook-form";

import { formFieldClass, formSelectTriggerClass } from "@/components/form/common/form-layout";
import { Field, FieldError, FieldLabel } from "@/components/ui/field";
import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
  ComboboxList,
  ComboboxTrigger,
} from "@/components/ui/combobox";
import { InputGroupAddon } from "@/components/ui/input-group";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { cn } from "@/lib/utils";

export type SelectOption = string | { value: string; label: string };

type NormalizedOption = { value: string; label: string };

function normalizeOption(option: SelectOption): NormalizedOption {
  if (typeof option === "string") {
    return { value: option, label: option };
  }
  return { value: String(option.value), label: option.label };
}

type SelectFieldProps<T extends FieldValues> = {
  control: Control<T>;
  name: FieldPath<T>;
  label: string;
  placeholder?: string;
  options: readonly SelectOption[];
  className?: string;
  disabled?: boolean;
  /** Filterable dropdown (Select2-style). Use for long lists like asset type / bank. */
  searchable?: boolean;
  searchPlaceholder?: string;
};

function SearchableSelectControl({
  id,
  value,
  onChange,
  placeholder,
  options,
  disabled,
  invalid,
  searchPlaceholder,
}: {
  id: string;
  value: string;
  onChange: (next: string) => void;
  placeholder: string;
  options: NormalizedOption[];
  disabled?: boolean;
  invalid?: boolean;
  searchPlaceholder: string;
}) {
  const selected = React.useMemo(
    () => options.find((option) => option.value === value) ?? null,
    [options, value],
  );

  return (
    <Combobox
      items={options}
      value={selected}
      onValueChange={(next) => {
        onChange(next?.value ?? "");
      }}
      itemToStringLabel={(item) => item.label}
      isItemEqualToValue={(item, current) => item.value === current.value}
      disabled={disabled}
    >
      <ComboboxTrigger
        id={id}
        aria-invalid={invalid}
        className={cn(
          formSelectTriggerClass,
          "flex items-center justify-between gap-1.5 rounded-lg border border-input bg-transparent px-2.5 text-left text-sm outline-none transition-colors",
          "focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50",
          "disabled:cursor-not-allowed disabled:opacity-50",
          "aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20",
          "dark:bg-input/30 dark:hover:bg-input/50",
          !selected && "text-muted-foreground",
        )}
      >
        <span className="min-w-0 flex-1 truncate">{selected?.label ?? placeholder}</span>
      </ComboboxTrigger>
      <ComboboxContent
        side="bottom"
        sideOffset={4}
        align="start"
        className={cn(
          "w-(--anchor-width) min-w-(--anchor-width) overflow-hidden p-0",
          // Override default popup input-group chrome — we style the search row ourselves.
          "*:data-[slot=input-group]:m-0 *:data-[slot=input-group]:mb-0 *:data-[slot=input-group]:h-9",
          "*:data-[slot=input-group]:rounded-md *:data-[slot=input-group]:border-input",
          "*:data-[slot=input-group]:bg-background *:data-[slot=input-group]:shadow-none",
        )}
      >
        <div className="sticky top-0 z-10 border-b border-border/70 bg-muted/40 p-2">
          <ComboboxInput
            placeholder={searchPlaceholder}
            showTrigger={false}
            className="w-full bg-background"
          >
            <InputGroupAddon align="inline-start">
              <Search className="size-3.5 text-muted-foreground" aria-hidden />
            </InputGroupAddon>
          </ComboboxInput>
        </div>
        <ComboboxEmpty>No results found</ComboboxEmpty>
        <ComboboxList className="max-h-60 p-1">
          {(item) => (
            <ComboboxItem key={item.value} value={item}>
              {item.label}
            </ComboboxItem>
          )}
        </ComboboxList>
      </ComboboxContent>
    </Combobox>
  );
}

export function SelectField<T extends FieldValues>({
  control,
  name,
  label,
  placeholder = "Please Select",
  options,
  className,
  disabled,
  searchable = false,
  searchPlaceholder = "Search…",
}: SelectFieldProps<T>) {
  const normalized = options.map(normalizeOption).filter((option) => option.value !== "");

  return (
    <Controller
      control={control}
      name={name}
      render={({ field, fieldState }) => (
        <Field className={cn(formFieldClass, className)} data-invalid={!!fieldState.error}>
          <FieldLabel htmlFor={String(name)}>{label}</FieldLabel>
          {searchable ? (
            <SearchableSelectControl
              id={String(name)}
              value={field.value ? String(field.value) : ""}
              onChange={field.onChange}
              placeholder={placeholder}
              options={normalized}
              disabled={disabled}
              invalid={!!fieldState.error}
              searchPlaceholder={searchPlaceholder}
            />
          ) : (
            <Select
              value={field.value ? String(field.value) : ""}
              onValueChange={field.onChange}
              disabled={disabled}
            >
              <SelectTrigger
                id={String(name)}
                className={formSelectTriggerClass}
                aria-invalid={!!fieldState.error}
              >
                <SelectValue placeholder={placeholder} />
              </SelectTrigger>
              <SelectContent
                position="popper"
                align="start"
                sideOffset={4}
                className="w-(--radix-select-trigger-width)"
              >
                {normalized.map((option) => (
                  <SelectItem key={option.value} value={option.value}>
                    {option.label}
                  </SelectItem>
                ))}
              </SelectContent>
            </Select>
          )}
          {fieldState.error ? <FieldError errors={[fieldState.error]} /> : null}
        </Field>
      )}
    />
  );
}
