"use client";

import * as React from "react";

import { Plus } from "lucide-react";
import {
  useFormContext,
  useWatch,
  type Control,
  type FieldPath,
  type FieldValues,
} from "react-hook-form";
import { toast } from "sonner";

import { createBankClient } from "@/app/customer/_lib/create-bank-client";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { NativeSelect, NativeSelectOption } from "@/components/ui/native-select";
import {
  Popover,
  PopoverContent,
  PopoverTrigger,
} from "@/components/ui/popover";

import { SelectField, type SelectOption } from "./select-field";

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

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

function mergeSelectOptions(
  options: readonly SelectOption[],
  extras: CreatedOption[],
): CreatedOption[] {
  const byValue = new Map<string, CreatedOption>();
  for (const option of options) {
    const normalized = normalizeSelectOption(option);
    if (!normalized.value) continue;
    byValue.set(normalized.value, normalized);
  }
  for (const extra of extras) {
    if (!extra.value) continue;
    byValue.set(extra.value, extra);
  }
  return [...byValue.values()];
}

function QuickAddBankButton({
  currencyOptions,
  onCreated,
}: {
  currencyOptions: readonly SelectOption[];
  onCreated: (option: CreatedOption) => void;
}) {
  const currencies = React.useMemo(
    () => currencyOptions.map(normalizeSelectOption).filter((option) => option.value),
    [currencyOptions],
  );

  const [open, setOpen] = React.useState(false);
  const [reportingCurrencyId, setReportingCurrencyId] = React.useState("");
  const [bankName, setBankName] = React.useState("");
  const [error, setError] = React.useState<string | null>(null);
  const [saving, setSaving] = React.useState(false);

  function resetForm() {
    setReportingCurrencyId("");
    setBankName("");
    setError(null);
  }

  async function handleSave() {
    const trimmedName = bankName.trim();
    if (!trimmedName) {
      setError("Bank name is required.");
      return;
    }
    if (!reportingCurrencyId) {
      setError("Please select Reporting Currency.");
      return;
    }

    setSaving(true);
    setError(null);

    try {
      const created = await createBankClient({
        name: trimmedName,
        reportingCurrencyId,
      });
      onCreated(created);
      toast.success("Bank created");
      resetForm();
      setOpen(false);
    } catch (caught) {
      setError(caught instanceof Error ? caught.message : "Bank could not be created.");
    } finally {
      setSaving(false);
    }
  }

  return (
    <Popover
      open={open}
      onOpenChange={(next) => {
        setOpen(next);
        if (next) {
          resetForm();
        } else {
          setError(null);
          setBankName("");
        }
      }}
    >
      <PopoverTrigger asChild>
        <Button
          type="button"
          variant="outline"
          size="icon-xs"
          className="text-primary"
          aria-label="Add bank"
          title="Add bank"
        >
          <Plus className="size-3.5" />
        </Button>
      </PopoverTrigger>
      <PopoverContent align="end" className="w-80 space-y-2">
        <NativeSelect
          value={reportingCurrencyId}
          onChange={(event) => setReportingCurrencyId(event.target.value)}
          disabled={saving || currencies.length === 0}
          aria-label="Reporting currency"
        >
          <NativeSelectOption value="">
            {currencies.length === 0 ? "No currencies" : "Please Select"}
          </NativeSelectOption>
          {currencies.map((currency) => (
            <NativeSelectOption key={currency.value} value={currency.value}>
              {currency.label}
            </NativeSelectOption>
          ))}
        </NativeSelect>
        <div className="flex gap-2">
          <Input
            value={bankName}
            onChange={(event) => setBankName(event.target.value)}
            placeholder="Bank"
            disabled={saving}
            onKeyDown={(event) => {
              if (event.key === "Enter") {
                event.preventDefault();
                void handleSave();
              }
            }}
          />
          <Button type="button" size="sm" disabled={saving} onClick={() => void handleSave()}>
            {saving ? "Saving…" : "Save"}
          </Button>
        </div>
        {error ? <p className="text-destructive text-xs">{error}</p> : null}
      </PopoverContent>
    </Popover>
  );
}

type BankSelectFieldProps<T extends FieldValues> = {
  control: Control<T>;
  name?: FieldPath<T>;
  label?: string;
  options: readonly SelectOption[];
  /** Reporting currencies for Yii1 insert_bank (required with bank name). */
  currencyOptions?: readonly SelectOption[];
  disabled?: boolean;
  className?: string;
  searchPlaceholder?: string;
};

export function BankSelectField<T extends FieldValues>({
  control,
  name = "bank" as FieldPath<T>,
  label = "Bank",
  options,
  currencyOptions = [],
  disabled,
  className,
  searchPlaceholder = "Search bank…",
}: BankSelectFieldProps<T>) {
  const { setValue } = useFormContext<T>();
  const selectedValue = String(useWatch({ control, name }) ?? "").trim();
  const [extraOptions, setExtraOptions] = React.useState<CreatedOption[]>([]);

  const mergedOptions = React.useMemo(() => {
    const merged = mergeSelectOptions(options, extraOptions);
    if (selectedValue && !merged.some((option) => option.value === selectedValue)) {
      merged.push({ value: selectedValue, label: selectedValue });
    }
    return merged;
  }, [extraOptions, options, selectedValue]);

  return (
    <SelectField
      control={control}
      name={name}
      label={label}
      options={mergedOptions}
      searchable
      searchPlaceholder={searchPlaceholder}
      disabled={disabled}
      className={className}
      labelAction={
        // Keep + available on edit even when the select value is locked/read-only.
        <QuickAddBankButton
          currencyOptions={currencyOptions}
          onCreated={(option) => {
            setExtraOptions((current) =>
              current.some((item) => item.value === option.value)
                ? current
                : [...current, option],
            );
            setValue(name, option.value as T[typeof name], {
              shouldDirty: true,
              shouldValidate: true,
              shouldTouch: true,
            });
          }}
        />
      }
    />
  );
}
