import type { FC } from 'react';
import { useCallback, useEffect, useRef, useState } from 'react';
import { addTrailingSlash } from '@growi/core/dist/utils/path-utils';
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 { usePageCreate } from '../hooks/use-page-create';
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();

  // Page create hook
  const { createFromPlaceholder, isCreatingPlaceholder, cancelCreating } =
    usePageCreate();

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

  // onRename handler for headless-tree
  // Handles both rename and create (for placeholder nodes)
  const handleRename = useCallback(
    async (item, newValue: string) => {
      if (isCreatingPlaceholder(item)) {
        // Placeholder node: create new page or cancel if empty
        if (newValue.trim() === '') {
          // Empty value means cancel (Esc key or blur)
          cancelCreating();
        } else {
          await createFromPlaceholder(item, newValue);
        }
      } else {
        // Normal node: rename page
        await rename(item, newValue);
      }
      // Trigger re-render after operation
      setRebuildTrigger((prev) => prev + 1);
    },
    [rename, createFromPlaceholder, isCreatingPlaceholder, cancelCreating],
  );

  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();

  // Track items count to detect when async data loading completes
  const prevItemsCountRef = useRef(items.length);
  useEffect(() => {
    if (items.length !== prevItemsCountRef.current) {
      prevItemsCountRef.current = items.length;
      // Trigger re-render when items count changes (e.g., after async load completes)
      setRebuildTrigger((prev) => prev + 1);
    }
  }, [items.length]);

  // Auto-expand items that are ancestors of targetPath
  // This runs at the parent level to handle all items regardless of virtualization
  const expandedForTargetPathRef = useRef<string | null>(null);
  useEffect(() => {
    // Skip if no items loaded yet
    if (items.length === 0) {
      return;
    }

    // Skip if already fully processed for this targetPath
    if (expandedForTargetPathRef.current === targetPath) {
      return;
    }

    let didExpand = false;

    for (const item of items) {
      const itemData = item.getItemData();
      const itemPath = itemData.path;

      if (itemPath == null) continue;

      // Check if this item is an ancestor of targetPath
      const isAncestorOfTarget =
        itemPath === '/' ||
        (targetPath.startsWith(addTrailingSlash(itemPath)) &&
          targetPath !== itemPath);

      if (!isAncestorOfTarget) continue;

      const isFolder = item.isFolder();
      const isExpanded = item.isExpanded();

      if (isFolder && !isExpanded) {
        item.expand();
        didExpand = true;
      }
    }

    // If we expanded any items, trigger re-render to load children
    if (didExpand) {
      setRebuildTrigger((prev) => prev + 1);
    }
    else {
      // Only mark as fully processed when all ancestors are expanded
      // Check if we have all the ancestors we need
      const targetSegments = targetPath.split('/').filter(Boolean);
      let hasAllAncestors = true;

      // Build ancestor paths and check if they exist in items
      for (let i = 0; i < targetSegments.length - 1; i++) {
        const ancestorPath = '/' + targetSegments.slice(0, i + 1).join('/');
        const ancestorItem = items.find(item => item.getItemData().path === ancestorPath);
        if (!ancestorItem) {
          hasAllAncestors = false;
          break;
        }
      }

      if (hasAllAncestors) {
        expandedForTargetPathRef.current = targetPath;
      }
    }
  }, [items, targetPath]);

  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>
  );
};
