import useUrlState from '@ahooksjs/use-url-state'
import {
  ActionIcon,
  Button,
  Box,
  Center,
  CopyButton,
  Group,
  Highlight,
  Loader,
  Menu,
  Skeleton,
  Stack,
  Tooltip,
  Typography
} from '@tidbcloud/uikit'
import { CollapsibleCard, MRT_ColumnDef, ProMultiSelect, ProTable } from '@tidbcloud/uikit/biz'
import { useInViewport } from '@tidbcloud/uikit/hooks'
import {
  IconDotsHorizontal,
  IconExpandLine,
  IconRefreshCw02,
  IconRows01,
  IconSettings02,
  IconShare03,
  IconCheck,
  IconCopy03,
  IconFolder,
  IconDProjectFill
} from '@tidbcloud/uikit/icons'
import { observer } from 'mobx-react'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useHistory } from 'react-router-dom'

import { HeroComponent, isHeroQueryReady } from 'dbaas/components/Measurement'
import TimeComponent from 'dbaas/components/TimeComponent'
import { AccessDeniedHoverHint } from 'dbaas/contexts/access-control'
import { BFFTiDB, getBFFTiDB } from 'dbaas/features/ClusterManagement/tidb'
import { getCloudProviderLabel, ProjectType } from 'dbaas/features/Project'
import { StateTag } from 'dbaas/screens/Cluster/ClusterInfo'
import { ClusterOperations } from 'dbaas/screens/Cluster/Operations'
import { TableFallback } from 'dbaas/screens/ClusterV2/components/PausedStatus'
import { StarterListOperation } from 'dbaas/screens/ConsoleV2/features/Clusters/StarterListOperation'
import { ServicePlanTag } from 'dbaas/screens/NextGen/shared/tidb-instance/service-plan/ServicePlanTag'
import { ProjectInvitationModal } from 'dbaas/screens/OrgSettings/features/InviteUserModal'
import { ProjectEditModal } from 'dbaas/screens/OrgSettings/features/Projects/EditModal'
import { CentralProject, useListProjects } from 'dbaas/services'
import { useTiDBServiceListTidbs } from 'dbaas/services/api-clients/v2023/cluster-management/v1beta2'
import { getDataApps } from 'dbaas/services/dataservice'
import useStores from 'dbaas/stores/useStores'

import { useVirtualProjectId } from '../my-tidb-creation/shared/use-virtual-project-id'
import { PremiumListOperation } from '../premium/list/ListOperation'

import { MyTiDBViewToolbar } from './ViewToolbar'

const DEFAULT_PAGE_SIZE = 10
const PROJECT_PAGE_SIZE = 50

interface UrlStateParams {
  projectIds?: string | string[]
}

type ProjectCardProject = CentralProject & { id: string; name: string }

