/* eslint-disable @typescript-eslint/no-explicit-any */
import { useEffect, useState } from "react";
import Select, { StylesConfig, CSSObjectWithLabel } from "react-select";
import CreatableSelect from "react-select/creatable";
import { getList } from "../../Api/MastersApi";

export interface OptionType {
  value: string | number;
  label: string;
  code?: string;
  name?: string;
  id?: string | number;
}

interface SelectOptionsProps {
  optionsData?: OptionType[];
  skillValue?: OptionType | null;
  apiUrl?: string;
  className?: string;
  name: string;
  id?: string;
  onChange: (
    value: OptionType | OptionType[] | null,
    name: string,
    index?: string | number
  ) => void;
  value?: OptionType | OptionType[] | null;
  required?: boolean;
  isMulti?: boolean;
  label?: string;
  addNew?: boolean;
  stringAdd?: boolean;
  initValue?: string | number | (string | number)[];
  index?: string | number;
  isclear?: boolean;
  isDisabled?: boolean;
  placeholder?: string;
  borderFlag?: boolean;
  indicationFlag?: boolean;
  style?: StylesConfig<OptionType, boolean>;
  classStyleFlag?: boolean;
  any?: boolean;
  divClassName?: string;
  refreshKey?: number;
}

function SelectOptions({
  optionsData,
  skillValue = null,
  apiUrl,
  className = "",
  name,
  id,
  onChange,
  value = null,
  required = false,
  isMulti = false,
  label = "",
  addNew = false,
  stringAdd = false,
  initValue = "",
  index = "",
  isclear = true,
  isDisabled = false,
  placeholder = "",
  style,
  classStyleFlag = false,
  any = false,
  divClassName = "",
  refreshKey = 0,
}: SelectOptionsProps) {
  const [options, setOptions] = useState<OptionType[]>([]);
  const [loading, setLoading] = useState(true);
  const [inputValue, setInputValue] = useState("");
  const [selectedValues, setSelectedValues] = useState<
    OptionType | OptionType[] | null
  >(value || (isMulti ? [] : null));

  const Multiselect = addNew  ? CreatableSelect : Select;

  const formatLabel = (opt: OptionType): string => {
    return opt.code && opt.name && opt.code !== opt.name
      ? `${opt.code} - ${opt.name}`
      : opt.label || opt.name || opt.code || String(opt.value);
  };

  useEffect(() => {
    setSelectedValues(value);
  }, [value]);

  useEffect(() => {
    if (optionsData) {
      setOptions(optionsData);
      setLoading(false);
      return;
    }

    if (!apiUrl) {
      setOptions([]);
      setLoading(false);
      return;
    }

    setLoading(true);
    getList(`${apiUrl}`, { search: inputValue })
      .then((response) => {
        const { status, data } = response;
        setLoading(false);
        if (status) {
          const arr = Array.isArray(data) ? data : [];
          const initialOptions: OptionType[] = arr.map(
            (option: { id: string | number; name: string; code?: string }) => ({
              value: option.id,
              label: formatLabel({
                value: option.id,
                label: option.name,
                code: option.code,
                name: option.name,
                id: option.id,
              }),
              code: option.code,
              name: option.name,
              id: option.id,
            })
          );

          if (initValue !== "" && initValue !== undefined) {
            const initValues = Array.isArray(initValue)
              ? initValue
              : [initValue];
            initValues.forEach((val) => {
              if (!initialOptions.some((option) => option.value === val)) {
                initialOptions.push({
                  value: val,
                  label: String(val),
                });
              }
            });
          }

          setOptions(initialOptions);
        }
      })
      .catch((error) => {
        setLoading(false);
        console.error("Error fetching data:", error);
      });
  }, [apiUrl, initValue, name, optionsData, refreshKey, inputValue]);

  useEffect(() => {
    if (skillValue) {
      setOptions((prev) => {
        if (!prev.some((opt) => opt.value === skillValue.value)) {
          return [...prev, skillValue];
        }
        return prev;
      });
    }
  }, [skillValue]);

  const selectAllOption: OptionType = {
    value: !any ? 0 : options.length ? "__ANY__" : 0,
    label: "Any",
  };

  const isAnySelected = () => {
    if (!any) return false;
    if (isMulti) {
      return Array.isArray(selectedValues)
        ? selectedValues.some((v) => v.value === selectAllOption.value)
        : false;
    }
    return (selectedValues as OptionType | null)?.value === selectAllOption.value;
  };

  const mappedOptions = options.map((option) => ({
    ...option,
    label: formatLabel(option),
    id: option.value,
  }));

  const mappedValue = (() => {
    if (isMulti) {
      if (any && isAnySelected()) {
        return [selectAllOption];
      }
      return Array.isArray(selectedValues)
        ? selectedValues.map((val) => ({
            ...val,
            label: formatLabel(val),
            id: val.value,
          }))
        : [];
    } else if (selectedValues) {
      if (any && isAnySelected()) {
        return selectAllOption;
      }
      return {
        ...selectedValues,
        label: formatLabel(selectedValues as OptionType),
        id: (selectedValues as OptionType).value,
      };
    }
    return null;
  })();

  const handleCreateOption = (inputVal: string) => {
    const exists = options.some(
      (opt) => opt.value?.toString().toLowerCase() === inputVal.toLowerCase()
    );
    if (exists) return;

    const newOption: OptionType = {
      value: inputVal,
      label: inputVal,
    };

    setOptions((prev) => [...prev, newOption]);

    if (isMulti) {
      const updated = Array.isArray(selectedValues)
        ? [...selectedValues, newOption]
        : [newOption];
      setSelectedValues(updated);
      onChange(updated, name, index);
    } else {
      setSelectedValues(newOption);
      onChange(newOption, name, index);
    }
  };
  const handleCreateOptionString = (inputVal: string) => {
    const newOption: OptionType = {
      value: inputVal,
      label: inputVal,
      code: inputVal,
      name: inputVal,
      id: inputVal
    };

    setOptions((prev) => [...prev, newOption]);

    if (isMulti) {
      const updated = Array.isArray(selectedValues)
        ? [...selectedValues, newOption]
        : [newOption];
      setSelectedValues(updated);
      onChange(updated, name, index);
    } else {
      setSelectedValues(newOption);
      onChange(newOption, name, index);
    }
  };
  const commonProps: any = {
    name,
    id,
    options: any ? [selectAllOption, ...mappedOptions] : mappedOptions,
    value: mappedValue,
    noOptionsMessage: () => (stringAdd ? null : 'No options'),
    onChange: (selected: unknown) => {
      setSelectedValues(selected as OptionType | OptionType[] | null);
      onChange(selected as OptionType | OptionType[] | null, name, index);
    },
    onInputChange: (val: string) => {
      setInputValue(val);
      return val;
    },
    onKeyDown: (e: React.KeyboardEvent) => {
      // if (stringAdd && (e.key === 'Enter' || e.key === 'Tab')) {
      if (stringAdd && (e.key === 'Enter')) {
        e.preventDefault(); // Prevent default behavior like form submission
        if (inputValue.trim()) {
          handleCreateOptionString(inputValue.trim());
        }
      }
    },
    onBlur: () => {
      if (stringAdd && inputValue.trim()) {
        handleCreateOptionString(inputValue.trim());
      }
    },
    
    onMenuClose: () => {
      if (stringAdd && inputValue.trim()) {
        handleCreateOptionString(inputValue.trim());
      }
    },
    isValidNewOption: () => addNew,
    onCreateOption: addNew ? handleCreateOption : undefined,
    required,
    className: className + " multiple-select01",
    styles: {
      control: (base: CSSObjectWithLabel) => ({
        ...base,
        minHeight: "28px",
        fontSize: "12px",
        padding: "0px 5px 0px 30px",
        border: "1px solid #d1d5db",
        borderRadius: "6px",
      }),
      valueContainer: (base: CSSObjectWithLabel) => ({
        ...base,
        padding: "2px 4px",
      }),
      input: (base: CSSObjectWithLabel) => ({
        ...base,
        fontSize: "12px",
      }),
      dropdownIndicator: (base: CSSObjectWithLabel) => ({
        ...base,
        padding: "2px",
      }),
      clearIndicator: (base: CSSObjectWithLabel) => ({
        ...base,
        padding: "2px",
      }),
      option: (base: CSSObjectWithLabel, state: any) => ({
        ...base,
        backgroundColor: state.isFocused ? "#f3f4f6" : "white",
        color: state.isFocused ? "#1f2937" : "#374151",
        cursor: "pointer",
      }),
      ...style,
    },
    placeholder,
    isMulti,
    isClearable: isclear,
    isDisabled,
    isLoading: loading,
    menuIsOpen: undefined,
    getOptionLabel: formatLabel,
    getOptionValue: (opt: OptionType) =>
      opt.id !== undefined ? String(opt.id) : String(opt.value),
    filterOption: (
      option: { label: string | React.ReactElement; data: OptionType },
      input: string
    ) => {
      const label =
        typeof option.label === "string" ? option.label.toLowerCase() : "";
      const value = option.data?.code?.toString().toLowerCase() || "";
      const name = option.data?.name?.toLowerCase() || "";
      const search = input.toLowerCase();
      return (
        label.includes(search) ||
        value.includes(search) ||
        name.includes(search)
      );
    },
    formatCreateLabel:  addNew
      ? (inputVal: string) => `Add "${inputVal}" as a new ${label}`
      : undefined,
  };

  return (
    <>
      {classStyleFlag && <style>{`.css-1nmdiq5-menu {}`}</style>}
      <div className={divClassName}>
        <Multiselect {...commonProps} />
      </div>
    </>
  );
}

export default SelectOptions;
