import React, {
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
} from 'react';
import { DeleteButton, EditButton, useTable } from '@refinedev/antd';
import { useGetIdentity } from '@refinedev/core';
import {
  Table,
  Button,
  Space,
  Tooltip,
  Tag,
  Alert,
  Modal,
  message,
  Tabs,
  Typography,
} from 'antd';
import {
  SaveOutlined,
  UndoOutlined,
  HolderOutlined,
  RightOutlined,
  DownOutlined,
  PlusOutlined,
  EyeOutlined,
  TeamOutlined,
  CodeOutlined,
} from '@ant-design/icons';
import {
  DndContext,
  closestCenter,
  KeyboardSensor,
  PointerSensor,
  useSensor,
  useSensors,
  DragEndEvent,
  DragOverEvent,
} from '@dnd-kit/core';
import {
  SortableContext,
  arrayMove,
  sortableKeyboardCoordinates,
  useSortable,
  verticalListSortingStrategy,
} from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import { restrictToVerticalAxis } from '@dnd-kit/modifiers';

import { customRequest } from '@/helpers/http';

import { AssignRolesModal } from './Components/AssignRolesModal';
import { EditPageModal } from './Components/EditPageModal';
import { CreatePageModal } from './Components/CreatePageModal';
import { PageDetailDrawer } from './Components/PageDetailDrawer';
import type {
  PageMenuTreeNode,
  PageResourceAssignment,
  PageResourcePermissionSummary,
  ResourceDefinition,
} from './types';
import { usePageManagement } from './hooks/usePageManagement';
import { useTranslation } from 'react-i18next';
import { SqlModal, generatePageSql } from '../../components/SqlModal';

const RESOURCE_NAME_DISPLAY_TRUNCATION_LIMIT = 40;

// Sparse number gap for sortOrder (allows inserting items between existing ones)
const SORT_ORDER_GAP = 10000;

// API function for updating page order (sortOrder and parentId)
const updatePageOrder = async (
  pageId: number,
  updateData: { sortOrder?: number; parentId?: number | null },
): Promise<any> => {
  return customRequest(`page/updateUnique/${pageId}`, 'PATCH', updateData);
};

const safelyConvertToNumber = (value: unknown): number | null => {
  if (typeof value === 'number' && !Number.isNaN(value)) {
    return value;
  }
  if (typeof value === 'string' && value.trim().length > 0) {
    const parsedNumericValue = Number(value);
    return Number.isNaN(parsedNumericValue) ? null : parsedNumericValue;
  }
  return null;
};

const extractPermissionActionsFromAssociation = (
  associationRecord: any,
  resourceRecord: any,
): string[] => {
  const aggregatedPermissionActions = new Set<string>();

  const associationActions =
    associationRecord?.actions ??
    associationRecord?.actionList ??
    associationRecord?.permissions;

  if (Array.isArray(associationActions)) {
    associationActions.forEach((action: any) => {
      if (typeof action === 'string' && action) {
        aggregatedPermissionActions.add(action);
      } else if (typeof action === 'object' && action?.name) {
        aggregatedPermissionActions.add(action.name);
      } else if (typeof action === 'object' && action?.code) {
        aggregatedPermissionActions.add(action.code);
      }
    });
  } else if (typeof associationActions === 'string') {
    associationActions
      .split(',')
      .map((action: string) => action.trim())
      .filter(Boolean)
      .forEach((action) => aggregatedPermissionActions.add(action));
  }

  return Array.from(aggregatedPermissionActions);
};

// Draggable row component for dnd-kit
interface DraggableRowProps extends React.HTMLAttributes<HTMLTableRowElement> {
  'data-row-key': string;
}

const DraggableRow: React.FC<DraggableRowProps> = ({ children, ...props }) => {
  const id = props['data-row-key'];
  const {
    attributes,
    listeners,
    setNodeRef,
    transform,
    transition,
    isDragging,
  } = useSortable({
    id,
  });

  const style: React.CSSProperties = {
    ...props.style,
    transform: CSS.Transform.toString(transform && { ...transform, scaleY: 1 }),
    transition: transition ?? 'transform 200ms ease',
    ...(isDragging
      ? {
          position: 'relative',
          zIndex: 9999,
          background: '#bae0ff',
          boxShadow: '0 8px 24px rgba(22, 119, 255, 0.25)',
          borderLeft: '3px solid #1677ff',
          opacity: 0.95,
          outline: '1px solid #1677ff',
        }
      : {}),
  };

  return (
    <tr {...props} ref={setNodeRef} style={style} {...attributes}>
      {React.Children.map(children, (child) => {
        if ((child as React.ReactElement).key === 'drag-handle') {
          return React.cloneElement(child as React.ReactElement, {
            children: (
              <HolderOutlined
                {...listeners}
                style={{ cursor: 'grab', color: '#999', touchAction: 'none' }}
              />
            ),
          });
        }
        return child;
      })}
    </tr>
  );
};

