import { useCallback, useEffect, useMemo, useState } from 'react';
import { createRootNode, DEFAULT_FOLDER_NAME, flattenFolderTree } from '../../../app/workspaceUtils';
import type { FolderNodeId, FolderId } from '../../../types/identifiers';
import type { FolderNode } from '../../../types/documents';
import type FoldersManager from '../../FoldersManager';

interface UseFolderTreeOptions {
  initialSelectedFolder?: FolderNodeId;
  foldersManager?: FoldersManager;
}

interface FolderOption {
  id: FolderNodeId;
  label: string;
}

const useFolderTree = ({
  initialSelectedFolder = 'root',
  foldersManager,
}: UseFolderTreeOptions) => {
  const [folderNodes, setFolderNodes] = useState<Map<FolderNodeId, FolderNode>>(() => {
    const rootNode = createRootNode() as FolderNode;
    return new Map([[rootNode.id, rootNode]]);
  });

  useEffect(() => {
    const fetchTree = async () => {
      try {
        if (!foldersManager) return;

        // Use FoldersManager as the single source of truth for fetching the tree
        const roots = await foldersManager.ensureTree();

        setFolderNodes((prev) => {
          const next = new Map(prev);
          const rootChildren: FolderNodeId[] = [];

          const flatData = flattenFolderTree(roots);

          flatData.forEach((item) => {
            const id = item.id as FolderNodeId;
            const parentId = (item.parent_id || 'root') as FolderNodeId;
            // item.children is now FolderTreeNode[], so map to IDs
            const children = (item.children || []).map((c) => c.id as FolderNodeId);

            // Preserve existing UI state (expanded, loaded)
            const existing = prev.get(id);

            next.set(id, {
              id,
              name: item.name,
              parentId,
              children,
              expanded: existing?.expanded ?? false,
              loaded: true,
              hasChildren: children.length > 0,
            });

            if (parentId === 'root') {
              rootChildren.push(id);
            }
          });

          const root = next.get('root');
          if (root) {
            next.set('root', {
              ...(root as FolderNode),
              children: rootChildren,
              hasChildren: rootChildren.length > 0,
              loaded: true,
            });
          }
          return next;
        });
      } catch (error) {
        console.error('Failed to fetch folder tree', error);
      }
    };
    fetchTree();
  }, [foldersManager]);

  const [selectedFolder, setSelectedFolder] = useState<FolderNodeId>(initialSelectedFolder || 'root');

  const isInvalidFolderDrop = useCallback(
    (sourceId: FolderNodeId | null, targetId: FolderNodeId | null) => {
      if (!sourceId) return false;
      if (!targetId || targetId === 'root') {
        return false;
      }
      if (sourceId === targetId) {
        return true;
      }

      let current = targetId;
      const visited = new Set();
      while (current && current !== 'root' && !visited.has(current)) {
        visited.add(current);
        if (current === sourceId) {
          return true;
        }
        const node = folderNodes.get(current);
        if (!node) break;
        current = (node.parentId ?? 'root') as FolderNodeId;
      }
      return false;
    },
    [folderNodes],
  );

  const resetFolderTreeState = useCallback(() => {
    const rootNode = createRootNode() as FolderNode;
    setFolderNodes(new Map<FolderNodeId, FolderNode>([[rootNode.id, rootNode]]));
    setSelectedFolder('root');
  }, []);

  const currentFolderName = useMemo(() => {
    if (selectedFolder === 'root') return DEFAULT_FOLDER_NAME;
    const node = folderNodes.get(selectedFolder);
    return node?.name || DEFAULT_FOLDER_NAME;
  }, [selectedFolder, folderNodes]);

  const folderOptions: FolderOption[] = useMemo(() => {
    const cache = new Map<FolderNodeId, string>();
    const computePath = (id: FolderNodeId | null): string => {
      if (cache.has(id as FolderNodeId)) {
        return cache.get(id as FolderNodeId) as string;
      }
      if (!id || id === 'root') {
        cache.set('root', DEFAULT_FOLDER_NAME);
        return DEFAULT_FOLDER_NAME;
      }
      const node = folderNodes.get(id);
      if (!node) {
        return 'Folder';
      }
      const parentId = (node.parentId || 'root') as FolderId;
      const parentPath = computePath(parentId);
      const name = node.name || 'Folder';
      const fullPath = parentId === 'root' ? name : `${parentPath}/${name}`;
      cache.set(id, fullPath);
      return fullPath;
    };

    const entries: FolderOption[] = [];
    folderNodes.forEach((node, id) => {
      if (!node) return;
      entries.push({ id, label: computePath(id) });
    });

    entries.sort((a, b) => {
      if (a.id === 'root') return -1;
      if (b.id === 'root') return 1;
      return a.label.localeCompare(b.label, undefined, { sensitivity: 'base' });
    });

    return entries;
  }, [folderNodes]);


  useEffect(() => {
    if (foldersManager && folderNodes.size > 0) {
      foldersManager.ingest(Array.from(folderNodes.values()));
    }
  }, [foldersManager, folderNodes]);

  const folderLabelMap = useMemo(() => {
    const map = new Map<FolderNodeId, string>();
    folderOptions.forEach((option) => {
      map.set(option.id, option.label);
    });
    return map;
  }, [folderOptions]);

  return {
    folderNodes,
    setFolderNodes,
    selectedFolder,
    setSelectedFolder,
    currentFolderName,
    folderOptions,
    folderLabelMap,
    isInvalidFolderDrop,
    resetFolderTreeState,
  };
};

export default useFolderTree;
