import { ActionIcon, Anchor, Drawer, Menu, notifier, Stack, Text, Typography } from '@tidbcloud/uikit'
import { MRT_ColumnDef, ProTable, ProTableProps } from '@tidbcloud/uikit/biz'
import { IconDotsHorizontal } from '@tidbcloud/uikit/icons'
import { useMemoizedFn, useMount } from 'ahooks'
import { observer } from 'mobx-react'
import { ReactNode, useState } from 'react'
import { useHistory } from 'react-router-dom'

import { humanByteSize } from 'common/services/prommetric'
import { HeroComponent } from 'dbaas/components/Measurement'
import { renderTimeColumn } from 'dbaas/components/Table/utils'
import { AccessCheck } from 'dbaas/contexts/access-control'
import { SharedCluster } from 'dbaas/features/ClusterManagement/cluster'
import { useSqlEditorStatus } from 'dbaas/features/ClusterManagement/feats/chat2query/status'
import { canCancelImport, canCancelServerlessImport, DataSourceTypeDisplayName } from 'dbaas/features/Import'
import { ImportStatus } from 'dbaas/features/Import/ImportStatus'
import { CloudProviderConst } from 'dbaas/features/Project'
import { isClusterInvalid, TableFallback } from 'dbaas/screens/ClusterV2/components/PausedStatus'
import { ImportTaskDetailDrawer } from 'dbaas/screens/DataImportV2/ImportTaskDetailDrawer'
import { useImportServiceCancelImport, useImportServiceListImports } from 'dbaas/services/api-clients/legacy/serverless'
import {
  Import as ServerlessImport,
  ImportSourceTypeEnum as ServerlessImportSourceTypeEnum,
  ImportStateEnum as ServerlessImportStateEnum
} from 'dbaas/services/api-clients/legacy/serverless/interface'
import { useCancelImport, useListImports } from 'dbaas/services/dataflow'
import { DataflowImportGetResp, DataflowImportGetRespStatus } from 'dbaas/services/dataflow/interface'
import { getErrorMessage } from 'dbaas/services/errorCodes'
import useStores from 'dbaas/stores/useStores'
import { eventTracking } from 'dbaas/utils/tracking'

import ImportDropzone from './ImportDropzone'

const featureDesc = {
  [CloudProviderConst.AWS]:
    'Import data files in SQL, CSV, Parquet, or Aurora Snapshot formats into TiDB Cloud Dedicated from Amazon S3. This feature lets you easily bring your data into TiDB Cloud and quickly get started with your database.',
  [CloudProviderConst.AZURE]:
    'Import data files in SQL, CSV, or Parquet formats into TiDB Cloud Dedicated from Azure Blob Storage. This feature lets you easily bring your data into TiDB Cloud and quickly get started with your database.',
  [CloudProviderConst.GCP]:
    'Import data files in SQL, CSV or Parquet formats into TiDB Cloud Dedicated from Google Cloud Storage. This feature lets you easily bring your data into TiDB Cloud and quickly get started with your database.'
}

const renderImportListFallback = (cluster: SharedCluster) => {
  const desc = cluster.computed.isServerless
    ? 'Import data files in SQL, CSV, Parquet or Aurora Snapshot formats into TiDB Cloud from either local storage or cloud storage services such as Amazon S3, Google Cloud Storage, and Azure Blob Storage. This feature lets you easily bring your data into TiDB Cloud and quickly get started with your database.'
    : featureDesc[cluster.provider]

  if (isClusterInvalid(cluster)) {
    return (
      <TableFallback height="calc(100vh - 370px)">
        <TableFallback.Paused cluster={cluster} featureName="Import" featureDesc={desc} />
      </TableFallback>
    )
  }

  return (
    <TableFallback height="calc(100vh - 370px)">
      <TableFallback.Empty title="Get started by importing your data" desc={desc} />
    </TableFallback>
  )
}

