import { useCallback, useEffect, useMemo, useState } from 'react';
import type { Dispatch, SetStateAction } from 'react';
import {
  TAG_FILTER_UNTAGGED,
  resolveDocumentRowKey,
  isDocumentRowKey,
} from './appLayoutUtils';

type Identifier = string | number;

type DocumentLike = { id?: Identifier } & Record<string, unknown>;

type ApiClient = {
  get: <T = unknown>(url: string, config?: { params?: Record<string, unknown> }) => Promise<{ data: T }>;
};

type AssetManagerLike = {
  hydrateDocuments: (payload: unknown[]) => DocumentLike[];
};

type SelectionHelpers = {
  setSelectedEntries: (
    updater:
      | Array<Identifier | string>
      | ((prev: Array<Identifier | string>) => Array<Identifier | string>),
  ) => void;
  setSelectionOrder: (
    updater:
      | Array<Identifier | string>
      | ((prev: Array<Identifier | string>) => Array<Identifier | string>),
  ) => void;
  selectionOrderRef: { current: Array<Identifier | string> };
  selectionAnchorRef: { current: Identifier | string | null };
  setFocusedDocumentId: (
    updater: Identifier | null | ((prev: Identifier | null) => Identifier | null),
  ) => void;
};

interface UseDocumentsSearchArgs {
  api: ApiClient;
  assetManager: AssetManagerLike;
  token?: string | null;
  selectedFolder?: Identifier | 'root' | null;
  navigate?: (path: string, options?: { replace?: boolean }) => void;
  locationPathname?: string;
  isDocumentsRoute?: boolean;
  selectionHelpers: SelectionHelpers;
  searchIncludeDescendants?: boolean;
  documentsSortField?: string;
  documentsSortDirection?: string;
  notifyApiError: (error: unknown, message: string) => void;
  setLoading: (state: boolean) => void;
  setSearchIncludeDescendants: (value: boolean) => void;
}

interface UseDocumentsSearchResult {
  searchQuery: string;
  setSearchQuery: Dispatch<SetStateAction<string>>;
  searchResults: DocumentLike[] | null;
  setSearchResults: Dispatch<SetStateAction<DocumentLike[] | null>>;
  searchLoading: boolean;
  setSearchLoading: Dispatch<SetStateAction<boolean>>;
  activeTagFilters: Identifier[];
  setActiveTagFilters: Dispatch<SetStateAction<Identifier[]>>;
  activeCorrespondentFilters: Identifier[];
  setActiveCorrespondentFilters: Dispatch<SetStateAction<Identifier[]>>;
  toggleTagFilter: (tagId: Identifier) => void;
  toggleCorrespondentFilter: (correspondentId?: Identifier | null) => void;
  isFilterActive: boolean;
  clearFilters: () => void;
  handleSearchChange: (value: string) => void;
  handleSearchSubmit: () => void;
}

