import { CheckIcon, ChevronsUpDown } from "lucide-react";
import * as React from "react";
import * as RPNInput from "react-phone-number-input";
import flags from "react-phone-number-input/flags";

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 { ScrollArea } from "@/components/ui/scroll-area";
import { cn } from "@/lib/utils";

type PhoneInputProps = Omit<
  React.ComponentProps<"input">,
  "onChange" | "value" | "ref"
> &
  Omit<RPNInput.Props<typeof RPNInput.default>, "onChange"> & {
    onChange?: (value: RPNInput.Value) => void;
  };

const PhoneInput: React.ForwardRefExoticComponent<PhoneInputProps> =
  React.forwardRef<React.ElementRef<typeof RPNInput.default>, PhoneInputProps>(
    ({ className, onChange, value, ...props }, ref) => {
      // Memoize onChange handler to prevent recreating on every render
      const handleChange = React.useCallback(
        (value: RPNInput.Value | undefined) => {
          onChange?.(value || ("" as RPNInput.Value));
        },
        [onChange],
      );

      const wrapperClassName = cn(
        // Keep the phone UI stable across pages by owning the border/focus state
        // at the wrapper level (instead of relying on nested inputs).
        "flex w-full overflow-hidden rounded-lg border border-input bg-background transition-[border-color,box-shadow] focus-within:border-ring focus-within:ring-[3px] focus-within:ring-ring/50",
        className,
      );

      return (
        <div dir="ltr" className={wrapperClassName}>
          <RPNInput.default
            ref={ref}
            className="flex w-full"
            flagComponent={MemoizedFlagComponent}
            countrySelectComponent={CountrySelect}
            inputComponent={PhoneNumberField}
            smartCaret={false}
            value={value || undefined}
            onChange={handleChange}
            {...props}
          />
        </div>
      );
    },
  );
PhoneInput.displayName = "PhoneInput";

const PhoneNumberField = React.memo(
  React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
    ({ className, ...props }, ref) => (
      <input
        ref={ref}
        {...props}
        inputMode="tel"
        dir="ltr"
        className={cn(
          "h-10 min-w-0 flex-1 bg-transparent px-3 py-2 text-base leading-6 outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
          className,
        )}
      />
    ),
  ),
);
PhoneNumberField.displayName = "PhoneNumberField";

type CountryEntry = { label: string; value: RPNInput.Country | undefined };

type CountrySelectProps = {
  disabled?: boolean;
  value: RPNInput.Country;
  options: CountryEntry[];
  onChange: (country: RPNInput.Country) => void;
};

const CountrySelect = React.memo(
  ({
    disabled,
    value: selectedCountry,
    options: countryList,
    onChange,
  }: CountrySelectProps) => {
    const [searchValue, setSearchValue] = React.useState("");
    const [isOpen, setIsOpen] = React.useState(false);

    // Memoize callbacks to prevent child re-renders
    const handleOpenChange = React.useCallback((open: boolean) => {
      setIsOpen(open);
      if (open) {
        setSearchValue("");
      }
    }, []);

    const handleSearchChange = React.useCallback((value: string) => {
      setSearchValue(value);
    }, []);

    const handleSelectComplete = React.useCallback(() => {
      setIsOpen(false);
    }, []);

    // Filter countries based on search (memoized)
    const filteredCountries = React.useMemo(() => {
      if (!searchValue) return countryList;
      const search = searchValue.toLowerCase();
      return countryList.filter(({ label }) =>
        label?.toLowerCase().includes(search),
      );
    }, [countryList, searchValue]);

    return (
      <Popover open={isOpen} modal onOpenChange={handleOpenChange}>
        <PopoverTrigger asChild>
          <Button
            type="button"
            variant="ghost"
            className={cn(
              "h-10 rounded-none border-e border-input bg-transparent px-3 hover:bg-accent focus-visible:ring-0 focus-visible:border-transparent",
            )}
            disabled={disabled}
          >
            <MemoizedFlagComponent
              country={selectedCountry}
              countryName={selectedCountry}
            />
            {!disabled && (
              <ChevronsUpDown className="-me-1 size-4 opacity-60" />
            )}
          </Button>
        </PopoverTrigger>
        <PopoverContent dir="ltr" className="w-[360px] p-0" align="start">
          <Command>
            <CommandInput
              value={searchValue}
              onValueChange={handleSearchChange}
              placeholder="Search country..."
            />
            <CommandList>
              <ScrollArea className="h-72">
                <CommandEmpty>No country found.</CommandEmpty>
                <CommandGroup>
                  {filteredCountries.map(({ value, label }) =>
                    value ? (
                      <CountrySelectOption
                        key={value}
                        country={value}
                        countryName={label}
                        selectedCountry={selectedCountry}
                        onChange={onChange}
                        onSelectComplete={handleSelectComplete}
                      />
                    ) : null,
                  )}
                </CommandGroup>
              </ScrollArea>
            </CommandList>
          </Command>
        </PopoverContent>
      </Popover>
    );
  },
);
CountrySelect.displayName = "CountrySelect";

interface CountrySelectOptionProps extends RPNInput.FlagProps {
  selectedCountry: RPNInput.Country;
  onChange: (country: RPNInput.Country) => void;
  onSelectComplete: () => void;
}

// Memoized to prevent re-renders when list scrolls
const CountrySelectOption = React.memo(
  ({
    country,
    countryName,
    selectedCountry,
    onChange,
    onSelectComplete,
  }: CountrySelectOptionProps) => {
    const handleSelect = React.useCallback(() => {
      onChange(country);
      onSelectComplete();
    }, [country, onChange, onSelectComplete]);

    const isSelected = country === selectedCountry;

    return (
      <CommandItem className="gap-2" onSelect={handleSelect}>
        <MemoizedFlagComponent country={country} countryName={countryName} />
        <span className="flex-1 text-sm">{countryName}</span>
        <span className="text-sm tabular-nums">{`+${RPNInput.getCountryCallingCode(country)}`}</span>
        <CheckIcon
          className={cn(
            "ms-auto size-4",
            isSelected ? "opacity-100" : "opacity-0",
          )}
        />
      </CommandItem>
    );
  },
);
CountrySelectOption.displayName = "CountrySelectOption";

// Memoized flag component to prevent re-renders
const FlagComponent = ({ country, countryName }: RPNInput.FlagProps) => {
  const Flag = flags[country];

  return (
    <span
      className={cn(
        "inline-flex h-5 w-7 items-center justify-center overflow-hidden rounded-md bg-muted ring-1 ring-border/60 shadow-xs",
      )}
    >
      {Flag ? (
        <span className="[&_svg]:block [&_svg]:h-full [&_svg]:w-full">
          <Flag title={countryName} />
        </span>
      ) : (
        <span className="h-2.5 w-2.5 rounded-full bg-foreground/30" />
      )}
    </span>
  );
};

const MemoizedFlagComponent = React.memo(FlagComponent);
MemoizedFlagComponent.displayName = "FlagComponent";

export { PhoneInput };
