import { Button, Group, Badge, Alert, AlertProps, Box, Typography } from '@tidbcloud/uikit'
import {
  IconDownloadCloud01,
  IconCheck,
  IconInfoCircle,
  IconCheckCircle,
  IconAlertCircle
} from '@tidbcloud/uikit/icons'
import { useMemoizedFn } from 'ahooks'
import DOMPurify from 'dompurify'
import { observer } from 'mobx-react'
import React, { useEffect, useMemo } from 'react'
import { Link } from 'react-router-dom'

import { AccessDeniedHoverHint } from 'dbaas/contexts/access-control'
import {
  useImportServiceValidateImport,
  useImportServiceCreateImport
} from 'dbaas/services/api-clients/legacy/serverless'
import {
  ConsoleImportServiceCreateImportBody,
  ImportS3AuthTypeEnum,
  ImportSourceTypeEnum,
  ImportValidationTypeEnum
} from 'dbaas/services/api-clients/legacy/serverless/interface'
import { getErrorMessage } from 'dbaas/services/errorCodes'
import useStores from 'dbaas/stores/useStores'

import { useIsClusterReady, useIsImportReady, useStarterTutorialTiDBs } from '../../helpers'

import { useCheckTableStatus } from './helper'

export interface LoadDatasetProps {
  clusterId: string
  importId?: string
  region?: string
  database: string
  tables?: string[]
  s3: string
  arn: string
  loaded?: boolean
  onLoadStart?: (id: string) => void
  onLoadTerminated?: () => void
  onLoaded?: () => void
  validateData?: boolean
}