const ProjectCard = observer(
  ({
    project,
    tenantData,
    highlight,
    onRenameProject,
    onInviteProject,
    expandAllSignal,
    expandAllOpened,
    refreshToken
  }: {
    project: ProjectCardProject
    tenantData: { id: string }
    highlight?: string
    onRenameProject: (project: CentralProject) => void
    onInviteProject: (project: CentralProject) => void
    expandAllSignal: number
    expandAllOpened: boolean
    refreshToken: number
  }) => {
    const history = useHistory()
    const {
      actions: { updateLastVisitLocation, updateActiveProjectId }
    } = useStores()

    const [opened, setOpened] = useState(false)
    const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: DEFAULT_PAGE_SIZE })
    const initializedRefreshSyncRef = useRef(false)
    const initializedCountRefreshSyncRef = useRef(false)
    const [isDataserviceVisible, setIsDataserviceVisible] = useState(false)
    useEffect(() => {
      // reset dataservice visibility when refresh token changes
      setIsDataserviceVisible(false)
    }, [refreshToken])

    const isOutOfProjectType = project.type === ProjectType.TIDBX_VIRTUAL || project.id === '0'
    const projectIdsForRequest = isOutOfProjectType ? [project.id, '0'] : [project.id]
    const { data, isLoading, refetch } = useTiDBServiceListTidbs(
      {
        orgId: tenantData.id,
        projectIds: projectIdsForRequest,
        pageSize: pagination.pageSize,
        skip: pagination.pageIndex * pagination.pageSize
      },
      {
        query: {
          enabled: !!tenantData.id,
          refetchOnWindowFocus: false,
          select: (resp) => ({
            ...resp,
            tidbs: resp.tidbs?.map((tidb) => getBFFTiDB(tidb))
          })
        }
      }
    )

    useEffect(() => {
      setOpened(expandAllOpened)
    }, [expandAllOpened, expandAllSignal])

    useEffect(() => {
      if (!initializedCountRefreshSyncRef.current) {
        initializedCountRefreshSyncRef.current = true
        return
      }
      refetch()
    }, [refetch, refreshToken])

    useEffect(() => {
      if (!opened) return
      if (!initializedRefreshSyncRef.current) {
        initializedRefreshSyncRef.current = true
        return
      }
      refetch()
    }, [opened, refetch, refreshToken])

    const clusters = data?.tidbs || []
    const totalSize = data?.totalSize || 0

    const clusterColumns: MRT_ColumnDef<BFFTiDB>[] = useMemo(
      () => [
        {
          header: 'Name',
          Header: () => <Typography pl={16}>Name</Typography>,
          Cell: ({ row: rowData }) => {
            const row = rowData.original
            return (
              <Typography
                tabIndex={0}
                variant="label-lg"
                c="peacock.7"
                ml={16}
                sx={{
                  cursor: 'pointer',
                  maxWidth: 500,
                  overflow: 'hidden',
                  wordBreak: 'keep-all',
                  whiteSpace: 'nowrap',
                  textOverflow: 'ellipsis'
                }}
                onClick={() => {
                  history.push(`/tidbs/${row.tidbId}/overview`)
                }}
              >
                {row.displayName}
              </Typography>
            )
          }
        },
        {
          header: 'Status',
          accessorFn: (row) => (
            <Group>
              <StateTag displayStatus={row.state} />
            </Group>
          ),
          maxSize: 100
        },
        {
          header: 'Plan',
          accessorFn: (row) =>
            row.servicePlan ? (
              <ServicePlanTag
                servicePlan={row.instance?.computed.isCassandra ? 'Cassandra Integration' : row.servicePlan}
              />
            ) : (
              '-'
            ),
          size: 126
        },
        {
          header: 'Cloud',
          accessorFn: (row) => getCloudProviderLabel(row.cloudProvider),
          size: 100
        },
        {
          header: 'Region',
          accessorFn: (row) => row.regionDisplayName
        },
        {
          header: 'Created At',
          accessorFn: (row) => <TimeComponent time={row.createTime || ''} format="YYYY-MM-DD HH:mm:ss" />
        },
        {
          id: 'Action',
          header: '',
          accessorFn: (row) => {
            if (row.cluster?.dedicated) {
              return (
                <ClusterOperations
                  cluster={row.cluster}
                  position="bottom-end"
                  actionVariant="subtle"
                  from="my-tidb"
                  onMenuOpen={() => {
                    updateLastVisitLocation({ projectId: row.projectId })
                  }}
                />
              )
            }
            if (row.cluster?.serverless) {
              return (
                <StarterListOperation
                  cluster={row.cluster.serverless}
                  onMenuOpen={() => {
                    updateLastVisitLocation({ projectId: row.projectId })
                  }}
                />
              )
            }
            if (row.instance) {
              return (
                <PremiumListOperation
                  tidb={row.instance}
                  onMenuOpen={() => {
                    updateLastVisitLocation({ projectId: row.projectId })
                  }}
                />
              )
            }
            return null
          },
          size: 80
        }
      ],
      [history, updateLastVisitLocation]
    )

    const projectDisplayName =
      project.id === '0' || project.type === ProjectType.TIDBX_VIRTUAL
        ? '(Out of project)'
        : project.name || `Project (ID: ${project.id})`
    const isOutOfProject = project.id === '0' || project.type === ProjectType.TIDBX_VIRTUAL
    const isDedicatedProject = project.type === ProjectType.DEDICATED
    const shouldShowProjectId = project.type !== ProjectType.TIDBX_VIRTUAL
    const instanceCount = totalSize
    const countLabel =
      project.id === '0' || !isDedicatedProject
        ? instanceCount === 1
          ? 'instance'
          : 'instances'
        : instanceCount === 1
          ? 'cluster'
          : 'clusters'

    return (
      <Box data-testid="my-tidb-project-card" data-project-id={project.id}>
        <CollapsibleCard
          data-testid="my-tidb-project-card-container"
          title={
            <Group wrap="nowrap" sx={{ flex: 1 }} align="center">
              <Group wrap="nowrap" gap={8} align="center" sx={{ minWidth: 0, flex: 1, overflow: 'hidden' }}>
                {!isOutOfProject ? (
                  isDedicatedProject ? (
                    <IconDProjectFill size={18} c={'carbon.8'} strokeWidth={1.5} />
                  ) : (
                    <IconFolder size={18} c={'carbon.8'} strokeWidth={1} />
                  )
                ) : null}
                <Typography variant="title-md" truncate>
                  <Highlight
                    fw={!isOutOfProject ? 700 : undefined}
                    c={'carbon.8'}
                    highlight={highlight || ''}
                    data-mp-event={isDedicatedProject ? 'My TiDB Project Network Access Link Clicked' : undefined}
                  >
                    {projectDisplayName}
                  </Highlight>
                </Typography>
                <Typography c="carbon.7" truncate>
                  Total {instanceCount} {countLabel}
                </Typography>
                {shouldShowProjectId && (
                  <Group gap={4} wrap="nowrap" align="center" ml={8} sx={{ flexShrink: 0 }}>
                    <Typography c="carbon.7">ID: {project.id}</Typography>
                    <CopyButton value={project.id} timeout={2000}>
                      {({ copied, copy }) => (
                        <Tooltip label={copied ? 'Copied' : 'Copy'} withArrow position="right">
                          <ActionIcon
                            variant="transparent"
                            size="sm"
                            c="carbon.7"
                            aria-label={`Copy project ID ${project.id}`}
                            data-testid="my-tidb-project-id-copy-button"
                            data-project-id={project.id}
                            data-mp-event="My TiDB Project ID Copy Button Clicked"
                            onClick={(event) => {
                              event.stopPropagation()
                              copy()
                            }}
                          >
                            {copied ? (
                              <IconCheck size={16} c="carbon.7" />
                            ) : (
                              <IconCopy03 size={16} strokeWidth={1} c="carbon.7" />
                            )}
                          </ActionIcon>
                        </Tooltip>
                      )}
                    </CopyButton>
                  </Group>
                )}
              </Group>
              {!isOutOfProject && (
                <Group gap={8} ml="auto" align="center" onClick={(e) => e.stopPropagation()}>
                  {isDedicatedProject && (
                    <ActionIcon
                      variant="subtle"
                      size="md"
                      onClick={() => {
                        updateLastVisitLocation({ projectId: project.id })
                        updateActiveProjectId(project.id)
                        history.push(`/project/network-access?orgId=${tenantData.id}&projectId=${project.id}`)
                      }}
                      data-testid="my-tidb-project-network-access-button"
                      data-mp-event="My TiDB Project Network Access Button Clicked"
                    >
                      <IconSettings02 size={16} c="carbon.7" />
                    </ActionIcon>
                  )}
                  <Menu
                    position="bottom-end"
                    onOpen={async () => {
                      // For dedicated projects, check whether the project has any Data Service apps.
                      if (!isDedicatedProject) return
                      if (!tenantData?.id || !project?.id) return
                      try {
                        setIsDataserviceVisible(false)
                        const resp = await getDataApps(tenantData.id, project.id)
                        const apps = (resp as any)?.data || []
                        setIsDataserviceVisible(!!apps.length)
                      } catch (_e) {
                        setIsDataserviceVisible(false)
                      }
                    }}
                  >
                    <Menu.Target>
                      <ActionIcon
                        variant="subtle"
                        size="md"
                        onClick={(e) => e.stopPropagation()}
                        data-testid="my-tidb-project-more-actions-button"
                        data-mp-event="My TiDB Project More Actions Button Clicked"
                      >
                        <IconDotsHorizontal size={16} />
                      </ActionIcon>
                    </Menu.Target>
                    <Menu.Dropdown>
                      <Menu.Item
                        onClick={() => onRenameProject(project)}
                        data-mp-event="My TiDB Project Rename Menu Item Clicked"
                      >
                        Rename
                      </Menu.Item>
                      <AccessDeniedHoverHint id="ProjectUsersAction" scopeProps={{ projectId: project.id }}>
                        {({ accessible }) => (
                          <Menu.Item
                            disabled={!accessible}
                            onClick={() => onInviteProject(project)}
                            data-mp-event="My TiDB Project Invite Menu Item Clicked"
                          >
                            Invite
                          </Menu.Item>
                        )}
                      </AccessDeniedHoverHint>
                      {/* For non-dedicated projects show Data Service as before */}
                      {!isDedicatedProject && (
                        <Menu.Item
                          rightSection={<IconShare03 size={14} c="carbon.7" />}
                          data-mp-event="My TiDB Project Data Service Menu Item Clicked"
                          onClick={() => {
                            window.open(
                              `/project/data-service?orgId=${tenantData.id}&projectId=${project.id}`,
                              '_blank'
                            )
                          }}
                        >
                          Data Service
                        </Menu.Item>
                      )}

                      {/* For dedicated projects, only show Data Service when we detected linked apps */}
                      {isDedicatedProject && isDataserviceVisible && (
                        <Menu.Item
                          rightSection={<IconShare03 size={14} c="carbon.7" />}
                          data-mp-event="My TiDB Project Data Service Menu Item Clicked"
                          onClick={() => {
                            window.open(
                              `/project/data-service?orgId=${tenantData.id}&projectId=${project.id}`,
                              '_blank'
                            )
                          }}
                        >
                          Data Service
                        </Menu.Item>
                      )}
                    </Menu.Dropdown>
                  </Menu>
                </Group>
              )}
            </Group>
          }
          opened={opened}
          onToggle={setOpened}
          contentProps={{ p: 0 }}
        >
          {opened && (
            <>
              {isLoading && clusters.length === 0 ? (
                <Center p={16} data-testid="my-tidb-project-card-loading">
                  <Typography variant="body-lg" c="carbon.7">
                    Loading...
                  </Typography>
                </Center>
              ) : clusters.length === 0 ? (
                <Center p={16} data-testid="my-tidb-project-card-empty">
                  <Typography variant="body-lg" c="carbon.7">
                    {project.type === ProjectType.DEDICATED ? 'No cluster yet' : 'No instance yet'}
                  </Typography>
                </Center>
              ) : (
                <ProTable
                  columns={clusterColumns}
                  data={clusters}
                  loading={isLoading}
                  renderEmptyRowsFallback={() => (
                    <TableFallback height="calc(100vh - 300px)">
                      <TableFallback.Empty title="No instances" desc="Instances will appear here." />
                    </TableFallback>
                  )}
                  manualPagination
                  enablePagination
                  state={{ pagination }}
                  rowCount={totalSize}
                  onPaginationChange={setPagination}
                  withBorder={false}
                  pagination={{
                    showRowsPerPage: false,
                    showTotal: true,
                    wrapperProps: {
                      px: 16,
                      py: 8,
                      sx: { borderTop: '1px solid var(--mantine-color-carbon-3)' }
                    }
                  }}
                  data-testid="my-tidb-project-card-table"
                />
              )}
            </>
          )}
        </CollapsibleCard>
      </Box>
    )
  }
)

