import React, { useEffect, useRef, useState } from 'react';
import { Form, Select, Checkbox, Tag, Typography } from 'antd';
import type { RuleObject } from 'rc-field-form/lib/interface';
import useDebounce from '@/hooks/useDebounce';
type RuleConfig = RuleObject;

interface SearchableSelectProps {
  options: Array<{ value: number | string; label: React.ReactNode }>;
  selectedOption?: { value: number | string; label: React.ReactNode } | null;
  onSelect?: (
    value: number | string,
    item?: { value: number | string; label: React.ReactNode },
  ) => void;
  onChange?: (
    value: string | number,
    item:
      | { value: number | string; label: React.ReactNode }
      | Array<{ value: number | string; label: React.ReactNode }>
  ) => void;
  isSearchable?: boolean;
  name: string;
  label?: string;
  value?: number | string | Array<number | string>;
  loading?: boolean;
  rules?: RuleConfig[] | undefined;
  placeholder: string;
  onSearch?: (query: string) => void;
  mode?: 'multiple' | undefined;
  handleLoadMore?: () => void;
  open?: boolean;
  onDropdownVisibleChange?: (open: boolean) => void;
  /** When true, onSearch fires only on Enter key (or when cleared), not on every keystroke */
  searchOnEnter?: boolean;
  [key: string]: any;
  hideSelectAll?: boolean;

}