const useDocumentsSearch = ({
  api,
  assetManager,
  token,
  selectedFolder,
  navigate,
  locationPathname,
  isDocumentsRoute,
  selectionHelpers,
  searchIncludeDescendants,
  documentsSortField,
  documentsSortDirection,
  notifyApiError,
  setLoading,
  setSearchIncludeDescendants,
}: UseDocumentsSearchArgs): UseDocumentsSearchResult => {
  const [searchQuery, setSearchQuery] = useState<string>('');
  const [activeTagFilters, setActiveTagFilters] = useState<Identifier[]>([]);
  const [activeCorrespondentFilters, setActiveCorrespondentFilters] = useState<Identifier[]>([]);
  const [searchResults, setSearchResults] = useState<DocumentLike[] | null>(null);
  const [searchLoading, setSearchLoading] = useState<boolean>(false);

  const toggleTagFilter = useCallback((tagId: Identifier) => {
    if (!tagId) return;
    setActiveTagFilters((previous) => {
      if (tagId === TAG_FILTER_UNTAGGED) {
        return previous.includes(TAG_FILTER_UNTAGGED) ? [] : [TAG_FILTER_UNTAGGED];
      }
      const sanitized = previous.filter((id) => id !== TAG_FILTER_UNTAGGED);
      if (sanitized.includes(tagId)) {
        return sanitized.filter((id) => id !== tagId);
      }
      return sanitized.concat([tagId]);
    });
  }, []);

  const toggleCorrespondentFilter = useCallback((correspondentId?: Identifier | null) => {
    setActiveCorrespondentFilters((previous) => {
      if (!correspondentId) {
        return [];
      }
      return previous.includes(correspondentId) ? [] : [correspondentId];
    });
  }, []);

  const isFilterActive = useMemo(
    () =>
      searchQuery.trim().length > 0
      || activeTagFilters.length > 0
      || activeCorrespondentFilters.length > 0,
    [searchQuery, activeTagFilters, activeCorrespondentFilters],
  );

  const clearFilters = useCallback(() => {
    setSearchQuery('');
    setActiveTagFilters([]);
    setActiveCorrespondentFilters([]);
    setSearchLoading(false);
    setSearchIncludeDescendants(true);
  }, [
    setSearchIncludeDescendants,
  ]);

  const handleSearchChange = useCallback((value: string) => {
    setSearchQuery(value);
  }, []);

  const handleSearchSubmit = useCallback(() => {
    if (!navigate) return;
    const targetFolder = selectedFolder && selectedFolder !== 'root' ? selectedFolder : 'root';
    const targetPath = targetFolder === 'root' ? '/documents' : `/documents/folder/${targetFolder}`;
    if (!isDocumentsRoute || locationPathname !== targetPath) {
      navigate(targetPath, { replace: false });
    }
  }, [navigate, selectedFolder, isDocumentsRoute, locationPathname]);

  useEffect(() => {
    if (!token) return undefined;

    if (!isFilterActive) {
      setSearchResults(null);
      setSearchLoading(false);
      return undefined;
    }

    let cancelled = false;
    let started = false;
    setSearchLoading(true);

    const debounce = setTimeout(async () => {
      started = true;
      setLoading(true);
      try {
        const params: Record<string, unknown> = {};
        const trimmedQuery = searchQuery.trim();
        if (trimmedQuery.length) {
          params.query = trimmedQuery;
        }
        if (activeTagFilters.length) {
          const onlyUntagged = activeTagFilters.length === 1
            && activeTagFilters[0] === TAG_FILTER_UNTAGGED;
          if (onlyUntagged) {
            params.tags = 'none';
          } else {
            const tagIds = activeTagFilters.filter((id) => id !== TAG_FILTER_UNTAGGED);
            if (tagIds.length) {
              params.tags = tagIds.join(',');
            }
          }
        }
        if (activeCorrespondentFilters.length) {
          params.correspondents = activeCorrespondentFilters.join(',');
        }
        const folderIdentifier = selectedFolder === 'root' ? null : selectedFolder;
        if (folderIdentifier) {
          params.folder_id = folderIdentifier;
        }
        if (!searchIncludeDescendants) {
          params.include_descendants = false;
        }
        if (documentsSortField) {
          params.sort = documentsSortField;
        }
        if (documentsSortDirection) {
          params.dir = documentsSortDirection;
        }
        const { data } = await api.get<unknown[]>('/documents', { params });
        if (cancelled) return;

        const results = assetManager.hydrateDocuments(data || []);
        setSearchResults(results);

        if (!results.length) {
          setSearchLoading(false);
          selectionHelpers.setSelectedEntries([]);
          selectionHelpers.setFocusedDocumentId(null);
          selectionHelpers.selectionOrderRef.current = [];
          selectionHelpers.setSelectionOrder([]);
          selectionHelpers.selectionAnchorRef.current = null;
          return;
        }

        const resultKeys = results
          .map((doc) => resolveDocumentRowKey(doc.id as Identifier))
          .filter(Boolean) as Array<Identifier | string>;

        let targetKey: Identifier | string | null = null;
        let nextSelectionKeys: Array<Identifier | string> = [];

        selectionHelpers.setSelectedEntries((previous) => {
          const previousDocKeys = previous.filter(isDocumentRowKey);
          const filtered = previousDocKeys.filter((key) => resultKeys.includes(key));
          if (filtered.length) {
            targetKey = filtered[filtered.length - 1];
            nextSelectionKeys = filtered;
            return filtered;
          }
          targetKey = null;
          nextSelectionKeys = [];
          return [];
        });

        selectionHelpers.selectionOrderRef.current = nextSelectionKeys;
        selectionHelpers.setSelectionOrder(nextSelectionKeys);

        selectionHelpers.setFocusedDocumentId((previous) => {
          if (previous && resultKeys.includes(resolveDocumentRowKey(previous as Identifier))) {
            return previous;
          }
          return null;
        });

        selectionHelpers.selectionAnchorRef.current = targetKey;
      } catch (error) {
        if (cancelled) return;
        notifyApiError(error, 'Search failed. Please try again.');
        setSearchResults(null);
      } finally {
        if (!cancelled && started) {
          setLoading(false);
          setSearchLoading(false);
        }
      }
    }, 300);

    return () => {
      cancelled = true;
      clearTimeout(debounce);
      if (started) {
        setLoading(false);
        setSearchLoading(false);
      }
    };
  }, [
    api,
    token,
    isFilterActive,
    searchQuery,
    activeTagFilters,
    activeCorrespondentFilters,
    searchIncludeDescendants,
    documentsSortField,
    documentsSortDirection,
    selectedFolder,
    notifyApiError,
    assetManager,
    selectionHelpers,
    setLoading,
  ]);

  return {
    searchQuery,
    setSearchQuery,
    searchResults,
    setSearchResults,
    searchLoading,
    setSearchLoading,
    activeTagFilters,
    setActiveTagFilters,
    activeCorrespondentFilters,
    setActiveCorrespondentFilters,
    toggleTagFilter,
    toggleCorrespondentFilter,
    isFilterActive,
    clearFilters,
    handleSearchChange,
    handleSearchSubmit,
  };
};

export default useDocumentsSearch;
