import React, {
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
} from 'react';
import { Box } from '@wix/design-system';
import {
  CellReferenceInput,
  type CellReferenceInputHandle,
  type CellReferenceItem,
} from '@wix/patterns-fields';
import type { CellEditProps } from '../../types';
import { useCloseOnAncestorScroll } from '../../../../hooks/useCloseOnAncestorScroll';
import { useFilterCollectionStates } from '../../../../hooks/useFilterCollectionStates';
import { FilterTranslationProvider } from '../../../../providers';
import type { ReferenceItem, ReferenceTypeConfig } from './types';
import { getImageFieldId } from './getImageFieldId';
import { useCollection } from '@wix/bex-core/react';
import { observer } from 'mobx-react-lite';
import { useOpenReferencedCollection } from './openCollectionContext';

type ReferenceValue = Record<string, unknown> | null;

function _ReferenceEditContent({
  value,
  onChange,
  onCommit,
  onCancel,
  typeConfig,
  editTrigger,
  inputRef,
}: CellEditProps<ReferenceValue>) {
  const config = typeConfig as ReferenceTypeConfig | undefined;
  const refSchema = config?.refSchema;
  const acHandle = useRef<CellReferenceInputHandle | null>(null);
  const wrapperRef = useRef<HTMLDivElement | null>(null);
  // Opener provided by ReferenceEditWrapper (a stable ancestor) so the
  // navigation + return-to-foreground subscription survive this editor
  // unmounting when the overlay opens.
  const openReferencedCollection = useOpenReferencedCollection();

  const collection = useCollection<ReferenceItem>({
    queryName: `reference-cell-edit-${refSchema?.id ?? 'unknown'}`,
    itemKey: (item) => item.id,
    itemName: (item) => item.name,
    fetchData: async (query) => {
      if (!config?.fetchReferenceItems) {
        return { items: [], total: 0 };
      }
      return config.fetchReferenceItems(query);
    },
  });

  const initialSearchTextRef = useRef(
    editTrigger &&
      typeof editTrigger === 'object' &&
      editTrigger.kind === 'type'
      ? editTrigger.key
      : undefined,
  );
  // True once the user has typed into the search input. After that, displayValue
  // follows searchValue directly (even when empty) rather than falling back to
  // selectedDisplayValue — prevents the input from snapping back after backspace.
  // useState (not useRef) so that setIsSearchActive(true) triggers a re-render
  // even when collection.search('') produces no MobX change (searchValue already '').
  const [isSearchActive, setIsSearchActive] = useState(
    initialSearchTextRef.current !== undefined,
  );

  useEffect(() => {
    const dispose = collection.init();
    if (initialSearchTextRef.current !== undefined) {
      collection.search(initialSearchTextRef.current, {
        clearResult: true,
      });
    }
    return dispose;
  }, [collection]);

  const keyedItems = collection.result.keyedItems;
  const fetchedLastPage = collection.fetchedLastPage;
  const searchValue = collection.query.search.value;

  const idField = refSchema?.idField ?? '_id';
  const displayField = refSchema?.displayField ?? 'title';
  const imageField = getImageFieldId(refSchema?.fields);

  const selectedDisplayValue = value ? String(value[displayField] ?? '') : '';
  const displayValue = isSearchActive
    ? searchValue
    : searchValue || selectedDisplayValue;

  const selectedImage = useMemo(() => {
    const selectedId = value?.[idField];
    if (!selectedId) {
      return undefined;
    }
    const foundItem = keyedItems.find((ki) => ki.key === String(selectedId));
    if (foundItem?.item.image) {
      return foundItem.item.image;
    }
    if (imageField) {
      return value?.[imageField] as string | undefined;
    }
    return undefined;
  }, [value, idField, keyedItems, imageField]);

  const selectedId = value ? String(value[idField] ?? '') : '';
  const selectedItem =
    selectedDisplayValue && !searchValue && selectedId && !isSearchActive
      ? { id: selectedId, name: selectedDisplayValue, image: selectedImage }
      : undefined;

  const items = useMemo(
    () =>
      keyedItems.map((ki) => ({
        id: String(ki.key),
        name: ki.item.name,
        image: ki.item.image,
      })),
    [keyedItems],
  );

  const handleSelect = useCallback(
    (item: CellReferenceItem) => {
      onChange({
        [idField]: item.id,
        [displayField]: item.name,
        ...(imageField && item.image ? { [imageField]: item.image } : {}),
      });
      collection.clearSearch();
      onCommit();
    },
    [idField, displayField, imageField, onChange, onCommit, collection],
  );

  const handleChange = useCallback(
    (e: React.ChangeEvent<HTMLInputElement>) => {
      setIsSearchActive(true);
      if (e.target.value) {
        collection.search(e.target.value, { clearResult: true });
      } else {
        collection.clearSearch();
      }
    },
    [collection],
  );

  const handleClear = useCallback(() => {
    onChange(null);
    collection.clearSearch();
    onCommit();
  }, [onChange, onCommit, collection]);

  const handleKeyDown = useCallback(
    (e: React.KeyboardEvent) => {
      if (e.key === 'Escape') {
        e.preventDefault();
        e.stopPropagation();
        onCancel();
      } else if (
        (e.key === 'Backspace' || e.key === 'Delete') &&
        value &&
        !displayValue &&
        !isSearchActive
      ) {
        onChange(null);
        collection.clearSearch();
        onCommit();
      }
    },
    [
      onCancel,
      onChange,
      onCommit,
      value,
      displayValue,
      collection,
      isSearchActive,
    ],
  );

  const closeDropdown = useCallback(() => acHandle.current?.hideOptions(), []);
  useCloseOnAncestorScroll(wrapperRef, closeDropdown, true);

  const { showInitialLoader, fixedFooter } = useFilterCollectionStates({
    collection,
    fixedFooter: config?.renderFooter?.(),
  });

  // Refresh this editor's dropdown options when the overlay closes — force
  // invalidates the shared query cache (keyed by queryName), so even after this
  // editor has unmounted the next open refetches fresh.
  const handleOpenCollection = useMemo(
    () =>
      openReferencedCollection
        ? () =>
            openReferencedCollection(() =>
              collection.clearResultAndMoveToStart({ force: true }),
            )
        : undefined,
    [openReferencedCollection, collection],
  );

  if (!refSchema) {
    return null;
  }

  return (
    <Box ref={wrapperRef} width="100%" height="100%">
      <CellReferenceInput
        ref={acHandle}
        items={items}
        value={displayValue}
        selectedItem={selectedItem}
        onChange={handleChange}
        onSelect={handleSelect}
        onClear={handleClear}
        onKeyDown={handleKeyDown}
        clearButton={!!displayValue}
        infiniteScroll
        hasMore={!showInitialLoader && !fetchedLastPage}
        loadMore={collection.fetchNextPageIfNeeded}
        fixedFooter={fixedFooter}
        onBlur={onCommit}
        onOpenCollection={handleOpenCollection}
        autoOpen={!!editTrigger}
        inputRef={inputRef}
      />
    </Box>
  );
}

const ReferenceEditContent = observer(_ReferenceEditContent);

export const ReferenceEdit = (props: CellEditProps<ReferenceValue>) => (
  <FilterTranslationProvider>
    <ReferenceEditContent {...props} />
  </FilterTranslationProvider>
);
