import React, { FC, useCallback, useEffect, useMemo, useRef, useState } from "react";
import SelectRS from "react-select";
import { customStyles as baseSelectStyles } from "../Select/Select.types";
import { Label } from "..";
import { SelectAsyncProps } from "./types";
import "./SelectAsync.scss";

export type SelectAsyncOption = Record<string, any>;

const useDebouncedCallback = (cb: (v: string) => void, delay: number) => {
  const timerRef = useRef<number | null>(null);
  return useCallback((val: string) => {
    if (timerRef.current) window.clearTimeout(timerRef.current);
    timerRef.current = window.setTimeout(() => cb(val), delay);
  }, [cb, delay]);
};

export const SelectAsync: FC<SelectAsyncProps> = ({
  name = "select-async",
  label,
  placeholder = "Select",
  isMultiple = false,
  isClearable = false,
  isDisabled = false,
  pageSize = 20,
  textField = "label",
  valueField = "value",
  selectedState,
  value,
  onChange,
  loadOptions,
  showSelection = true,
  pillColor = "default-blue",
  pillVariant = "filled",
  className,
  style,
  hideSelectedOptions = false,
  noOptionsMessage,
  loadOnOpen = true,
  onFocus,
  onBlur,
  showPills = false,
}) => {
  const [options, setOptions] = useState<SelectAsyncOption[]>([]);
  const [isLoading, setIsLoading] = useState(false);
  const [hasMore, setHasMore] = useState(true);
  const [cursor, setCursor] = useState<any>(undefined);
  const [page, setPage] = useState(1);
  const [menuIsOpen, setMenuIsOpen] = useState(false);
  const [inputValue, setInputValue] = useState("");

  // Controlled selection source
  const controlledValue = useMemo(() => {
    if (selectedState) return selectedState[0];
    return value;
  }, [selectedState, value]);

  const setControlledValue = useCallback((newVal: any) => {
    if (selectedState) {
      const [, setSelected] = selectedState;
      setSelected(newVal);
    }
    if (onChange) onChange(newVal);
  }, [selectedState, onChange]);

  const resetAndLoad = useCallback(async (query: string) => {
    // Si loadOnOpen es false y el query está vacío, no cargar opciones
    if (!loadOnOpen && query.trim() === "") {
      setOptions([]);
      setHasMore(true);
      setCursor(undefined);
      setPage(1);
      return;
    }

    setIsLoading(true);
    setPage(1);
    setCursor(undefined);
    try {
      const res = await loadOptions({ search: query || undefined, page: 1, pageSize });
      setOptions(res.options || []);
      setHasMore(!!res.hasMore);
      setCursor(res.nextCursor);
    } finally {
      setIsLoading(false);
    }
  }, [loadOptions, pageSize, loadOnOpen]);

  const loadMore = useCallback(async () => {
    if (isLoading || !hasMore) return;
    setIsLoading(true);
    try {
      const nextPage = page + 1;
      const res = await loadOptions({ search: inputValue || undefined, page: nextPage, pageSize, cursor });
      setOptions(prev => [...prev, ...(res.options || [])]);
      setHasMore(!!res.hasMore);
      setCursor(res.nextCursor);
      setPage(nextPage);
    } finally {
      setIsLoading(false);
    }
  }, [isLoading, hasMore, loadOptions, page, pageSize, inputValue, cursor]);

  const debouncedSearch = useDebouncedCallback((q) => {
    resetAndLoad(q);
  }, 400);

  // Efecto para mantener opciones vacías cuando loadOnOpen es false y no hay input
  useEffect(() => {
    if (!loadOnOpen && inputValue.trim() === "" && options.length > 0) {
      setOptions([]);
      setHasMore(true);
      setCursor(undefined);
      setPage(1);
    }
  }, [loadOnOpen, inputValue, options.length]);

  // Open menu → initial load if empty and loadOnOpen is true
  const handleMenuOpen = useCallback(() => {
    setMenuIsOpen(true);
    if (loadOnOpen && options.length === 0) {
      resetAndLoad("");
    } else if (!loadOnOpen) {
      // Si loadOnOpen es false, asegurar que las opciones estén vacías
      setOptions([]);
      setHasMore(true);
      setCursor(undefined);
      setPage(1);
      setInputValue(""); // También limpiar el input value
    }
  }, [loadOnOpen, options.length, resetAndLoad]);

  const handleMenuClose = useCallback(() => {
    setMenuIsOpen(false);
  }, []);

  const handleInputChange = useCallback((val: string, meta: any) => {
    // react-select fires 'input-change' for typing
    if (meta?.action === "input-change") {
      setInputValue(val);
      // Si el input está vacío, limpiar las opciones inmediatamente
      if (val.trim() === "") {
        setOptions([]);
        setHasMore(true);
        setCursor(undefined);
        setPage(1);
      } else {
        debouncedSearch(val);
      }
    }
    return val;
  }, [debouncedSearch]);

  const handleChange = useCallback((newVal: any) => {
    setControlledValue(newVal);
  }, [setControlledValue]);

  const handleFocus = useCallback(() => {
    if (onFocus) {
      onFocus();
    }
  }, [onFocus]);

  const handleBlur = useCallback(() => {
    if (onBlur) {
      onBlur();
    }
  }, [onBlur]);

  const styles = baseSelectStyles; // reuse existing MUI-like styles


  // Custom component for multi-value display (pills inside the input)
  const MultiValue = (props: any) => {
    const { data, removeProps } = props;

    const handleRemove = (e: any) => {
      e.preventDefault();
      e.stopPropagation();
      if (removeProps?.onClick) {
        removeProps.onClick(e);
      }
    };

    // Get pill colors based on pillColor prop - using same logic as Pill component
    const getPillColors = () => {
      let color = "#03A9F4"; // default-blue
      if (pillColor === "default-green") {
        color = "#005BF8";
      } else if (pillColor === "default-blue") {
        color = "#03A9F4";
      } else if (pillColor === "default") {
        color = "#F1F1F1";
      }

      let textColor, backgroundColor, borderColor = "";
      if (pillVariant === "filled") {
        textColor = color === "#F1F1F1" ? "black" : "white";
        backgroundColor = color;
        borderColor = color;
      } else {
        textColor = color;
        backgroundColor = "white";
        borderColor = color;
      }

      return { textColor, backgroundColor, borderColor };
    };

    const colors = getPillColors();

    return (
      <div
        className="gtc-rc-search-async__multi-value"
        style={{
          backgroundColor: colors.backgroundColor,
          border: `1px solid ${colors.borderColor}`,
          color: colors.textColor
        }}
      >
        <span className="gtc-rc-search-async__multi-value__label">
          {data?.[textField]}
        </span>
        <button
          type="button"
          className="gtc-rc-search-async__multi-value__remove-button"
          onClick={handleRemove}
          style={{
            color: colors.textColor
          }}
          onMouseDown={(e) => e.preventDefault()}
        >
          ×
        </button>
      </div>
    );
  };

  return (
    <div className="gtc-rc-search-async" style={style} data-testid="gtc-rc-select-async">
      {label && (
        <div className="gtc-rc-search-async__label">
          <Label>{label}</Label>
        </div>
      )}
      <SelectRS
        className={className}
        classNamePrefix="gtc-rc-search-async__react-select"
        name={name}
        placeholder={placeholder}
        isDisabled={isDisabled}
        isClearable={isClearable}
        isLoading={isLoading}
        isSearchable
        isMulti={isMultiple}
        options={options}
        value={controlledValue}
        onChange={handleChange}
        onMenuOpen={handleMenuOpen}
        onMenuClose={handleMenuClose}
        onMenuScrollToBottom={() => {
          if (hasMore) loadMore();
        }}
        onInputChange={handleInputChange}
        onFocus={handleFocus}
        onBlur={handleBlur}
        styles={styles}
        getOptionLabel={(opt) => (opt?.[textField] ?? "")}
        getOptionValue={(opt) => String(opt?.[valueField] ?? "")}
        controlShouldRenderValue={(!showPills && isMultiple) ? false : showSelection}
        hideSelectedOptions={hideSelectedOptions}
        noOptionsMessage={noOptionsMessage}
        menuIsOpen={menuIsOpen}
        closeMenuOnSelect={!isMultiple}
        components={(showPills && isMultiple) ? { MultiValue } : { MultiValue: () => null }}
        menuPortalTarget={document.body}
        menuPosition="fixed"
      />
    </div>
  );
};

export default SelectAsync;
