import { Card, Stack, Group, Typography, ScrollArea, Box } from '@tidbcloud/uikit'
import { Tree, TreeDataNode } from '@tidbcloud/uikit/biz'
import { IconDatabase01, IconTable, IconTrash01 } from '@tidbcloud/uikit/icons'
import { observer } from 'mobx-react'
import { useEffect, useMemo, useState } from 'react'

import { useGetClusterTables } from 'dbaas/services/dataflow'
import { getErrorMessage } from 'dbaas/services/errorCodes'
import useStores from 'dbaas/stores/useStores'

import { ClusterTableViewer, key, unkey } from '../TableViewer'

interface TableSelectorProps {
  clusterId: string
  label?: string
  emptyMessage?: string
  onSelect?: (nodes: { schema: string; table: string }[]) => void
  tableCheckable?: boolean
  searchPlaceholder?: string
  showEmptyDatabase?: boolean
  showDatabaseOnly?: boolean
}

export const TableSelector = observer(
  ({
    clusterId,
    label,
    emptyMessage,
    onSelect,
    tableCheckable = true,
    searchPlaceholder,
    showEmptyDatabase = false,
    showDatabaseOnly = false
  }: TableSelectorProps) => {
    const {
      store: { tenantData, activeProjectId }
    } = useStores()
    const {
      data: clusterTableData,
      isFetching: isLoadingTables,
      refetch: refetchTable,
      error
    } = useGetClusterTables(tenantData.id, activeProjectId, clusterId, {
      query: {
        refetchOnWindowFocus: false,
        enabled: Boolean(clusterId)
      }
    })

    const [expandKeys, setExpandKeys] = useState<string[]>([])
    const [checkedTreeNodes, setCheckedTreeNodes] = useState<{
      checked: string[]
      halfChecked: string[]
    }>({
      checked: [],
      halfChecked: []
    })

    const clusterTableTreeData: TreeDataNode[] = useMemo(() => {
      return (
        clusterTableData?.schemas
          ?.filter((i) => (showEmptyDatabase ? true : (i.tables?.length ?? 0) > 0))
          .map((i) => ({
            key: i.schema_name!,
            title: (
              <Group gap={4} sx={{ position: 'relative', top: 4 }}>
                <Box component={IconDatabase01} size={14} c="carbon.8" />
                <Typography variant="body-md">{i.schema_name}</Typography>
              </Group>
            ),
            isLeaf: showDatabaseOnly,
            children: showDatabaseOnly
              ? undefined
              : i.tables?.map((k) => ({
                  title: (
                    <Group gap={4} sx={{ position: 'relative', top: 4 }}>
                      <Box component={IconTable} size={14} c="carbon.8" />
                      <Typography variant="body-md">{k.table_name}</Typography>
                    </Group>
                  ),
                  checkable: tableCheckable,
                  key: key(i.schema_name!, k.table_name!),
                  isLeaf: true
                }))
          })) ?? []
      )
    }, [clusterTableData, tableCheckable, showEmptyDatabase, showDatabaseOnly])

    const checkedTableTreeData = useMemo(() => {
      const TrashIcon = (props: any) => (
        <Box
          component={IconTrash01}
          size={14}
          c="carbon.8"
          sx={{ position: 'relative', top: 4, opacity: 0, cursor: 'pointer' }}
          {...props}
        />
      )

      const handleUncheckDbOrTable = (key: string) => {
        setCheckedTreeNodes((prev) => {
          let checked = [...prev.checked]
          let halfChecked = [...prev.halfChecked]
          const isDb = !key.includes('.')
          if (isDb) {
            checked = checked.filter((item) => item !== key && !item.startsWith(key + '.'))
            halfChecked = halfChecked.filter((item) => item !== key)
          } else {
            checked = checked.filter((i) => i !== key)

            const [db] = key.split('.')
            checked = checked.filter((i) => i !== db)
            const otherTablesInDb = checked.filter((item) => item.startsWith(db + '.'))

            if (otherTablesInDb.length > 0) {
              if (!halfChecked.includes(db)) {
                halfChecked.push(db)
              }
            } else {
              if (halfChecked.includes(db)) {
                halfChecked = halfChecked.filter((i) => i !== db)
              }
            }
          }

          return {
            checked,
            halfChecked
          }
        })
      }

      return clusterTableTreeData
        .filter(
          (i) =>
            checkedTreeNodes.checked.includes(i.key as string) || checkedTreeNodes.halfChecked.includes(i.key as string)
        )
        .map((i) => ({
          ...i,
          title: (
            <Group justify="space-between">
              {i.title as any}
              <TrashIcon onClick={() => handleUncheckDbOrTable(i.key as string)} />
            </Group>
          ),
          isLeaf: showDatabaseOnly,
          children: showDatabaseOnly
            ? undefined
            : i.children
                ?.filter((k) => checkedTreeNodes.checked.includes(k.key as string))
                .map((child) => ({
                  ...child,
                  title: (
                    <Group justify="space-between">
                      {/* @ts-ignore */}
                      <Group gap={4}>{child.title}</Group>
                      {tableCheckable && <TrashIcon onClick={() => handleUncheckDbOrTable(i.key as string)} />}
                    </Group>
                  )
                }))
        }))
    }, [clusterTableTreeData, checkedTreeNodes, tableCheckable, showDatabaseOnly])

    useEffect(() => {
      onSelect?.(checkedTreeNodes.checked.map((i) => unkey(i)))
    }, [checkedTreeNodes, onSelect])

    return (
      <Stack>
        <Group wrap="nowrap" grow align="flex-start">
          <ClusterTableViewer
            databases={clusterTableTreeData}
            checkedKeys={checkedTreeNodes}
            onRefresh={clusterId ? refetchTable : () => {}}
            isFetching={isLoadingTables}
            emptyMessage={emptyMessage}
            errorMessage={getErrorMessage(error)}
            placeholder={searchPlaceholder}
            showDatabaseOnly={showDatabaseOnly}
            onCheck={(nodes) => {
              setCheckedTreeNodes(nodes)
              setExpandKeys(nodes.halfChecked)
            }}
          />

          <Card withBorder shadow="unset" h={360} p={0}>
            <Box component="header" px={24} py={16} bg="carbon.3">
              <Typography variant="label-lg">{label}</Typography>
            </Box>

            <ScrollArea h={298}>
              <Box
                p={16}
                sx={(theme) => ({
                  '& .tc-uikit-tree-treenode': {
                    borderRadius: 8
                  },
                  '& .tc-uikit-tree-node-content-wrapper': {
                    flex: 1,
                    paddingRight: 16
                  },
                  '& .tc-uikit-tree-treenode:hover': {
                    backgroundColor: theme.colors.carbon[2],

                    '&:hover': { svg: { opacity: 1 } }
                  }
                })}
              >
                <Tree
                  treeData={checkedTableTreeData}
                  expandedKeys={expandKeys}
                  onExpand={(keys) => {
                    setExpandKeys(keys as string[])
                  }}
                />
              </Box>
            </ScrollArea>
          </Card>
        </Group>
      </Stack>
    )
  }
)