export const LoadDataset: React.FC<React.PropsWithChildren<LoadDatasetProps>> = observer(
  ({
    clusterId,
    s3,
    arn,
    onLoaded,
    loaded,
    database,
    onLoadStart,
    importId,
    onLoadTerminated,
    tables = [],
    validateData = true
  }) => {
    const {
      store: { tenantData }
    } = useStores()
    const starterTiDBsQuery = useStarterTutorialTiDBs()
    const selectedCluster = useMemo(
      () => starterTiDBsQuery.data?.tidbs?.find((tidb) => tidb.cluster?.id === clusterId)?.cluster || null,
      [clusterId, starterTiDBsQuery.data?.tidbs]
    )
    const targetProjectId = selectedCluster?.computed.projectId || ''
    const waitingForProjectId = !!clusterId && !targetProjectId
    const { ready: clusterReady, cluster } = useIsClusterReady(clusterId, false, targetProjectId)

    const validateImport = useImportServiceValidateImport({
      request: {
        timeout: 60 * 1000
      }
    })
    const createImport = useImportServiceCreateImport({ request: { timeout: 10 * 60 * 1000 } })
    const {
      ready: importReady,
      error: importError,
      remove: removeImportStatus,
      canceled: importCanceled,
      terminated: importTerminated
    } = useIsImportReady(clusterId, loaded ? '' : importId || createImport.data?.id || '', targetProjectId)

    const {
      status: tableStatus,
      reCheck,
      isChecking,
      reset: resetCheck
    } = useCheckTableStatus({
      database,
      orgId: tenantData.id,
      projectId: targetProjectId,
      clusterId,
      tables
    })
    const resetImportState = useMemoizedFn(() => {
      createImport.reset()
      validateImport.reset()
      removeImportStatus()
    })
    const handleLoaded = useMemoizedFn(() => {
      onLoaded?.()
    })
    const handleLoadTerminated = useMemoizedFn(() => {
      onLoadTerminated?.()
    })
    const handleLoadStart = useMemoizedFn((id: string) => {
      onLoadStart?.(id)
    })

    const importHandler = async (truncateTables?: boolean) => {
      const payload: ConsoleImportServiceCreateImportBody = {
        source: {
          type: ImportSourceTypeEnum.S3,
          s3: {
            uri: s3,
            auth_type: ImportS3AuthTypeEnum.ROLE_ARN,
            role_arn: arn,
            target_table_infos: truncateTables
              ? tables.map((table) => ({ target_table: { table, database } }))
              : undefined
          }
        },
        import_options: {
          file_type: 'SQL',
          need_drop_table: truncateTables
        }
      }

      const data = await createImport.mutateAsync({
        orgId: tenantData.id,
        projectId: targetProjectId,
        clusterId,
        data: payload
      })
      handleLoadStart(data.id || '')
    }

    const validate = async (skipCheck = false, truncateTables?: boolean) => {
      try {
        // reset status
        handleLoadStart('')
        resetImportState()
        if (!loaded && !skipCheck) {
          const res = await reCheck()
          if (res) {
            handleLoadTerminated()
            return
          }
        }
        // skip validate data can save at least 5 seconds
        if (validateData) {
          await validateImport.mutateAsync({
            orgId: tenantData.id,
            projectId: targetProjectId,
            clusterId,
            data: {
              validation_type: ImportValidationTypeEnum.SOURCE_ACCESS_CHECK,
              source: {
                type: ImportSourceTypeEnum.S3,
                s3: {
                  uri: s3,
                  auth_type: ImportS3AuthTypeEnum.ROLE_ARN,
                  role_arn: arn
                }
              }
            }
          })
        }

        await importHandler(truncateTables)
      } catch (_e) {
        handleLoadTerminated()
      }
    }

    useEffect(() => {
      resetImportState()
    }, [clusterId, resetImportState])

    const error =
      getErrorMessage(validateImport.error || createImport.error) ||
      importError ||
      (importCanceled ? 'The import task is canceled.' : '')
    const isImporting =
      validateImport.isLoading ||
      createImport.isLoading ||
      ((!!createImport.data?.id || !!importId) && !importReady && !error && !loaded && !importTerminated)
    const isImported = ((createImport.isSuccess || importId) && importReady) || loaded

    useEffect(() => {
      if (isImported) {
        handleLoaded()
      }
    }, [handleLoaded, isImported])

    useEffect(() => {
      if (importTerminated || error) {
        handleLoadTerminated()
      }
    }, [error, handleLoadTerminated, importTerminated])

    useEffect(() => {
      if (isImporting && importId) {
        handleLoadStart(importId)
      }
    }, [handleLoadStart, importId, isImporting])

    const alertStyles: AlertProps['styles'] = {
      icon: {
        marginRight: 8
      }
    }

    const disabled = waitingForProjectId || (!clusterReady && !isImporting) || isChecking || loaded

    return (
      <Box pb={24}>
        <Typography variant="action-md" c="carbon.9">
          Import Dataset
        </Typography>
        <Group align="center" gap={8} mt={16}>
          <AccessDeniedHoverHint id="TidbxImportAction" scopeProps={{ tidbId: clusterId }}>
            {({ accessible }) => (
              <Button
                variant="default"
                data-event
                loading={isChecking || isImporting}
                disabled={disabled || !accessible}
                leftSection={<IconDownloadCloud01 size={16} />}
                h={32}
                styles={() => ({
                  section: { marginRight: 4 },
                  root: {
                    padding: '6px 12px'
                  },
                  label: { fontSize: 14, lineHeight: '20px', fontWeight: 700 }
                })}
                onClick={() => validate()}
                data-mp-event="mp-dataset-1691461016596"
              >
                {isImporting ? 'Importing ' : 'Import '}
                Dataset
              </Button>
            )}
          </AccessDeniedHoverHint>
          {isImported && (
            <Badge
              variant="outline"
              h={26}
              leftSection={<IconCheck size={12} />}
              px={8}
              py={4}
              styles={(theme) => ({
                root: {
                  borderColor: theme.colors.carbon[5],
                  color: theme.colors.carbon[8],
                  borderRadius: theme.defaultRadius
                },
                leftSection: {
                  display: 'flex',
                  alignItems: 'center',
                  color: theme.colors.green[5]
                }
              })}
            >
              Imported
            </Badge>
          )}
        </Group>
        {tableStatus === 'fully' && (
          <Alert icon={<IconInfoCircle size={14} />} color="yellow" styles={alertStyles} mt={16}>
            <>
              Table already exits, please check your database.{' '}
              <Box
                component="a"
                color="gray"
                onClick={() => {
                  resetCheck()
                  validate(true, true)
                }}
                sx={() => ({ ':hover': { textDecoration: 'none' } })}
              >
                Drop Table and Re-Import
              </Box>
            </>
          </Alert>
        )}
        {tableStatus === 'partial' && (
          <Alert icon={<IconInfoCircle size={14} />} color="yellow" styles={alertStyles} mt={16}>
            <>
              Some Table already exits, please check your database.{' '}
              <Link to={`/clusters/${clusterId}/sqleditor`}>Go to SQL Editor</Link>{' '}
            </>
          </Alert>
        )}
        {isImporting && (
          <Alert icon={<IconInfoCircle size={14} />} color={'peacock'} styles={alertStyles} mt={16}>
            <>
              Importing dataset to Cluster: <b>{cluster?.displayName}</b> with Database: <b>{database} </b>. This
              usually takes 60 seconds. You may leave this page and come back later to check the status.
            </>
          </Alert>
        )}
        {isImported && (
          <Alert icon={<IconCheckCircle size={14} />} color={'green'} styles={alertStyles} mt={16}>
            Dataset has been successfully imported. Click "Start" button to continue.
          </Alert>
        )}
        {!!error && (
          <Alert icon={<IconAlertCircle size={14} />} color={'red'} styles={alertStyles} mt={16}>
            <span dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(error) }}></span>
          </Alert>
        )}
      </Box>
    )
  }
)