const InputAutoComplete: React.FC<SearchableSelectProps> = ({
  name,
  onSearch,
  label,
  rules,
  options,
  selectedOption,
  value,
  onSelect,
  onChange,
  isSearchable,
  loading = false,
  mode,
  placeholder,
  handleLoadMore,
  open,
  onDropdownVisibleChange,
  extra,
  searchOnEnter,
  hideSelectAll = false,
  ...rest // Collect additional props
}) => {
  const [searchQuery, setSearchQuery] = useState<string>('');
  const optionLabelCache = useRef<Map<number | string, string>>(new Map());

  const debouncedSearchQuery = useDebounce(searchQuery, 500);
  const isMultiple = mode === 'multiple';
  const loadMoreTimeout = useRef<NodeJS.Timeout | null>(null);
  const controlledValue = selectedOption ?? value;
  // Reflects the option(s) actually selected, used to drive the checkbox in each option's label.
  // The Select's real value lives in the Form store once wrapped by Form.Item with `name`,
  // which overrides the `value` prop seen here, so it can't be read directly from props.
  const watchedFormValue = Form.useWatch(name);
  const formInstance = Form.useFormInstance();
  const [selectedValues, setSelectedValues] = useState<Array<number | string>>(
    Array.isArray(watchedFormValue) ? watchedFormValue : Array.isArray(value) ? value : []
  );
  useEffect(() => {
    if (Array.isArray(watchedFormValue)) {
      setSelectedValues(watchedFormValue);
    }
  }, [watchedFormValue]);

  const allOptionValues = options?.map((opt) => opt.value) ?? [];
  const isAllSelected = isMultiple && allOptionValues.length > 0 && allOptionValues.every((v) => selectedValues.includes(v));

  const handleSelectAllToggle = (): void => {
    const nextValues = isAllSelected ? [] : allOptionValues;
    setSelectedValues(nextValues);
    formInstance?.setFieldValue(name, nextValues);
    if (onChange) {
      onChange(nextValues as any, options?.filter((opt) => nextValues.includes(opt.value)) ?? []);
    }
  };
  useEffect(() => {
    if (onSearch !== undefined && !searchOnEnter) {
      onSearch(debouncedSearchQuery);
    }
  }, [debouncedSearchQuery]);

  const handleSearch = (value: string): void => {
    setSearchQuery(value);
    // Clear immediately resets server-side results without waiting for Enter
    if (searchOnEnter && value === '' && onSearch) {
      onSearch('');
    }
  };

    const handlePopupScroll = (e: React.UIEvent<HTMLDivElement>): void => {
      e.stopPropagation();

      const { currentTarget } = e;

      if (
        currentTarget.scrollTop + currentTarget.clientHeight >=
        currentTarget.scrollHeight - 5
      ) {
        if (handleLoadMore) {
          if (loadMoreTimeout.current) return;

          loadMoreTimeout.current = setTimeout(() => {
            handleLoadMore();
            loadMoreTimeout.current = null;
          }, 300);
        }
      }
    };

  const formattedOptions = options?.map((opt) => {
    const rawLabel = typeof opt.label === 'string' ? opt.label : '';
    // Cache every label we see so tags remain readable when options list changes
    // (e.g. after a search or load-more replaces the current page of results)
    if (rawLabel) {
      optionLabelCache.current.set(opt.value, rawLabel);
    }
    return {
      ...opt,
      label: isMultiple ? (
        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
          <Checkbox checked={selectedValues.includes(opt.value)} />
          <span>{opt.label}</span>
        </div>
      ) : (
        opt.label
      ),
      rawLabel,
    };
  });

  const tagRender = isMultiple
    ? (props: any) => {
        const { value: tagValue, closable, onClose } = props;
        const option = formattedOptions?.find((opt) => opt.value === tagValue);
        // Fall back to cache when the option is no longer in the current page/search results
        const displayLabel = option?.rawLabel ?? optionLabelCache.current.get(tagValue) ?? String(tagValue);
        return (
          <Tag
            closable={closable}
            onClose={onClose}
            style={{
              marginRight: 3,
              backgroundColor: '#e6e6e6',
              color: 'rgba(0,0,0,0.88)',
              borderColor: '#c8c8c8',
              fontSize: '90%',
            }}
          >
            {displayLabel}
          </Tag>
        );
      }
    : undefined;

  return (
    <div style={{ position: 'relative' }}>
      {isMultiple &&  !hideSelectAll  && options && options.length > 0 && (
        // Positioned out of flow so it doesn't add row height and push the control
        // down relative to the label column (which stretches to the row's full height).
        <div style={{ position: 'absolute', top: -20, right: 0, zIndex: 1 }}>
          <Typography.Link style={{ fontSize: 12 }} onClick={handleSelectAllToggle}>
            {isAllSelected ? 'Clear All' : 'Select All'}
          </Typography.Link>
        </div>
      )}
      <Form.Item label={label} name={name} rules={rules} extra={extra} {...rest}>
        <Select<any, any>
          onPopupScroll={handlePopupScroll}
          defaultActiveFirstOption={false}
          loading={loading}
          getPopupContainer={() => document.body}
          dropdownStyle={{ zIndex: 2000 }}
          mode={mode}
          showSearch={isSearchable}
          placeholder={placeholder}
          menuItemSelectedIcon={null}
          labelInValue={!!selectedOption && !isMultiple}
          optionLabelProp="label"
          value={controlledValue}
          tagRender={tagRender}
          filterOption={(input, option) => {
            const rawLabel = (option as any)?.rawLabel ?? '';
            return rawLabel.toLowerCase().includes(input.toLowerCase());
          }}
          onSearch={handleSearch}
          onKeyDown={(e: React.KeyboardEvent<HTMLDivElement>) => {
            if (searchOnEnter && e.key === 'Enter' && searchQuery) {
              e.preventDefault();
              e.stopPropagation();

              if (onSearch) {
                onSearch(searchQuery);
              }
            }
          }}
          onSelect={(selectedValue, option) => {
            const normalizedValue = typeof selectedValue === 'object' && selectedValue !== null
              ? (selectedValue as any)?.value
              : selectedValue;
            if (onSelect) {
              onSelect(normalizedValue, option);
            }
          }}
          onChange={(changedValue, option) => {
            let normalizedValue;
            if (Array.isArray(changedValue)) {
              // multiple mode — pass the array of ids as-is
              normalizedValue = changedValue;
              // Deselecting an option (array shrank) doesn't clear the server-search results on its own — the dropdown would otherwise keep showing only the last searched-for option(s). Reset back to the unfiltered list.
              const isRemoval = changedValue.length < selectedValues.length;
              setSelectedValues(changedValue);
              if (isRemoval && searchOnEnter && onSearch) {
                setSearchQuery('');
                onSearch('');
              }
            } else if (typeof changedValue === 'object' && changedValue !== null) {
              normalizedValue = (changedValue as any)?.value;
            } else {
              normalizedValue = changedValue;
            }
            if (onChange) {
              onChange(normalizedValue, option as any);
            }
          }}
          options={formattedOptions}
          allowClear={true}
          open={open}
          onDropdownVisibleChange={onDropdownVisibleChange}
          {...rest}
        />
      </Form.Item>
    </div>
  );
};

export default InputAutoComplete;