export const PageManagementListPage: React.FC = () => {
  const { i18n } = useTranslation();
  const { data: identity } = useGetIdentity<any>();
  const { tableProps } = useTable({
    resource: 'page',
    pagination: {
      pageSize: 20,
    },
  });

  const {
    pageListData,
    refetchPageList,
    pageListRequestQuery,
    availableResourcesData,
    refetchAvailableResources,
    resourceListRequestQuery,
    pageResourceAssociationsData,
    refetchPageResourceAssociations,
    pageResourceAssociationRequestQuery,
  } = usePageManagement();

  const resourceLookupByIdMap = useMemo<
    Record<number, ResourceDefinition>
  >(() => {
    const resourcesArray: any[] = Array.isArray(availableResourcesData?.data)
      ? availableResourcesData.data
      : [];
    const lookupMap: Record<number, ResourceDefinition> = {};

    resourcesArray.forEach((resource: any) => {
      const resourceId = safelyConvertToNumber(resource?.id);
      if (resourceId !== null) {
        lookupMap[resourceId] = {
          id: resourceId,
          code: resource?.code ?? String(resourceId),
          name: resource?.name || resource?.code || `Resource ${resourceId}`,
          description: resource?.description,
          type: resource?.type,
        };
      }
    });

    return lookupMap;
  }, [availableResourcesData]);

  const availableResourcesListForModal = useMemo<ResourceDefinition[]>(() => {
    return Object.values(resourceLookupByIdMap);
  }, [resourceLookupByIdMap]);

  const pageMenuTreeOptionsData = useMemo<PageMenuTreeNode[]>(() => {
    const pagesArray: any[] = Array.isArray(pageListData?.data)
      ? pageListData.data
      : [];
    const nodeMapByPageId = new Map<
      number,
      { treeNode: PageMenuTreeNode; parentPageId: number | null }
    >();

    pagesArray.forEach((page: any) => {
      const pageId = safelyConvertToNumber(
        page?.id ?? page?.pageId ?? page?.page_id,
      );
      if (pageId === null) {
        return;
      }

      const parentPageId =
        safelyConvertToNumber(
          page?.parentId ?? page?.parent_id ?? page?.parent?.id,
        ) ?? null;
      const pageDisplayTitle = page?.name || page?.code || `Page ${pageId}`;

      nodeMapByPageId.set(pageId, {
        treeNode: {
          title: pageDisplayTitle,
          value: String(pageId),
          key: String(pageId),
          children: [],
        },
        parentPageId,
      });
    });

    const rootLevelNodes: PageMenuTreeNode[] = [];

    nodeMapByPageId.forEach(({ treeNode, parentPageId }) => {
      if (parentPageId !== null && nodeMapByPageId.has(parentPageId)) {
        nodeMapByPageId.get(parentPageId)?.treeNode.children?.push(treeNode);
      } else {
        rootLevelNodes.push(treeNode);
      }
    });

    const sortTreeNodesAlphabetically = (nodes: PageMenuTreeNode[]) => {
      nodes.sort((nodeA, nodeB) => {
        const titleA =
          typeof nodeA.title === 'string' ? nodeA.title : String(nodeA.key);
        const titleB =
          typeof nodeB.title === 'string' ? nodeB.title : String(nodeB.key);
        return titleA.localeCompare(titleB);
      });
      nodes.forEach((node) => {
        if (node.children && node.children.length) {
          sortTreeNodesAlphabetically(node.children);
        }
      });
    };

    const removeEmptyChildrenArrays = (
      nodes: PageMenuTreeNode[],
    ): PageMenuTreeNode[] =>
      nodes.map((node) => {
        if (node.children && node.children.length) {
          return {
            ...node,
            children: removeEmptyChildrenArrays(node.children),
          };
        }
        const { children, ...nodeWithoutChildren } = node;
        return nodeWithoutChildren as PageMenuTreeNode;
      });

    sortTreeNodesAlphabetically(rootLevelNodes);
    return removeEmptyChildrenArrays(rootLevelNodes);
  }, [pageListData]);

  const pageResourcePermissionSummaryMap = useMemo<
    Record<number, PageResourcePermissionSummary>
  >(() => {
    const associationsArray: any[] = Array.isArray(
      pageResourceAssociationsData?.data,
    )
      ? pageResourceAssociationsData.data
      : [];
    const summaryMapByPageId: Record<number, PageResourcePermissionSummary> =
      {};

    associationsArray.forEach((association: any) => {
      const pageId = safelyConvertToNumber(association?.pageId);
      const resourceId = safelyConvertToNumber(association?.resourceId);

      if (pageId === null || resourceId === null) {
        return;
      }

      if (!summaryMapByPageId[pageId]) {
        summaryMapByPageId[pageId] = {
          resourceIds: [],
          resourceNames: [],
          allPermissionActions: [],
        };
      }

      const pageSummary = summaryMapByPageId[pageId];

      if (!pageSummary.resourceIds.includes(resourceId)) {
        pageSummary.resourceIds.push(resourceId);
      }

      const resourceRecord =
        association?.resource ?? resourceLookupByIdMap[resourceId];
      const resourceDisplayName =
        resourceRecord?.name ||
        resourceRecord?.code ||
        `Resource ${resourceId}`;

      if (
        resourceDisplayName &&
        !pageSummary.resourceNames.includes(resourceDisplayName)
      ) {
        pageSummary.resourceNames.push(resourceDisplayName);
      }

      extractPermissionActionsFromAssociation(
        association,
        resourceRecord,
      ).forEach((permissionAction) => {
        if (
          permissionAction &&
          !pageSummary.allPermissionActions.includes(permissionAction)
        ) {
          pageSummary.allPermissionActions.push(permissionAction);
        }
      });
    });

    return summaryMapByPageId;
  }, [pageResourceAssociationsData, resourceLookupByIdMap]);

  const getPageResourceAssignmentsForModal = useCallback(
    (pageId: number): PageResourceAssignment[] => {
      console.log(
        'Getting resource assignments for page ID:',
        pageId,
        pageResourceAssociationsData,
      );
      const associationsArray: any[] = Array.isArray(
        pageResourceAssociationsData?.data,
      )
        ? pageResourceAssociationsData.data
        : [];

      const pageAssignments: PageResourceAssignment[] = [];

      associationsArray.forEach((association: any) => {
        const associationPageId = safelyConvertToNumber(association?.pageId);
        const resourceId = safelyConvertToNumber(association?.resourceId);

        if (associationPageId !== pageId || resourceId === null) {
          return;
        }

        const resourceRecord =
          association?.resource ?? resourceLookupByIdMap[resourceId];
        const resourceDisplayName =
          resourceRecord?.name ||
          resourceRecord?.code ||
          `Resource ${resourceId}`;

        pageAssignments.push({
          resourceId,
          resourceName: resourceDisplayName,
          assignedPermissions: extractPermissionActionsFromAssociation(
            association,
            resourceRecord,
          ),
        });
      });

      return pageAssignments;
    },
    [pageResourceAssociationsData, resourceLookupByIdMap],
  );

  const [activeModalState, setActiveModalState] = useState<{
    modalType: 'view' | 'edit' | 'create' | null;
    targetPageId: number | null;
    defaultParentId?: number | null;
    defaultPathPrefix?: string;
  }>({
    modalType: null,
    targetPageId: null,
    defaultParentId: null,
    defaultPathPrefix: '',
  });

  // State for draggable table
  const [pendingChanges, setPendingChanges] = useState<
    Map<number, { sortOrder?: number; parentId?: number | null }>
  >(new Map());
  const [isSavingOrder, setIsSavingOrder] = useState(false);

  // State for tree expand/collapse
  const [expandedKeys, setExpandedKeys] = useState<Set<number>>(new Set());

  // State for detail drawer
  const [drawerPage, setDrawerPage] = useState<any>(null);

  // State for assign roles modal
  const [assignRolesTarget, setAssignRolesTarget] = useState<{
    pageId: number;
    pageName: string;
  } | null>(null);

  // State for active tab
  const [activeTab, setActiveTab] = useState<string>('regular');

  // State for SQL modal
  const [sqlModal, setSqlModal] = useState<{
    open: boolean;
    title: string;
    sql: string;
  }>({ open: false, title: '', sql: '' });

  const handleShowSql = useCallback(
    (record: any, hasChildren: boolean) => {
      const pagesArray: any[] = Array.isArray(pageListData?.data)
        ? pageListData.data
        : [];

      // Build pageResources for the given page
      const buildPageResources = (pageId: number) => {
        const associationsArray: any[] = Array.isArray(
          pageResourceAssociationsData?.data,
        )
          ? pageResourceAssociationsData.data
          : [];
        return associationsArray
          .filter(
            (assoc: any) => safelyConvertToNumber(assoc?.pageId) === pageId,
          )
          .map((assoc: any) => {
            const resourceId = safelyConvertToNumber(assoc?.resourceId);
            // Prefer nested resource object, then fall back to the id-keyed lookup map (which includes code).
            // The final fallback `String(resourceId)` should never be reached if availableResourcesData is loaded.
            const resourceRecord =
              assoc?.resource ??
              (resourceId !== null ? resourceLookupByIdMap[resourceId] : null);
            const resourceCode =
              resourceRecord?.code ??
              (resourceId !== null ? String(resourceId) : 'unknown');
            const resourceName =
              resourceRecord?.name ||
              resourceRecord?.code ||
              `Resource ${resourceId}`;
            const actions = extractPermissionActionsFromAssociation(
              assoc,
              resourceRecord,
            );
            return { resourceCode, resourceName, actions };
          });
      };

      const pageResources = buildPageResources(record.id);

      // Collect child pages if this is a parent
      const children: Array<{
        page: any;
        pageResources: Array<{
          resourceCode: string;
          resourceName: string;
          actions: string[];
        }>;
      }> = [];
      if (hasChildren) {
        const childPages = pagesArray.filter(
          (p: any) =>
            safelyConvertToNumber(p?.parentId ?? p?.parent_id) === record.id,
        );
        for (const child of childPages) {
          const childId = safelyConvertToNumber(child?.id);
          if (childId === null) continue;
          children.push({
            page: child,
            pageResources: buildPageResources(childId),
          });
        }
      }

      // Resolve parent's code from the in-memory page list so the SQL uses a
      // portable subquery (SELECT id … WHERE code = '…') instead of a raw
      // numeric parentId that differs between environments.
      const parentId = safelyConvertToNumber(
        record?.parentId ?? record?.parent_id,
      );
      const parentPage =
        parentId !== null
          ? pagesArray.find(
              (p: any) => safelyConvertToNumber(p?.id) === parentId,
            )
          : null;
      const recordWithParentCode = {
        ...record,
        parentCode: parentPage?.code ?? null,
      };

      const sql = generatePageSql(
        recordWithParentCode,
        pageResources,
        children,
        { authorId: identity?.userid, authorName: identity?.username },
      );
      setSqlModal({ open: true, title: `SQL — Page: ${record.name}`, sql });
    },
    [pageListData, pageResourceAssociationsData, resourceLookupByIdMap],
  );

  // Ref for expand button click/double-click disambiguation
  const expandClickTimer = useRef<number | null>(null);

  const refreshAllPageRelatedData = useCallback(() => {
    refetchPageList(pageListRequestQuery ?? {});
    refetchAvailableResources(resourceListRequestQuery ?? {});
    refetchPageResourceAssociations(pageResourceAssociationRequestQuery ?? {});
  }, [
    refetchPageList,
    pageListRequestQuery,
    refetchAvailableResources,
    resourceListRequestQuery,
    refetchPageResourceAssociations,
    pageResourceAssociationRequestQuery,
  ]);

  const handleModalClose = useCallback(() => {
    setActiveModalState({
      modalType: null,
      targetPageId: null,
      defaultParentId: null,
      defaultPathPrefix: '',
    });
  }, []);

  const handleModalOperationSuccess = useCallback(() => {
    handleModalClose();
    refreshAllPageRelatedData();
  }, [refreshAllPageRelatedData, handleModalClose]);

  // Calculate new sortOrder when dropping a node
  const calculateNewSortOrder = useCallback(
    (siblings: any[], dropIndex: number, dragNodeId: number): number => {
      // Filter out the dragged node from siblings
      const filteredSiblings = siblings.filter(
        (s) => safelyConvertToNumber(s.id) !== dragNodeId,
      );

      if (filteredSiblings.length === 0) {
        // First item in this parent
        return SORT_ORDER_GAP;
      }

      if (dropIndex === 0) {
        // Dropped at the beginning
        const firstSibling = filteredSiblings[0];
        const firstOrder = firstSibling?.sortOrder ?? SORT_ORDER_GAP;
        return Math.max(1, Math.floor(firstOrder / 2));
      }

      if (dropIndex >= filteredSiblings.length) {
        // Dropped at the end
        const lastSibling = filteredSiblings[filteredSiblings.length - 1];
        const lastOrder = lastSibling?.sortOrder ?? 0;
        return lastOrder + SORT_ORDER_GAP;
      }

      // Dropped in the middle
      const prevSibling = filteredSiblings[dropIndex - 1];
      const nextSibling = filteredSiblings[dropIndex];
      const prevOrder = prevSibling?.sortOrder ?? 0;
      const nextOrder =
        nextSibling?.sortOrder ?? prevOrder + SORT_ORDER_GAP * 2;

      // Calculate middle value
      const newOrder = Math.floor((prevOrder + nextOrder) / 2);

      // If there's no room (gap too small), we need to redistribute
      if (newOrder <= prevOrder || newOrder >= nextOrder) {
        // Return a value that signals we need redistribution
        // For now, just use prevOrder + 1 and let batch save handle redistribution
        return prevOrder + 1;
      }

      return newOrder;
    },
    [],
  );

  // Toggle expand/collapse for a node
  const handleToggleExpand = useCallback((pageId: number) => {
    setExpandedKeys((prev) => {
      const newSet = new Set(prev);
      if (newSet.has(pageId)) {
        newSet.delete(pageId);
      } else {
        newSet.add(pageId);
      }
      return newSet;
    });
  }, []);

  // Separate regular pages and virtual pages
  const { regularPagesData, virtualPagesData } = useMemo(() => {
    const pagesArray: any[] = Array.isArray(pageListData?.data)
      ? pageListData.data
      : [];
    const regular: any[] = [];
    const virtual: any[] = [];

    pagesArray.forEach((page: any) => {
      if (page.isVirtual) {
        virtual.push(page);
      } else {
        regular.push(page);
      }
    });

    return { regularPagesData: regular, virtualPagesData: virtual };
  }, [pageListData]);

  // Flatten tree data for table display (with depth info for indentation) - only for regular (non-virtual) pages
  const flattenedTableData = useMemo(() => {
    const nodeMapById = new Map<number, any>();

    // First pass: create all nodes with pending changes applied (only regular pages)
    regularPagesData.forEach((page: any) => {
      const pageId = safelyConvertToNumber(page?.id);
      if (pageId === null) return;

      const pendingChange = pendingChanges.get(pageId);
      const sortOrder = pendingChange?.sortOrder ?? page.sortOrder;
      const parentId =
        pendingChange?.parentId !== undefined
          ? pendingChange.parentId
          : (page.parentId ?? null);

      nodeMapById.set(pageId, {
        ...page,
        id: pageId,
        sortOrder,
        parentId,
        children: [],
      });
    });

    // Second pass: build tree structure
    const rootNodes: any[] = [];
    nodeMapById.forEach((node) => {
      const parentId = node.parentId;
      if (parentId !== null && nodeMapById.has(parentId)) {
        nodeMapById.get(parentId).children.push(node);
      } else {
        rootNodes.push(node);
      }
    });

    // Sort children by sortOrder
    const sortNodes = (nodes: any[]) => {
      nodes.sort(
        (a, b) => (a.sortOrder ?? Infinity) - (b.sortOrder ?? Infinity),
      );
      nodes.forEach((node) => {
        if (node.children?.length > 0) {
          sortNodes(node.children);
        }
      });
    };
    sortNodes(rootNodes);

    // Flatten tree to array with depth info (respecting expanded state)
    const result: any[] = [];
    const flatten = (nodes: any[], depth: number, parentExpanded: boolean) => {
      nodes.forEach((node) => {
        // Only show node if its parent is expanded (or it's a root node)
        if (!parentExpanded && depth > 0) return;

        const { children, ...nodeWithoutChildren } = node;
        const hasChildren = children?.length > 0;
        const isExpanded = expandedKeys.has(node.id);

        result.push({
          ...nodeWithoutChildren,
          _depth: depth,
          _hasChildren: hasChildren,
          _isExpanded: isExpanded,
          _isModified: pendingChanges.has(node.id),
        });

        if (hasChildren) {
          flatten(children, depth + 1, isExpanded);
        }
      });
    };
    flatten(rootNodes, 0, true);

    return result;
  }, [regularPagesData, pendingChanges, expandedKeys]);

  // Get sortable item IDs for dnd-kit
  const sortableIds = useMemo(() => {
    return flattenedTableData.map((item) => String(item.id));
  }, [flattenedTableData]);

  // Live-reordered IDs during drag (for visual feedback)
  const [activeSortableIds, setActiveSortableIds] = useState<string[]>([]);

  // Sync activeSortableIds when base list changes (e.g. after save/refresh)
  useEffect(() => {
    setActiveSortableIds(sortableIds);
  }, [sortableIds]);

  // dnd-kit sensors
  const sensors = useSensors(
    useSensor(PointerSensor, {
      activationConstraint: {
        distance: 1,
      },
    }),
    useSensor(KeyboardSensor, {
      coordinateGetter: sortableKeyboardCoordinates,
    }),
  );

  // Reorder the visual list live during drag for smooth feedback
  const handleDragOver = useCallback((event: DragOverEvent) => {
    const { active, over } = event;
    if (!over || active.id === over.id) return;
    setActiveSortableIds((prev) => {
      const oldIndex = prev.indexOf(String(active.id));
      const newIndex = prev.indexOf(String(over.id));
      if (oldIndex === -1 || newIndex === -1) return prev;
      return arrayMove(prev, oldIndex, newIndex);
    });
  }, []);

  // Handle drag end for dnd-kit
  const handleDragEnd = useCallback(
    (event: DragEndEvent) => {
      const { active, over } = event;

      if (!over || active.id === over.id) return;

      const draggedId = safelyConvertToNumber(active.id);
      const overId = safelyConvertToNumber(over.id);

      if (draggedId === null || overId === null) return;

      const pagesArray: any[] = Array.isArray(pageListData?.data)
        ? pageListData.data
        : [];
      const draggedPage = pagesArray.find(
        (p) => safelyConvertToNumber(p.id) === draggedId,
      );
      const overPage = pagesArray.find(
        (p) => safelyConvertToNumber(p.id) === overId,
      );

      if (!draggedPage || !overPage) return;

      // Get target's parentId (with pending changes applied)
      const overPendingChange = pendingChanges.get(overId);
      const targetParentId =
        overPendingChange?.parentId !== undefined
          ? overPendingChange.parentId
          : (overPage.parentId ?? null);

      // Get dragged item's current parentId
      const draggedPendingChange = pendingChanges.get(draggedId);
      const currentParentId =
        draggedPendingChange?.parentId !== undefined
          ? draggedPendingChange.parentId
          : (draggedPage.parentId ?? null);

      // Determine new parentId - when dropping on an item, take its parentId (same level)
      const newParentId = targetParentId;

      // Get all siblings at the target parent level (with pending changes)
      const getSiblingsWithPendingChanges = (parentId: number | null) => {
        return pagesArray
          .map((page) => {
            const pageId = safelyConvertToNumber(page.id);
            if (pageId === null) return null;
            const pending = pendingChanges.get(pageId);
            return {
              ...page,
              id: pageId,
              parentId:
                pending?.parentId !== undefined
                  ? pending.parentId
                  : (page.parentId ?? null),
              sortOrder: pending?.sortOrder ?? page.sortOrder ?? 0,
            };
          })
          .filter(
            (page): page is NonNullable<typeof page> =>
              page !== null && page.parentId === parentId,
          )
          .sort((a, b) => a.sortOrder - b.sortOrder);
      };

      const siblings = getSiblingsWithPendingChanges(newParentId);

      // Find the position of the over item in siblings
      const overIndex = siblings.findIndex((s) => s.id === overId);
      const dragIndex = siblings.findIndex((s) => s.id === draggedId);

      // Calculate drop index
      let dropIndex: number;
      if (dragIndex === -1) {
        // Dragged item is not in this parent group (moving to new parent)
        dropIndex = overIndex;
      } else if (dragIndex < overIndex) {
        // Moving down
        dropIndex = overIndex;
      } else {
        // Moving up
        dropIndex = overIndex;
      }

      // Calculate new sort order
      const newSortOrder = calculateNewSortOrder(
        siblings,
        dropIndex,
        draggedId,
      );

      // Check if anything changed
      const parentChanged = newParentId !== currentParentId;
      const orderChanged =
        newSortOrder !==
        (draggedPendingChange?.sortOrder ?? draggedPage.sortOrder ?? 0);

      if (!parentChanged && !orderChanged) return;

      // Update pending changes
      setPendingChanges((prev) => {
        const newChanges = new Map(prev);
        const existingChange = newChanges.get(draggedId) || {};

        newChanges.set(draggedId, {
          ...existingChange,
          ...(parentChanged && { parentId: newParentId }),
          sortOrder: newSortOrder,
        });

        return newChanges;
      });

      message.info(
        `Page "${draggedPage.name}" moved. Click "Save Order" to apply changes.`,
      );
    },
    [pageListData, pendingChanges, calculateNewSortOrder],
  );

  // Discard all pending changes
  const handleDiscardChanges = useCallback(() => {
    if (pendingChanges.size === 0) return;

    Modal.confirm({
      title: 'Discard Changes',
      content: `Are you sure you want to discard ${pendingChanges.size} pending change(s)?`,
      okText: 'Discard',
      okType: 'danger',
      styles: { body: { padding: '16px 24px' } },
      onOk: () => {
        setPendingChanges(new Map());
        message.success('Changes discarded');
      },
    });
  }, [pendingChanges]);

  // Save all pending changes
  const handleSaveOrder = useCallback(async () => {
    if (pendingChanges.size === 0) {
      message.info('No changes to save');
      return;
    }

    setIsSavingOrder(true);

    try {
      const updatePromises: Promise<any>[] = [];

      pendingChanges.forEach((change, pageId) => {
        const updateData: { sortOrder?: number; parentId?: number | null } = {};

        if (change.sortOrder !== undefined) {
          updateData.sortOrder = change.sortOrder;
        }
        if (change.parentId !== undefined) {
          updateData.parentId = change.parentId;
        }

        if (Object.keys(updateData).length > 0) {
          updatePromises.push(updatePageOrder(pageId, updateData));
        }
      });

      await Promise.all(updatePromises);

      message.success(`Successfully updated ${pendingChanges.size} page(s)`);
      setPendingChanges(new Map());
      refreshAllPageRelatedData();
    } catch (error) {
      console.error('Failed to save order:', error);
      message.error('Failed to save some changes. Please try again.');
    } finally {
      setIsSavingOrder(false);
    }
  }, [pendingChanges, refreshAllPageRelatedData]);

  // Build parent-children lookup for expand operations
  const parentChildrenMap = useMemo(() => {
    const childrenOf = new Map<number | null, number[]>(); // parentId -> childIds
    for (const page of regularPagesData) {
      const pageId = safelyConvertToNumber(page?.id);
      if (pageId === null) continue;
      const pendingChange = pendingChanges.get(pageId);
      const parentId =
        pendingChange?.parentId !== undefined
          ? pendingChange.parentId
          : (page.parentId ?? null);
      if (!childrenOf.has(parentId)) childrenOf.set(parentId, []);
      childrenOf.get(parentId)!.push(pageId);
    }
    return childrenOf;
  }, [regularPagesData, pendingChanges]);

  // Expand to N levels (single click = 2 levels)
  const handleExpandLevels = useCallback(
    (maxDepth: number) => {
      const keysToExpand = new Set<number>();
      const expand = (parentId: number | null, depth: number) => {
        if (depth >= maxDepth) return;
        const children = parentChildrenMap.get(parentId);
        if (!children) return;
        for (const childId of children) {
          // Only expand nodes that have children
          if (parentChildrenMap.has(childId)) {
            keysToExpand.add(childId);
            expand(childId, depth + 1);
          }
        }
      };
      expand(null, 0);
      setExpandedKeys(keysToExpand);
    },
    [parentChildrenMap],
  );

  // Expand all nodes (double click)
  const handleExpandAll = useCallback(() => {
    // Expand every node that has children
    const keysToExpand = new Set<number>();
    for (const [parentId] of parentChildrenMap) {
      if (parentId !== null) keysToExpand.add(parentId);
    }
    setExpandedKeys(keysToExpand);
  }, [parentChildrenMap]);

  // Collapse all nodes
  const handleCollapseAll = useCallback(() => {
    setExpandedKeys(new Set());
  }, []);

  // Common column renderers for both tables
  const renderAssignedResourcesColumn = (record: any) => {
    const permissionSummary = pageResourcePermissionSummaryMap[record.id];

    if (!permissionSummary?.resourceNames.length) {
      return <span style={{ color: '#999' }}>No resources assigned</span>;
    }

    const joinedResourceNames = permissionSummary.resourceNames.join(', ');
    const shouldTruncateDisplay =
      joinedResourceNames.length > RESOURCE_NAME_DISPLAY_TRUNCATION_LIMIT;
    const truncatedDisplayValue = shouldTruncateDisplay
      ? `${joinedResourceNames.slice(0, RESOURCE_NAME_DISPLAY_TRUNCATION_LIMIT)}...`
      : joinedResourceNames;

    return (
      <Tooltip title={joinedResourceNames}>
        <span>{truncatedDisplayValue}</span>
      </Tooltip>
    );
  };

  const renderPermissionActionsColumn = (record: any) => {
    const permissionSummary = pageResourcePermissionSummaryMap[record.id];

    if (!permissionSummary?.allPermissionActions.length) {
      return <span style={{ color: '#999' }}>-</span>;
    }

    return (
      <Space size={[4, 4]} wrap>
        {permissionSummary.allPermissionActions.map((permissionAction) => (
          <Tag key={`${record.id}-${permissionAction}`} color="blue">
            {permissionAction}
          </Tag>
        ))}
      </Space>
    );
  };

  // Handle click on add child page button
  const handleAddChildPage = useCallback((parentPage: any) => {
    const parentPath = parentPage.path || '';
    const pathPrefix = parentPath.endsWith('/') ? parentPath : `${parentPath}/`;
    setActiveModalState({
      modalType: 'create',
      targetPageId: null,
      defaultParentId: parentPage.id,
      defaultPathPrefix: pathPrefix,
    });
  }, []);

  const renderActionsColumn = (record: any, hasChildren: boolean = false) => (
    <Space>
      <Tooltip title="Add child page">
        <Button
          type="default"
          size="small"
          icon={<PlusOutlined />}
          onClick={() => handleAddChildPage(record)}
        />
      </Tooltip>
      <EditButton
        hideText
        size="small"
        recordItemId={record.id}
        onClick={() =>
          setActiveModalState({ modalType: 'edit', targetPageId: record.id })
        }
      />
      <Tooltip title="Generate SQL">
        <Button
          type="default"
          size="small"
          icon={<CodeOutlined />}
          onClick={() => handleShowSql(record, hasChildren)}
        />
      </Tooltip>
      {/* Only show delete button for leaf nodes (pages without children) */}
      {!hasChildren && (
        <>
          <Tooltip title="Assign roles">
            <Button
              type="default"
              size="small"
              icon={<TeamOutlined />}
              onClick={() =>
                setAssignRolesTarget({
                  pageId: record.id,
                  pageName: record.name,
                })
              }
            />
          </Tooltip>
          <Tooltip title="View details">
            <Button
              type="default"
              size="small"
              icon={<EyeOutlined />}
              onClick={() => setDrawerPage(record)}
            />
          </Tooltip>
          <DeleteButton
            hideText
            size="small"
            recordItemId={record.id}
            resource="page"
            onSuccess={() => refreshAllPageRelatedData()}
          />
        </>
      )}
    </Space>
  );

  // Render regular pages table with drag-drop
  const renderRegularPagesTable = () => (
    <>
      <Space style={{ marginBottom: 16 }}>
        <Tooltip title="Click: expand 2 levels / Double-click: expand all">
          <Button
            onClick={() => {
              expandClickTimer.current = window.setTimeout(
                () => handleExpandLevels(1),
                200,
              );
            }}
            onDoubleClick={() => {
              if (expandClickTimer.current)
                clearTimeout(expandClickTimer.current);
              handleExpandAll();
            }}
          >
            Expand All
          </Button>
        </Tooltip>
        <Button onClick={handleCollapseAll}>Collapse All</Button>
        {pendingChanges.size > 0 && (
          <>
            <Button
              type="primary"
              icon={<SaveOutlined />}
              onClick={handleSaveOrder}
              loading={isSavingOrder}
            >
              Save Order ({pendingChanges.size} change
              {pendingChanges.size > 1 ? 's' : ''})
            </Button>
            <Button
              icon={<UndoOutlined />}
              onClick={handleDiscardChanges}
              disabled={isSavingOrder}
            >
              Discard Changes
            </Button>
          </>
        )}
      </Space>

      {/* Info alert about drag and drop */}
      <Alert
        message="Drag and drop rows using the handle (⋮⋮) to reorder pages. Changes are saved in batch when you click 'Save Order'."
        type="info"
        showIcon
        style={{ marginBottom: 16 }}
        closable
      />

      {/* Table with dnd-kit drag and drop */}
      <DndContext
        sensors={sensors}
        collisionDetection={closestCenter}
        modifiers={[restrictToVerticalAxis]}
        onDragOver={handleDragOver}
        onDragEnd={handleDragEnd}
      >
        <SortableContext
          items={activeSortableIds}
          strategy={verticalListSortingStrategy}
        >
          <Table
            {...tableProps}
            rowKey="id"
            dataSource={flattenedTableData}
            pagination={false}
            components={{
              body: {
                row: DraggableRow,
              },
            }}
          >
            {/* Drag handle column */}
            <Table.Column key="drag-handle" width={50} render={() => null} />
            <Table.Column
              dataIndex="name"
              title="Page Name"
              render={(text, record: any) => (
                <span
                  style={{
                    paddingLeft: record._depth * 20,
                    display: 'inline-flex',
                    alignItems: 'center',
                  }}
                >
                  {record._hasChildren ? (
                    <span
                      onClick={(e) => {
                        e.stopPropagation();
                        handleToggleExpand(record.id);
                      }}
                      style={{
                        cursor: 'pointer',
                        marginRight: 8,
                        display: 'inline-flex',
                        alignItems: 'center',
                      }}
                    >
                      {record._isExpanded ? (
                        <DownOutlined style={{ fontSize: 10 }} />
                      ) : (
                        <RightOutlined style={{ fontSize: 10 }} />
                      )}
                    </span>
                  ) : (
                    <span style={{ width: 18 }} />
                  )}
                  {i18n.language === 'zh_CN'
                    ? record.zhName || text
                    : record.enName || text}
                  {record._isModified && (
                    <Tag color="orange" style={{ marginLeft: 8, fontSize: 10 }}>
                      Modified
                    </Tag>
                  )}
                </span>
              )}
            />
            <Table.Column dataIndex="path" title="Page Path" />
            <Table.Column
              dataIndex="description"
              title="Description"
              ellipsis={{ showTitle: true }}
              onCell={() => ({ style: { maxWidth: 200 } })}
            />
            <Table.Column dataIndex="creator" title="Creator" />
            <Table.Column
              title="Actions"
              render={(_, record: any) =>
                renderActionsColumn(record, record._hasChildren)
              }
            />
          </Table>
        </SortableContext>
      </DndContext>
    </>
  );

  // Render virtual pages table (simple table without drag-drop and tree structure)
  const renderVirtualPagesTable = () => (
    <Table
      rowKey="id"
      dataSource={virtualPagesData}
      pagination={{ pageSize: 20 }}
    >
      <Table.Column dataIndex="name" title="Page Name" />
      <Table.Column dataIndex="creator" title="Creator" />
      <Table.Column
        dataIndex="description"
        title="Description"
        ellipsis={{ showTitle: true }}
        onCell={() => ({ style: { maxWidth: 200 } })}
      />
      <Table.Column
        title="Actions"
        render={(_, record: any) => renderActionsColumn(record, false)}
      />
    </Table>
  );

  return (
    <div>
      <div
        style={{
          display: 'flex',
          justifyContent: 'space-between',
          alignItems: 'center',
          marginBottom: 16,
        }}
      >
        <Typography.Title level={4} style={{ margin: 0 }}>
          Page mgnts
        </Typography.Title>
        <Button
          type="primary"
          onClick={() =>
            setActiveModalState({
              modalType: 'create',
              targetPageId: null,
            })
          }
        >
          Create Page
        </Button>
      </div>

      <Tabs
        activeKey={activeTab}
        onChange={setActiveTab}
        items={[
          {
            key: 'regular',
            label: `Regular Pages (${regularPagesData.length})`,
            children: renderRegularPagesTable(),
          },
          {
            key: 'virtual',
            label: `Virtual Pages (${virtualPagesData.length})`,
            children: renderVirtualPagesTable(),
          },
        ]}
      />

      {activeModalState.modalType === 'edit' &&
        activeModalState.targetPageId && (
          <EditPageModal
            pageIdToEdit={activeModalState.targetPageId}
            onModalClose={handleModalClose}
            onPageEditSuccess={handleModalOperationSuccess}
            parentPageTreeOptions={pageMenuTreeOptionsData}
            allAvailableResources={availableResourcesListForModal}
            currentPageResourceAssignments={getPageResourceAssignmentsForModal(
              activeModalState.targetPageId,
            )}
            allPagesData={pageListData?.data || []}
          />
        )}

      {drawerPage && (
        <PageDetailDrawer
          page={drawerPage}
          open={!!drawerPage}
          onClose={() => setDrawerPage(null)}
        />
      )}

      {assignRolesTarget && (
        <AssignRolesModal
          pageId={assignRolesTarget.pageId}
          pageName={assignRolesTarget.pageName}
          open={!!assignRolesTarget}
          onClose={() => setAssignRolesTarget(null)}
          onSuccess={() => {
            setAssignRolesTarget(null);
            refreshAllPageRelatedData();
          }}
        />
      )}

      {activeModalState.modalType === 'create' && (
        <CreatePageModal
          onModalClose={handleModalClose}
          onPageCreationSuccess={handleModalOperationSuccess}
          parentPageTreeOptions={pageMenuTreeOptionsData}
          allAvailableResources={availableResourcesListForModal}
          allPagesData={pageListData?.data || []}
          defaultParentId={activeModalState.defaultParentId}
          defaultPathPrefix={activeModalState.defaultPathPrefix}
        />
      )}

      <SqlModal
        open={sqlModal.open}
        onClose={() => setSqlModal({ open: false, title: '', sql: '' })}
        title={sqlModal.title}
        sql={sqlModal.sql}
      />
    </div>
  );
};
