import {
  ActionIcon,
  Box,
  Card,
  Group,
  Input,
  LoadingOverlay,
  Paper,
  ScrollArea,
  Stack,
  Typography
} from '@tidbcloud/uikit'
import { Tree, TreeDataNode } from '@tidbcloud/uikit/biz'
import { IconDatabase01, IconRefreshCw02, IconTable, IconTrash01 } from '@tidbcloud/uikit/icons'
import { ReactNode, useEffect, useMemo, useState } from 'react'

import { useCurrentCluster, useListCurrentClusterTables } from 'dbaas/features/Common'
import { useSharedTiDBContextSafe } from 'dbaas/screens/NextGen/tidb-mgmt-container/shared/tidb-context'

import { matchesTableSearch, toFilterRules } from './ClusterTableSelect.utils'

interface ClusterTableSelectProps {
  resultTitle?: string
  onChange?: (value: string[]) => void
  username?: string
  password?: string
}

const withRootNode = (tree: TreeDataNode[]) => {
  return [
    {
      key: 'All',
      title: 'All',
      isLeaf: false,
      children: tree
    }
  ]
}

export function ClusterTableSelect({
  resultTitle = 'Selected Data',
  onChange,
  username,
  password
}: ClusterTableSelectProps) {
  const [searchQuery, setSearchQuery] = useState('')
  const [checkedTreeNodes, setCheckedTreeNodes] = useState<{ checked: string[]; halfChecked: string[] }>({
    checked: [],
    halfChecked: []
  })
  const [expandKeys, setExpandKeys] = useState<string[]>([])
  const currentCluster = useCurrentCluster()
  const tidb = useSharedTiDBContextSafe()

  const { data, isLoading, refetch } = useListCurrentClusterTables({ username, password })
  const requiresCredentials =
    !!currentCluster?.computed.isPremium ||
    !!currentCluster?.computed.isEssentialV2 ||
    !!tidb?.computed.isPremium ||
    !!tidb?.computed.isEssentialV2
  const showCredentialHint = requiresCredentials && (!username || !password)

  const targetTableTreeData: TreeDataNode[] = useMemo(() => {
    return (
      data
        .filter((i) => (i.tables?.length ?? 0) > 0)
        .map((i) => ({
          key: i.database,
          title: (
            <Group gap={4} sx={{ position: 'relative', top: 4 }}>
              <Box component={IconDatabase01} size={14} c="carbon.8" />
              <Typography variant="body-md">{i.database}</Typography>
            </Group>
          ),
          isLeaf: false,
          children: 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}</Typography>
              </Group>
            ),

            key: `${i.database}.${k}`,
            isLeaf: true
          }))
        })) ?? []
    )
  }, [data])

  const filteredTargetTableTreeData: TreeDataNode[] = useMemo(() => {
    if (!searchQuery) {
      return withRootNode(targetTableTreeData)
    }
    const filter = (c: TreeDataNode) => {
      const key = c.key.toString()
      return matchesTableSearch(key, searchQuery)
    }

    const tree = targetTableTreeData
      .filter((i) => i.children && i.children.some(filter))
      .map((i) => ({
        ...i,
        children: i.children?.filter(filter)
      }))

    return withRootNode(tree)
  }, [targetTableTreeData, searchQuery])

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

        checked = checked.filter((i) => i !== 'All')

        return {
          checked,
          halfChecked
        }
      })
    }

    const tree =
      targetTableTreeData
        .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 ReactNode}
                <TrashIcon onClick={() => handleUncheckDbOrTable(i.key as string)} />
              </>
            </Group>
          ),
          children: i.children
            ?.filter((k) => checkedTreeNodes.checked.includes(k.key as string))
            .map((child) => ({
              ...child,
              title: (
                <Group justify="space-between">
                  <Group gap={4}>
                    <>{child.title as ReactNode}</>
                  </Group>
                  <TrashIcon onClick={() => handleUncheckDbOrTable(child.key as string)} />
                </Group>
              )
            }))
        })) ?? []

    return tree
  }, [targetTableTreeData, checkedTreeNodes])

  useEffect(() => {
    onChange?.(toFilterRules(checkedTreeNodes))
  }, [onChange, checkedTreeNodes])

  return (
    <Group wrap="nowrap" grow align="flex-start">
      <Card withBorder={false} shadow="unset" h={360} p={0}>
        <Stack>
          <Group h={30}>
            <ActionIcon variant="default" onClick={refetch}>
              <IconRefreshCw02 size={14} />
            </ActionIcon>

            <Input
              placeholder="Search for table"
              size="xs"
              sx={{ flexGrow: 1 }}
              onChange={(e: any) => setSearchQuery(e.target.value)}
            />
          </Group>

          <Paper withBorder>
            {data?.some((i) => (i.tables?.length ?? 0) > 0) ? (
              <ScrollArea h={312}>
                <Box py={12}>
                  <Tree
                    treeData={filteredTargetTableTreeData}
                    checkable
                    autoExpandParent
                    defaultExpandedKeys={['All']}
                    checkedKeys={checkedTreeNodes}
                    onCheck={(_, info) => {
                      setCheckedTreeNodes({
                        checked: info.checkedNodes.map((i) => i.key) as string[],
                        halfChecked: info.halfCheckedKeys as string[]
                      })

                      setExpandKeys(info.halfCheckedKeys as string[])
                    }}
                  />
                </Box>
              </ScrollArea>
            ) : (
              <Stack justify="center" align="center" h={312}>
                <Typography variant="body-lg" ta="center" maw={270} c="carbon.6">
                  {showCredentialHint
                    ? 'Enter the source database username and password above, then test connection to load tables.'
                    : 'No table in this cluster. Please create a table, then refresh this page.'}
                </Typography>
              </Stack>
            )}
          </Paper>
        </Stack>

        <LoadingOverlay visible={isLoading} overlayProps={{ blur: 2 }} loaderProps={{ color: 'carbon.6' }} />
      </Card>

      <Card withBorder shadow="unset" h={360} p={0}>
        <Box
          component="header"
          px={24}
          py={4}
          h={30}
          bg="carbon.3"
          sx={{ borderTopLeftRadius: 8, borderTopRightRadius: 8 }}
        >
          <Typography variant="label-lg">{resultTitle}</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.gray[1],

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