import { useColorContext } from "@/src/contexts/ColorContext";
import { useScreenContext } from "@/src/contexts/ScreenContext";
import { IInputSelectProps } from "@/types/formInterfaces";
import { compararArraysDeObjetosPorLabel } from "@/utils";
import { motion } from "framer-motion";
import { useEffect, useRef, useState } from "react";
import { Controller } from "react-hook-form";
import Select, { GroupBase, OptionsOrGroups } from "react-select";
import makeAnimated from "react-select/animated";
import CreatableSelect from "react-select/creatable";
import { twMerge } from "tailwind-merge";
import * as yup from "yup";
import { Skeleton } from "../ui/skeleton";

const InputSelect = ({
  name,
  label,
  width,
  options: newOptions,
  formulario,
  creatable,
  onChange,
  isMulti = false,
  defaultValue,
  required,
  disabled,
  menuPlacement = "auto",
  skipEffect = false,
  minimumCharacter = false,
  captureInputChange,
  removeDocumentBody = false,
  error,
  placeholder,
  dynamic = false,
  textSize = "text-md",
  labelSize = "text-md",
  formatOptionLabel,
  schema,
  menuPositionFixed = false,
  validateCreateOption,
}: IInputSelectProps) => {
  const SelectComponent = creatable ? CreatableSelect : Select;
  const [darkTheme, setDarkTheme] = useState(false);
  const { onDrawer } = useScreenContext();
  const [options, setOptions] = useState<
    OptionsOrGroups<any, GroupBase<any>> | { value: string; label: string }[]
  >([]);
  const animatedComponents = makeAnimated();
  const { colorMode } = useColorContext();
  const appliedDefaultRef = useRef(false);

  useEffect(() => {
    if (
      compararArraysDeObjetosPorLabel(newOptions as any[], options as any[]) ||
      newOptions?.length == 0
    ) {
      setOptions(newOptions);
    }
  }, [newOptions]); // eslint-disable-line

  useEffect(() => {
    if (formulario && name != undefined) {
      const newSchema = formulario.yupSchema.fields;

      if (isMulti) {
        newSchema[name] = required
          ? yup
              .array()
              .of(yup.string())
              .min(1, "É necessário selecionar pelo menos um")
              .required(error)
          : yup.array().of(yup.string());
      } else {
        newSchema[name] = required
          ? yup.string().required(error)
          : yup.string();
      }

      newSchema[name] = schema || newSchema[name];
      formulario.setYupSchema(yup.object().shape(newSchema));
    }

    return () => {
      if (dynamic && formulario?.control && name) {
        formulario.control.unregister([name] as never[], {
          keepValue: false,
          keepError: false,
        });
      }
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  const normalizeDefault = (defVal: any): string | string[] | undefined => {
    if (defVal === undefined || defVal === null || defVal === "")
      return undefined;

    if (isMulti) {
      if (Array.isArray(defVal)) {
        return defVal.map((v: any) =>
          typeof v === "object" ? String(v?.value) : String(v)
        );
      }
      if (typeof defVal === "string") {
        return defVal
          .split(",")
          .map((s) => s.trim())
          .filter(Boolean);
      }
      return [String(defVal)];
    } else {
      if (typeof defVal === "object") return String(defVal?.value);
      return String(defVal);
    }
  };

  const getSelectValueFromForm = (formValue: any) => {
    if (isMulti) {
      const valuesArr: string[] = Array.isArray(formValue)
        ? formValue.map((v: any) =>
            typeof v === "object" ? String(v?.value) : String(v)
          )
        : [];
      return options.filter((opt: any) =>
        valuesArr.includes(String(opt.value))
      );
    } else {
      const valueStr =
        formValue === null || formValue === undefined || formValue === ""
          ? undefined
          : typeof formValue === "object"
            ? String(formValue?.value)
            : String(formValue);
      return valueStr
        ? (options as any).find((opt: any) => String(opt.value) === valueStr) ||
            null
        : null;
    }
  };

  const handleChangeToForm = (
    fieldOnChange: (v: any) => void,
    selectedOption: any
  ) => {
    if (isMulti) {
      let selected = Array.isArray(selectedOption) ? selectedOption : [];
      const hasStar = selected.some((o) => String(o?.value) === "*");
      if (hasStar) {
        const last = selected[selected.length - 1];
        if (String(last?.value) === "*") {
          selected = options.filter(
            (o: any) => String(o.value) === "*"
          ) as any[];
        } else {
          selected = selected.filter((o) => String(o?.value) !== "*");
        }
      }

      const valueForForm = selected.map((o: any) => String(o.value));
      fieldOnChange(valueForForm);
      onChange && onChange(selectedOption || []);
    } else {
      const valueForForm = selectedOption
        ? String(selectedOption.value)
        : undefined;
      fieldOnChange(valueForForm);
      onChange && onChange(selectedOption || undefined);
    }
  };

  return (
    <div className={`${width ? width : "w-full"} relative`}>
      {label && (
        <label
          htmlFor={name}
          className={twMerge(
            `mb-[10px] labelInputDynamic text-black dark:text-white whitespace-nowrap flex flex-row`,
            labelSize
          )}
        >
          {typeof label == "string"
            ? label
                .split(" ")
                .map((str: string) =>
                  str.length > 3 ? str[0].toUpperCase() + str.slice(1) : str
                )
                .join(" ")
            : label}{" "}
          <span className="text-[#ff2b2b] font-semibold ">
            {required && "*"}
          </span>
        </label>
      )}
      <div className="relative">
        {name !== undefined && formulario !== undefined ? (
          <Controller
            name={name || ""}
            control={formulario.control}
            render={({ field }) => {
              // useEffect(() => {
              //   if (appliedDefaultRef.current) return;
              //   if (!options || !options.length) return;

              //   const current = field.value;
              //   const isEmpty = isMulti
              //     ? !Array.isArray(current) || current.length === 0
              //     : current === undefined || current === null || current === "";

              //   if (isEmpty && defaultValue !== undefined) {
              //     const normalized = normalizeDefault(defaultValue);
              //     if (normalized !== undefined) {
              //       field.onChange(normalized);
              //       appliedDefaultRef.current = true;
              //     }
              //   }
              // }, [defaultValue, options, field.value]); // eslint-disable-line

              const selectValue = getSelectValueFromForm(field.value);

              return (
                <SelectComponent
                  menuPosition={menuPositionFixed ? "fixed" : "absolute"}
                  isClearable
                  styles={{
                    option: (provided, state) => ({
                      ...provided,
                      backgroundColor:
                        colorMode == "dark" ? "#1d2a39" : "#fff !important",
                      color: colorMode == "dark" ? "#fff" : "inherit",
                      padding: "3px 10px !important",
                    }),
                    menuPortal: (base) => ({
                      ...base,
                      zIndex: 9999999999,
                      backgroundColor:
                        colorMode == "dark"
                          ? "#1d2a39 !important"
                          : "#fff !important",
                      fontSize: "13px !important",
                    }),
                  }}
                  menuPortalTarget={
                    onDrawer ? null : removeDocumentBody ? null : document.body
                  }
                  className={`${textSize} relative w-full text-black dark:text-white font-medium appearance-none rounded bg-transparent outline-none transition focus:border-primary active:border-primary dark:border-form-strokedark dark:focus:border-primary`}
                  options={options as any}
                  noOptionsMessage={({ inputValue }) =>
                    minimumCharacter
                      ? "Digite ao menos 3 letras"
                      : formulario?.errors && name && formulario.errors[name]
                        ? String(formulario.errors[name]?.message || "")
                            .split(" ")
                            .map((str: string) =>
                              str.length > 3
                                ? str[0].toUpperCase() + str.slice(1)
                                : str
                            )
                            .join(" ")
                        : "Nenhum resultado"
                  }
                  placeholder={placeholder || "Selecione uma opção"}
                  isMulti={isMulti}
                  menuPlacement={menuPlacement}
                  isDisabled={disabled}
                  value={selectValue as any}
                  onChange={(selected: any) =>
                    handleChangeToForm(field.onChange, selected)
                  }
                  onInputChange={(e) => {
                    if (minimumCharacter && captureInputChange) {
                      captureInputChange(e);
                    }
                    return e;
                  }}
                  onCreateOption={(inputValue: string) => {
                    if (!creatable) return;

                    const existingOption = (options as any[]).find(
                      (option: any) =>
                        option.label?.toLowerCase?.() ===
                          inputValue.toLowerCase() ||
                        String(option.value).toLowerCase() ===
                          inputValue.toLowerCase()
                    );

                    const applySelect = (opt: any) => {
                      if (isMulti) {
                        const curr = Array.isArray(selectValue)
                          ? (selectValue as any[])
                          : [];
                        const unique = [...curr, opt].filter(
                          (v, i, arr) =>
                            arr.findIndex(
                              (o) => String(o.value) === String(v.value)
                            ) === i
                        );
                        handleChangeToForm(field.onChange, unique);
                      } else {
                        handleChangeToForm(field.onChange, opt);
                      }
                    };

                    if (existingOption) {
                      applySelect(existingOption);
                      return;
                    }

                    let newOption = { label: inputValue, value: inputValue };

                    if (validateCreateOption && creatable) {
                      const validated = validateCreateOption(
                        inputValue,
                        options as any[]
                      );
                      if (!validated) return;
                      newOption = validated;
                    }

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

                    applySelect(newOption);
                  }}
                  formatOptionLabel={formatOptionLabel}
                />
              );
            }}
          />
        ) : (
          <Skeleton className="relative w-full h-[37px] css-t3ipsp-control" />
        )}
      </div>
      {formulario?.errors && name && formulario.errors[name] && (
        <motion.span
          className="text-danger text-sm"
          initial={{ opacity: 0, y: -5 }}
          animate={{ opacity: 1, y: 0 }}
          exit={{ opacity: 0, y: -5 }}
          transition={{ duration: 0.2 }}
        >
          {String(formulario.errors[name]?.message || "")
            .split(" ")
            .map((str: string) =>
              str.length > 3 ? str[0].toUpperCase() + str.slice(1) : str
            )
            .join(" ")}
        </motion.span>
      )}
    </div>
  );
};

export default InputSelect;
