import type { FC } from 'react';
import { useCallback, useEffect, useState } from 'react';
import {
  asyncDataLoaderFeature,
  hotkeysCoreFeature,
  renamingFeature,
  selectionFeature,
} from '@headless-tree/core';
import { useTree } from '@headless-tree/react';
import { useVirtualizer } from '@tanstack/react-virtual';

import type { IPageForTreeItem } from '~/interfaces/page';
import { useSWRxRootPage } from '~/stores/page-listing';

import { ROOT_PAGE_VIRTUAL_ID } from '../../constants';
import { useDataLoader } from '../hooks/use-data-loader';
import { usePageRename } from '../hooks/use-page-rename';
import { useScrollToSelectedItem } from '../hooks/use-scroll-to-selected-item';
import type { TreeItemProps } from '../interfaces';
import { useCreatingParentId } from '../states/page-tree-create';
import {
  usePageTreeInformationGeneration,
  usePageTreeRevalidationEffect,
} from '../states/page-tree-update';

type Props = {
  targetPath: string;
  targetPathOrId?: string;
  isWipPageShown?: boolean;
  isEnableActions?: boolean;
  isReadOnlyUser?: boolean;
  CustomTreeItem: React.FunctionComponent<TreeItemProps>;
  estimateTreeItemSize: () => number;
  scrollerElem?: HTMLElement | null;
};

export const SimplifiedItemsTree: FC<Props> = (props: Props) => {
  const {
    targetPath,
    targetPathOrId,
    isWipPageShown = true,
    isEnableActions = false,
    isReadOnlyUser = false,
    CustomTreeItem,
    estimateTreeItemSize,
    scrollerElem,
  } = props;

  const [, setRebuildTrigger] = useState(0);

  const { data: rootPageResult } = useSWRxRootPage({ suspense: true });
  const rootPage = rootPageResult?.rootPage;
  const rootPageId = rootPage?._id ?? ROOT_PAGE_VIRTUAL_ID;
  const allPagesCount = rootPage?.descendantCount ?? 0;

  const dataLoader = useDataLoader(rootPageId, allPagesCount);

  // Page rename hook
  const { rename, getPageName } = usePageRename();

  // Get creating parent id to determine if item should be treated as folder
  const creatingParentId = useCreatingParentId();

  // onRename handler for headless-tree
  const handleRename = useCallback(
    async (item, newValue: string) => {
      await rename(item, newValue);
      // Trigger re-render after rename
      setRebuildTrigger((prev) => prev + 1);
    },
    [rename],
  );

  const tree = useTree<IPageForTreeItem>({
    rootItemId: ROOT_PAGE_VIRTUAL_ID,
    getItemName: (item) => getPageName(item),
    initialState: { expandedItems: [ROOT_PAGE_VIRTUAL_ID] },
    // Item is a folder if it has loaded children OR if it's currently in "creating" mode
    // Use getChildren() to check actual cached children instead of descendantCount
    isItemFolder: (item) => {
      const itemData = item.getItemData();
      const isCreatingUnderThis = creatingParentId === itemData._id;
      if (isCreatingUnderThis) return true;

      // Check cached children - getChildren() returns cached child items
      const children = item.getChildren();
      if (children.length > 0) return true;

      // Fallback to descendantCount for items not yet expanded
      return itemData.descendantCount > 0;
    },
    createLoadingItemData: () => ({
      _id: '',
      path: 'Loading...',
      parent: '',
      descendantCount: 0,
      revision: '',
      grant: 1,
      isEmpty: false,
      wip: false,
    }),
    dataLoader,
    onRename: handleRename,
    features: [
      asyncDataLoaderFeature,
      selectionFeature,
      hotkeysCoreFeature,
      renamingFeature,
    ],
  });

  // Track local generation number
  const [localGeneration, setLocalGeneration] = useState(1);
  const globalGeneration = usePageTreeInformationGeneration();

  // Refetch data when global generation is updated
  usePageTreeRevalidationEffect(tree, localGeneration, {
    // Update local generation number after revalidation
    onRevalidated: () => setLocalGeneration(globalGeneration),
  });

  // Expand and rebuild tree when creatingParentId changes
  useEffect(() => {
    if (creatingParentId == null) return;

    const { getItemInstance, rebuildTree } = tree;

    // Rebuild tree first to re-evaluate isItemFolder
    rebuildTree();

    // Then expand the parent item
    const parentItem = getItemInstance(creatingParentId);
    if (parentItem != null && !parentItem.isExpanded()) {
      parentItem.expand();
    }

    // Invalidate children to load placeholder
    parentItem?.invalidateChildrenIds(true);

    // Trigger re-render
    setRebuildTrigger((prev) => prev + 1);
  }, [creatingParentId, tree]);

  const items = tree.getItems();

  const virtualizer = useVirtualizer({
    count: items.length,
    getScrollElement: () => scrollerElem ?? null,
    estimateSize: estimateTreeItemSize,
    overscan: 5,
  });

  // Scroll to selected item on mount or when targetPathOrId changes
  useScrollToSelectedItem({ targetPathOrId, items, virtualizer });

  return (
    <div {...tree.getContainerProps()} className="list-group">
      {virtualizer.getVirtualItems().map((virtualItem) => {
        const item = items[virtualItem.index];
        const itemData = item.getItemData();

        // Skip rendering virtual root
        if (itemData._id === ROOT_PAGE_VIRTUAL_ID) {
          return null;
        }

        // Skip rendering WIP pages if not shown
        if (!isWipPageShown && itemData.wip) {
          return null;
        }

        const props = item.getProps();

        return (
          <div
            key={virtualItem.key}
            data-index={virtualItem.index}
            ref={(node) => {
              virtualizer.measureElement(node);
              if (node && props.ref) {
                (props.ref as (node: HTMLElement) => void)(node);
              }
            }}
          >
            <CustomTreeItem
              item={item}
              targetPath={targetPath}
              targetPathOrId={targetPathOrId}
              isWipPageShown={isWipPageShown}
              isEnableActions={isEnableActions}
              isReadOnlyUser={isReadOnlyUser}
              onToggle={() => {
                // Trigger re-render to show/hide children
                setRebuildTrigger((prev) => prev + 1);
              }}
            />
          </div>
        );
      })}
    </div>
  );
};