const Imports = ({ cluster }: { cluster: SharedCluster }) => {
  const {
    store: { tenantData, activeProjectId },
    actions
  } = useStores()
  const history = useHistory()
  const [opened, setOpened] = useState(false)
  const urlParams = new URLSearchParams(window.location.search)
  const [importTaskId, setImportTaskId] = useState(urlParams.get('id') ?? '')
  const [params, setParams] = useState({
    page: 1,
    pages: 0
  })
  const sqlEditorState = useSqlEditorStatus(cluster)

  const isServerless = cluster.computed.isServerless
  const serverlessImportActionAccessCheckProps = cluster.computed.isServerless
    ? {
        id: 'TidbxImportAction' as const
      }
    : {
        id: 'ClusterImportAction' as const
      }

  const {
    data: dedicatedImportsData,
    isLoading: isDedicatedImportLoading,
    isFetched,
    refetch: refetchDedicatedImports
  } = useListImports(
    tenantData.id,
    activeProjectId,
    cluster.id,
    {
      page: params.page,
      page_size: 10
      // statuses: importTaskStatusFilter
    },
    {
      query: {
        refetchInterval: 30000,
        enabled: !isServerless && !isClusterInvalid(cluster),
        onSuccess(data) {
          setParams({
            ...params,
            pages: Math.ceil(parseInt(data?.total || '0') / 10)
          })
        }
      }
    }
  )

  const {
    data: serverlessImportData,
    isLoading: isServerlessImportLoading,
    isFetched: isServerlessImportFetched
  } = useImportServiceListImports(
    tenantData.id,
    activeProjectId,
    cluster.id,
    {
      page_size: 10,
      page_token: params.page + '',
      order_by: 'create_time desc'
    },
    {
      query: {
        enabled: isServerless,
        refetchInterval: 30000,
        onSuccess(data) {
          setParams({
            ...params,
            pages: Math.ceil(data?.total_size! / 10)
          })
        }
      }
    }
  )

  const cancelImport = useCancelImport()
  const cancelServerlessImport = useImportServiceCancelImport()

  const handleCancelImport = useMemoizedFn((id: string) => {
    try {
      if (isServerless) {
        cancelServerlessImport.mutateAsync({
          orgId: tenantData.id,
          projectId: activeProjectId,
          clusterId: cluster.id,
          id,
          data: {}
        })
      } else {
        cancelImport.mutateAsync({
          orgId: tenantData.id,
          projectId: activeProjectId,
          clusterId: cluster.id,
          id
        })
      }
      notifier.success('Import task successfully cancelled.')
    } catch (e) {
      notifier.error(getErrorMessage(e))
    }
  })

  const importDataColumns: MRT_ColumnDef<DataflowImportGetResp>[] = [
    {
      header: 'Name',
      Header: () => <Typography pl={16}>Name</Typography>,
      minSize: 100,
      maxSize: 400,
      accessorFn(data) {
        let name = ''
        if (data.import_create_req?.local_import_cfg) {
          name = data.import_create_req?.local_import_cfg.original_file_name!
        } else {
          let url = data.import_create_req?.source_url!
          if (url.endsWith('/')) {
            url = url.slice(0, -1)
          }
          name = url.substring(url.lastIndexOf('/') + 1)
        }

        return (
          <Anchor
            truncate
            pl={16}
            c="peacock.7"
            maw={400}
            title={name}
            display="block"
            onClick={() => {
              setImportTaskId(data.id!)
              setOpened(true)
            }}
          >
            {name}
          </Anchor>
        )
      }
    },
    {
      header: 'File Size',
      maxSize: 120,
      accessorFn: ({ total_size }) => humanByteSize(parseInt(total_size), false)
    },
    {
      header: 'Source',
      maxSize: 120,
      accessorFn(data) {
        return DataSourceTypeDisplayName[data.import_create_req?.data_source_type!]
      }
    },
    {
      header: 'Target Table',
      accessorFn(data) {
        let db = ''
        let table = ''
        let node: ReactNode = null
        let onClick = () => {}
        let multipleTable = false

        const isFromLocalFile = !!data.import_create_req?.local_import_cfg
        const isCompleted = data.status === DataflowImportGetRespStatus.COMPLETED

        if (isFromLocalFile) {
          db = data.import_create_req?.local_import_cfg?.target_table?.schema!
          table = data.import_create_req?.local_import_cfg?.target_table?.table!
        } else {
          const tables = data.all_completed_tables?.map((i) => i.table_name) ?? []
          if (tables.length > 0) {
            const first = tables[0]
            ;[db, table] = first.replaceAll('`', '').split('.')

            if (tables.length > 1) {
              multipleTable = true
            }
          }
        }

        if (db && table) {
          node = <Text maw={150} span sx={{ wordBreak: 'break-all' }}>{`${db}.${table}`}</Text>

          onClick = () => {
            eventTracking(`Click Target Table Name`)
            actions.setPreloadedSQLEditor({ db, table, name: `import-task-${data.id}` })
            history.push(`/clusters/${cluster.id}/sqleditor`)
          }
        }

        if (!node) {
          return '-'
        }

        const canUseSqlEditor = sqlEditorState.visible && !sqlEditorState.disabled
        if (!isCompleted || !canUseSqlEditor) {
          return node
        }

        if (isFromLocalFile) {
          return (
            <Anchor c="peacock.7" onClick={onClick}>
              {node}
            </Anchor>
          )
        }

        return (
          <>
            {node}
            {multipleTable && (
              <>
                ,{' '}
                <Anchor
                  c="peacock.7"
                  onClick={() => {
                    setImportTaskId(data.id!)
                    setOpened(true)
                  }}
                >
                  more
                </Anchor>
              </>
            )}
          </>
        )
      }
    },
    {
      header: 'Status',
      maxSize: 120,
      accessorFn: (data) => {
        return <ImportStatus status={data.status} error={data.message} withinPortal />
      }
    },
    {
      header: 'Creation Time',
      accessorFn: renderTimeColumn('created_at', 'YYYY-MM-DD HH:mm:ss'),
      id: 'created_at'
    },
    {
      header: 'Action',
      size: 50,
      accessorFn: (data) => {
        const canCancel = canCancelImport(data.status)

        return (
          <Menu shadow="md">
            <Menu.Target>
              <ActionIcon aria-label={`${data.name} actions`} variant="subtle">
                <IconDotsHorizontal size={16} />
              </ActionIcon>
            </Menu.Target>

            <Menu.Dropdown>
              <Menu.Item
                onClick={() => {
                  setImportTaskId(data.id!)
                  setOpened(true)
                }}
              >
                View
              </Menu.Item>
              {canCancel && (
                <AccessCheck {...serverlessImportActionAccessCheckProps}>
                  {({ accessible }) => (
                    <Menu.Item
                      disabled={!accessible || cancelImport.isLoading}
                      onClick={() => {
                        handleCancelImport(data.id!)
                      }}
                    >
                      Cancel
                    </Menu.Item>
                  )}
                </AccessCheck>
              )}
            </Menu.Dropdown>
          </Menu>
        )
      }
    }
  ]

  const serverlessImportDataColumns: MRT_ColumnDef<ServerlessImport>[] = [
    {
      header: 'Name',
      Header: () => <Typography pl={16}>Name</Typography>,
      minSize: 100,
      maxSize: 400,
      accessorFn(data) {
        let name =
          data.creation_details?.source?.local?.file_name ??
          data.creation_details?.source?.s3?.uri ??
          data.creation_details?.source?.gcs?.uri ??
          data.creation_details?.source?.azure_blob?.uri ??
          data.creation_details?.source?.oss?.uri

        if (name?.endsWith('/')) {
          name = name.slice(0, -1)
        }
        name = name?.substring(name?.lastIndexOf('/') + 1)

        return (
          <Anchor
            truncate
            pl={16}
            c="peacock.7"
            maw={400}
            title={name}
            display="block"
            onClick={() => {
              setImportTaskId(data.id!)
              setOpened(true)
            }}
          >
            {name}
          </Anchor>
        )
      }
    },
    {
      header: 'Source',
      maxSize: 120,
      accessorFn(data) {
        const t = data.creation_details?.source?.type!
        return DataSourceTypeDisplayName[t]
      }
    },
    {
      header: 'Target Table',
      accessorFn(data) {
        let db = ''
        let table = ''
        let node: ReactNode = null
        let onClick = () => {}
        let multipleTable = false

        const isFromLocalFile = data.creation_details?.source?.type === ServerlessImportSourceTypeEnum.LOCAL
        const isCompleted = data.state === ServerlessImportStateEnum.COMPLETED

        if (isFromLocalFile) {
          db = data.creation_details?.source?.local?.target_database!
          table = data.creation_details?.source?.local?.target_table!
        } else {
          const tables = data.all_completed_tables?.map((i) => i.table_name!) ?? []
          if (tables.length > 0) {
            const first = tables[0]
            ;[db, table] = first.replaceAll('`', '').split('.')

            if (tables.length > 1) {
              multipleTable = true
            }
          }
        }

        if (db && table) {
          node = <Text maw={150} span sx={{ wordBreak: 'break-all' }}>{`${db}.${table}`}</Text>

          onClick = () => {
            eventTracking(`Click Target Table Name`)
            actions.setPreloadedSQLEditor({ db, table, name: `import-task-${data.id}` })
            history.push(`/clusters/${cluster.id}/sqleditor`)
          }
        }

        if (!node) {
          return '-'
        }

        const canUseSqlEditor = sqlEditorState.visible && !sqlEditorState.disabled
        if (!isCompleted || !canUseSqlEditor) {
          return node
        }

        return (
          <>
            <Anchor c="peacock.7" onClick={onClick}>
              {node}
            </Anchor>
            {multipleTable && (
              <>
                ,{' '}
                <Anchor
                  c="peacock.7"
                  onClick={() => {
                    setImportTaskId(data.id!)
                    setOpened(true)
                  }}
                >
                  more
                </Anchor>
              </>
            )}
          </>
        )
      }
    },
    {
      header: 'Status',
      maxSize: 120,
      accessorFn: (data) => {
        return <ImportStatus status={data.state!} error={data.message} withinPortal />
      }
    },
    {
      header: 'Creation Time',
      accessorFn: renderTimeColumn('create_time', 'YYYY-MM-DD HH:mm:ss'),
      id: 'create_time'
    },
    {
      header: 'Action',
      size: 50,
      accessorFn: (data) => {
        const canCancel = canCancelServerlessImport(data.state!)
        return (
          <Menu shadow="md" withinPortal position="bottom">
            <Menu.Target>
              <ActionIcon aria-label={`${data.display_name} actions`} variant="subtle">
                <IconDotsHorizontal size={16} />
              </ActionIcon>
            </Menu.Target>
            <Menu.Dropdown>
              <Menu.Item
                onClick={() => {
                  setImportTaskId(data.id!)
                  setOpened(true)
                }}
              >
                View
              </Menu.Item>
              {canCancel && (
                <AccessCheck {...serverlessImportActionAccessCheckProps}>
                  {({ accessible }) => (
                    <Menu.Item
                      disabled={!accessible || cancelImport.isLoading}
                      onClick={() => {
                        handleCancelImport(data.id!)
                      }}
                    >
                      Cancel
                    </Menu.Item>
                  )}
                </AccessCheck>
              )}
            </Menu.Dropdown>
          </Menu>
        )
      }
    }
  ]

  const pagination: ProTableProps<any>['pagination'] = {
    total: params.pages,
    value: params.page,
    showTotal: true,
    onChange(page: number) {
      setParams({
        ...params,
        page
      })
    }
  }

  useMount(() => {
    if (importTaskId && !opened) {
      setTimeout(() => {
        setOpened(true)
        const searchParams = new URLSearchParams(window.location.search)
        searchParams.delete('id')
        history.replace({ search: searchParams.toString() }) // remove url search params
      }, 600)
    }
  })

  return (
    <Stack data-testid="import-list" px={24}>
      {(isFetched || isServerlessImportFetched) && <HeroComponent />}

      <ImportDropzone
        showDropzone={isServerless}
        isInvalid={isClusterInvalid(cluster)}
        onDropFile={(files) => {
          eventTracking('Drag & drop your CSV file here or click to upload')
          const file = files[0]
          if (file) {
            if (file.size === 0) {
              notifier.warn('File should not be empty')
              return
            }
            actions.setPreloadedFileForImport(file)
            history.push(`/clusters/${cluster.id}/imports/create-import?from=local`)
          }
        }}
        onNavigateToImportCreation={(from) => {
          history.push(`/clusters/${cluster.id}/imports/create-import?from=${from}`)
        }}
        mysqlCliDocLink={
          cluster.computed.isDedicated
            ? 'https://docs.pingcap.com/tidbcloud/import-with-mysql-cli'
            : 'https://docs.pingcap.com/tidbcloud/import-with-mysql-cli-serverless'
        }
        accessCheckProps={{
          ...serverlessImportActionAccessCheckProps
        }}
      />

      {isServerless ? (
        <ProTable
          data-testid="import-list-table"
          emptyMessage="No Import Data"
          data={serverlessImportData?.imports ?? []}
          columns={serverlessImportDataColumns}
          loading={isServerlessImportLoading}
          manualPagination={true}
          pagination={serverlessImportData?.imports?.length ? pagination : undefined}
          renderEmptyRowsFallback={() => renderImportListFallback(cluster)}
        />
      ) : (
        <ProTable
          data-testid="import-list-table"
          emptyMessage="No Import Data"
          data={dedicatedImportsData?.imports ?? []}
          columns={importDataColumns}
          loading={isDedicatedImportLoading}
          manualPagination={true}
          pagination={dedicatedImportsData?.imports?.length ? pagination : undefined}
          renderEmptyRowsFallback={() => renderImportListFallback(cluster)}
          mantineTableProps={{
            highlightOnHover: false
          }}
        />
      )}

      <Drawer
        size={640}
        position="right"
        opened={opened}
        overlayProps={{ opacity: 0.5 }}
        zIndex={1000}
        styles={() => ({
          root: {
            '--drawer-height': '100vh'
          },
          content: {
            display: 'flex',
            flexDirection: 'column'
          },
          body: {
            flexGrow: 1
          }
        })}
        title={
          <Typography px={8} variant="headline-md">
            Import Task Detail
          </Typography>
        }
        onClose={() => {
          setOpened(false)
          setImportTaskId('')
        }}
      >
        <ImportTaskDetailDrawer
          id={importTaskId}
          key={importTaskId}
          open={opened}
          cluster={cluster}
          onCancel={() => {
            setOpened(false)
            refetchDedicatedImports()
          }}
        />
      </Drawer>
    </Stack>
  )
}

export default observer(Imports)