export const MyTiDBProjectView = observer(
  ({
    tenantData,
    refreshKey,
    viewMode,
    onSwitchView
  }: {
    tenantData: { id: string }
    refreshKey: number
    viewMode: 'list' | 'project'
    onSwitchView: (value: string) => void
  }) => {
    const {
      store: { allProjects }
    } = useStores()
    const [urlState, setUrlState] = useUrlState<UrlStateParams>({ projectIds: undefined }, { navigateMode: 'replace' })
    const { ref: loadMoreRef, inViewport } = useInViewport()
    const normalizeArrayParam = (value: string | string[] | undefined): string[] => {
      if (!value) return []
      return Array.isArray(value) ? value : [value]
    }
    const projectIds = normalizeArrayParam(urlState.projectIds)
    const toProjectCardProject = useCallback(
      (project: { id: string; name: string; type?: CentralProject['type'] }) =>
        ({
          ...(project as unknown as CentralProject),
          id: project.id,
          name: project.name,
          type: project.type || ProjectType.TIDBX,
          active_clusters_count: 0,
          users_count: 0
        }) as ProjectCardProject,
      []
    )

    const [projectPage, setProjectPage] = useState(1)
    const [displayedProjects, setDisplayedProjects] = useState<CentralProject[]>([])
    const [hasNextPage, setHasNextPage] = useState(true)
    const [isRefreshing, setIsRefreshing] = useState(false)
    const [projectQueryRefreshKey, setProjectQueryRefreshKey] = useState(0)

    const {
      isFetching: isFetchingProjects,
      isFetched: isProjectsFetched,
      isError: isProjectsError
    } = useListProjects(
      tenantData.id,
      {
        page: projectPage,
        per_page: PROJECT_PAGE_SIZE,
        types: [ProjectType.DEDICATED, ProjectType.TIDBX]
      },
      {
        query: {
          queryKey: ['my-tidb-projects', tenantData.id, projectPage, projectQueryRefreshKey],
          enabled: !!tenantData.id && hasNextPage,
          refetchOnWindowFocus: false,
          onSuccess: (data) => {
            setDisplayedProjects((prev) => {
              const existingIds = new Set(prev.map((project) => project.id))
              const newProjects = (data.items || []).filter((project) => !existingIds.has(project.id))
              return prev.concat(newProjects)
            })
            setHasNextPage(projectPage * PROJECT_PAGE_SIZE < (data.total || 0))
            setIsRefreshing(false)
          },
          onError: () => {
            setIsRefreshing(false)
          }
        }
      }
    )

    const virtualProjectId = useVirtualProjectId()

    const [activeProject, setActiveProject] = useState<CentralProject>()
    const [renameOpened, setRenameOpened] = useState(false)
    const [inviteOpened, setInviteOpened] = useState(false)
    const [allExpanded, setAllExpanded] = useState(false)
    const [expandAllSignal, setExpandAllSignal] = useState(0)
    const [cardRefreshToken, setCardRefreshToken] = useState(0)

    const resetProjectPagination = useCallback((forceReload = false) => {
      setProjectPage(1)
      setDisplayedProjects([])
      setHasNextPage(true)
      setIsRefreshing(true)
      if (forceReload) {
        setProjectQueryRefreshKey((prev) => prev + 1)
      }
    }, [])

    useEffect(() => {
      resetProjectPagination()
    }, [tenantData.id, resetProjectPagination])

    useEffect(() => {
      resetProjectPagination(true)
      setCardRefreshToken((token) => token + 1)
    }, [refreshKey, resetProjectPagination])

    useEffect(() => {
      if (projectIds.length > 0 || !inViewport || !hasNextPage || isFetchingProjects) {
        return
      }
      setProjectPage((prev) => prev + 1)
    }, [hasNextPage, inViewport, isFetchingProjects, projectIds.length])

    const outOfProjectProject = useMemo<ProjectCardProject>(
      () =>
        toProjectCardProject({
          id: virtualProjectId || '0',
          name: '(Out of project)',
          type: ProjectType.TIDBX_VIRTUAL
        }),
      [toProjectCardProject, virtualProjectId]
    )

    const orgProjects = useMemo(
      () =>
        allProjects.filter(
          (project) =>
            project.org_id === tenantData.id &&
            (project.type === ProjectType.DEDICATED || project.type === ProjectType.TIDBX)
        ),
      [allProjects, tenantData.id]
    )
    const projectOptions = useMemo(() => {
      const currentOrgProjects = virtualProjectId ? [outOfProjectProject, ...orgProjects] : orgProjects
      return currentOrgProjects.map((project) => ({
        value: project.id,
        label: project.name
      }))
    }, [orgProjects, outOfProjectProject, virtualProjectId])
    const visibleProjects = useMemo(() => {
      if (projectIds.length === 0) {
        return virtualProjectId ? [outOfProjectProject, ...displayedProjects] : displayedProjects
      }

      const projectMap = new Map<string, ProjectCardProject>()
      if (virtualProjectId) {
        projectMap.set(outOfProjectProject.id, outOfProjectProject)
      }
      orgProjects.forEach((project) => {
        projectMap.set(project.id, toProjectCardProject(project))
      })

      return projectIds
        .map((projectId) => projectMap.get(projectId))
        .filter((project): project is ProjectCardProject => !!project)
    }, [displayedProjects, orgProjects, outOfProjectProject, projectIds, toProjectCardProject, virtualProjectId])

    const isInitialLoading =
      projectIds.length === 0 && (isFetchingProjects || isRefreshing) && displayedProjects.length === 0

    const handleRefresh = () => {
      resetProjectPagination(true)
      setCardRefreshToken((token) => token + 1)
    }
    const handleClearFilters = () => {
      setUrlState({ projectIds: undefined })
    }

    const handleToggleExpandAll = () => {
      setAllExpanded((prev) => {
        const next = !prev
        setExpandAllSignal((signal) => signal + 1)
        return next
      })
    }

    const handleOpenRename = (project: CentralProject) => {
      setActiveProject(project)
      setRenameOpened(true)
    }
    const handleOpenInvite = (project: CentralProject) => {
      setActiveProject(project)
      setInviteOpened(true)
    }
    const handleProjectActionFinish = () => {
      resetProjectPagination(true)
      setCardRefreshToken((token) => token + 1)
    }

    const showLoadMoreTrigger = projectIds.length === 0 && !isInitialLoading && hasNextPage
    const showLoadingMore = projectIds.length === 0 && !isInitialLoading && isFetchingProjects && hasNextPage
    const visibleProjectsCount = visibleProjects.filter((project) => project.type !== ProjectType.TIDBX_VIRTUAL).length
    const totalProjectsSummary =
      visibleProjectsCount === 0
        ? 'No projects yet.'
        : `Total ${visibleProjectsCount} ${visibleProjectsCount === 1 ? 'project' : 'projects'}`

    return (
      <Box data-testid="my-tidb-project-page">
        <HeroComponent ready={isHeroQueryReady({ isFetched: isProjectsFetched, isError: isProjectsError })} />
        <MyTiDBViewToolbar
          viewMode={viewMode}
          onSwitchView={onSwitchView}
          toolbarTestId="my-tidb-project-toolbar"
          filtersTestId="my-tidb-project-filters"
          stickyContentGap={16}
          actions={
            <>
              <ActionIcon
                onClick={handleRefresh}
                variant="subtle"
                data-mp-event="My TiDB Refresh Button Clicked"
                data-testid="my-tidb-refresh-button"
              >
                <IconRefreshCw02 size={16} />
              </ActionIcon>
              <ActionIcon
                variant="subtle"
                aria-label={allExpanded ? 'Collapse all projects' : 'Expand all projects'}
                onClick={handleToggleExpandAll}
                data-testid="my-tidb-project-expand-collapse-all-button"
                data-mp-event="My TiDB Project Expand Collapse All Button Clicked"
              >
                {allExpanded ? <IconExpandLine size={16} /> : <IconRows01 size={16} />}
              </ActionIcon>
            </>
          }
        >
          <ProMultiSelect
            data={projectOptions}
            placeholder="Project"
            clearable
            width={220}
            value={projectIds}
            searchable
            onChange={(value) => {
              setUrlState({ projectIds: value?.length ? value : undefined })
            }}
            data-testid="my-tidb-project-filter"
          />
          <Button onClick={handleClearFilters} variant="subtle" data-testid="my-tidb-clear-filter-button">
            Clear Filter
          </Button>
        </MyTiDBViewToolbar>

        <Box mt={0} data-testid="my-tidb-project-view-container">
          <Stack gap={16} data-testid="my-tidb-project-view" data-refresh-key={refreshKey}>
            {isInitialLoading && (
              <>
                <Skeleton height={100} data-testid="my-tidb-project-view-skeleton" />
                <Skeleton height={100} data-testid="my-tidb-project-view-skeleton" />
                <Skeleton height={100} data-testid="my-tidb-project-view-skeleton" />
                <Skeleton height={100} data-testid="my-tidb-project-view-skeleton" />
              </>
            )}

            {!isInitialLoading && (
              <>
                {visibleProjects.map((project) => (
                  <ProjectCard
                    key={project.id}
                    project={project as CentralProject & { id: string; name: string }}
                    tenantData={tenantData}
                    onRenameProject={handleOpenRename}
                    onInviteProject={handleOpenInvite}
                    expandAllSignal={expandAllSignal}
                    expandAllOpened={allExpanded}
                    refreshToken={cardRefreshToken}
                  />
                ))}
              </>
            )}

            {showLoadMoreTrigger && <div ref={loadMoreRef} data-testid="my-tidb-project-load-more-trigger" />}
            {showLoadingMore && (
              <Center data-testid="my-tidb-project-load-more-loader">
                <Loader size="md" />
              </Center>
            )}
          </Stack>
        </Box>
        {!isInitialLoading && (
          <Typography mt={16} c="carbon.7" data-testid="my-tidb-project-total-summary">
            {totalProjectsSummary}
          </Typography>
        )}

        <ProjectEditModal
          project={activeProject}
          opened={renameOpened}
          onClose={() => setRenameOpened(false)}
          onFinish={handleProjectActionFinish}
        />
        <ProjectInvitationModal
          mode="free"
          projectId={activeProject?.id || ''}
          opened={inviteOpened}
          onClose={() => setInviteOpened(false)}
          onFinish={handleProjectActionFinish}
        />
      </Box>
    )
  }
)

export default MyTiDBProjectView
