import { MIME_TYPES, notifier } from '@tidbcloud/uikit'
import { Button, Card, Group, Stack, Typography, Box, Alert } from '@tidbcloud/uikit'
import { FormErrorMessage } from '@tidbcloud/uikit/biz'
import { useMemoizedFn, useMount } from 'ahooks'
import { observer } from 'mobx-react'
import { useMemo, useRef, useState } from 'react'
import { Controller, FormProvider, useForm } from 'react-hook-form'
import { useHistory, useParams } from 'react-router-dom'

import { log } from 'common/log'
import { LOG_ERROR_SCENE } from 'common/log/logger'
import { useCloudImportName } from 'dbaas/features/Import/useCloudImportName'
import { useImportStatus } from 'dbaas/features/Import/useImportStatus'
import {
  DataFormat,
  DataSourceType,
  DefaultCSVFormat,
  MAX_UPLOAD_FILE_SIZE,
  NameValidator
} from 'dbaas/screens/DataImportV2/constant'
import {
  ServerlessImportFromLocalFilePreview as PreviewLocalFile,
  LocalImportConfig
} from 'dbaas/screens/DataImportV2/CreateImportFromLocal/Preview'
import { TargetInput } from 'dbaas/screens/DataImportV2/CreateImportFromLocal/TargetInput'
import {
  useImportServiceCreateImport,
  useImportServiceListTargetTables
} from 'dbaas/services/api-clients/legacy/serverless'
import {
  ConsoleImportServiceCreateImportBody as ImportServiceCreateImportBodyBody,
  ImportFileTypeEnum as ServerlessImportFileTypeEnum,
  ImportSourceTypeEnum as ServerlessImportSourceTypeEnum,
  CSVFormat as ServerlessCSVFormat
} from 'dbaas/services/api-clients/legacy/serverless/interface'
import { getErrorMessage } from 'dbaas/services/errorCodes'
import useStores from 'dbaas/stores/useStores'
import { eventTracking } from 'dbaas/utils/tracking'

import { ServerlessLocalFileUploader } from './UploadLocalFile'

interface FormData {
  source_url: string
  region: string
  data_format: string
  data_source_type: number
  local_import_cfg: {
    file_name?: string
    original_file_name?: string
    target_table?: {
      schema?: string
      table?: string
    }
    columns?: { column_name?: string; column_type?: string }[]
    pk_columns?: string[]
    total_size?: number
  }
}

const ImportFromLocalFile = () => {
  const {
    store: { tenantData, activeProjectId, currentCluster, preloadedFileForImport },
    actions
  } = useStores()
  const { id } = useParams<{ id: string }>()
  const history = useHistory()
  const cluster = currentCluster
  const urlParams = new URLSearchParams(window.location.search)
  const defaultDb = urlParams.get('db')
  const defaultTable = urlParams.get('table')
  const cloudImportName = useCloudImportName()
  const createImportUrl = `/clusters/${currentCluster?.id}/imports/create-import`
  const goToCreateImport = () => {
    history.push(createImportUrl)
    setTimeout(() => {
      notifier.clear()
    }, 300)
  }
  const uploaderRef = useRef<any>()
  const dbInputRef = useRef<HTMLInputElement>(null)
  const [csvFormat, setCsvFormat] = useState(DefaultCSVFormat)
  const methods = useForm<Partial<FormData>>({
    mode: 'onChange',
    defaultValues: {
      source_url: '',
      region: new URLSearchParams(window.location.search).get('region') || cluster?.region,
      data_format: DataFormat.csv + '',
      data_source_type: DataSourceType.local,
      local_import_cfg: {
        file_name: '',
        original_file_name: '',
        total_size: 0,
        target_table: {
          schema: defaultDb ?? '',
          table: defaultTable ?? ''
        }
      }
    }
  })

  const { control, handleSubmit, setValue, watch, formState } = methods
  const sourceType = Number(watch('data_source_type'))
  const targetDatabase = watch('local_import_cfg.target_table.schema')
  const targetTable = watch('local_import_cfg.target_table.table')
  const localImportConfig = watch('local_import_cfg')
  const [isPreviewed, setIsPreviewed] = useState(false)

  const { importTips, disableImport } = useImportStatus(tenantData.id, activeProjectId, cluster)
  const { data: clusterTableData, isLoading: isLoadingTables } = useImportServiceListTargetTables(
    tenantData.id,
    activeProjectId,
    cluster?.id ?? '',
    {
      query: {
        enabled: Boolean(cluster?.id),
        refetchOnWindowFocus: false
      }
    }
  )

  const createImportForServerlesss = useImportServiceCreateImport({
    mutation: {
      onError: (error) => {
        log.warning(`Create import from local file for serverless error, ${getErrorMessage(error, true)}`, {
          tags: {
            scene: LOG_ERROR_SCENE.CoreJourney
          }
        })
      }
    }
  })

  const clusterSchemas = useMemo(() => clusterTableData?.databases?.map((i) => i.database) ?? [], [clusterTableData])
  const clusterTables = useMemo(
    () => clusterTableData?.databases?.find((i) => i.database === targetDatabase)?.tables ?? [],
    [clusterTableData, targetDatabase]
  )
  const targetDatabaseOptions = useMemo(
    () =>
      clusterSchemas.map((i) => ({
        value: i!,
        label: i!
      })),
    [clusterSchemas]
  )
  const targetTableOptions = useMemo(
    () =>
      clusterTables.map((i) => ({
        value: i!,
        label: i!
      })),
    [clusterTables]
  )
  const isTargetingNewTable = useMemo(() => {
    return (
      targetDatabaseOptions.every((i) => i.value !== targetDatabase) ||
      targetTableOptions.every((i) => i.value !== targetTable)
    )
  }, [targetDatabaseOptions, targetDatabase, targetTableOptions, targetTable])

  const [canStartImport, setCanStartImport] = useState(true)

  const disableSubmit =
    disableImport ||
    isLoadingTables ||
    !targetDatabase ||
    !targetTable ||
    !localImportConfig?.file_name ||
    !canStartImport

  const tableColumnsRef = useRef<LocalImportConfig>()

  const onConfirmCSVChange = useMemoizedFn(async (data: ServerlessCSVFormat) => {
    setCsvFormat((prev) => ({ ...prev, ...data }))
  })

  const onCancel = useMemoizedFn(() => {
    history.push(`/clusters/${cluster?.id}/imports`)
  })
  const onSubmit = useMemoizedFn(async () => {
    if (isPreviewed) {
      handleCreateImport(tableColumnsRef.current)
    } else {
      setIsPreviewed(true)
    }
  })
  const handleCreateImport = useMemoizedFn(async (localImportConfig?: LocalImportConfig) => {
    const data: ImportServiceCreateImportBodyBody = {
      source: {
        type: ServerlessImportSourceTypeEnum.LOCAL,
        local: {
          upload_id: localImportConfig?.file_name!,
          target_database: localImportConfig?.target_table?.schema!,
          target_table: localImportConfig?.target_table?.table!,
          file_name: localImportConfig?.original_file_name!,
          columns: localImportConfig?.columns ?? [],
          pk_columns: localImportConfig?.pk_columns ?? []
        }
      },
      import_options: {
        file_type: ServerlessImportFileTypeEnum.CSV,
        csv_format: csvFormat
      }
    }

    const res = await createImportForServerlesss.mutateAsync({
      orgId: tenantData.id,
      projectId: activeProjectId,
      clusterId: id,
      data
    })
    history.push(`/clusters/${id}/imports?id=${res?.id}`)
  })

  useMount(() => {
    if (preloadedFileForImport) {
      uploaderRef.current?.upload(preloadedFileForImport)
      actions.setPreloadedFileForImport(null)
    }
  })

  return (
    <FormProvider {...methods}>
      <Box component="form" onSubmit={handleSubmit(onSubmit)} maw={1200}>
        {importTips.length > 0 && (
          <Alert color="yellow" mb={16}>
            {importTips}
          </Alert>
        )}

        <Card shadow="none" withBorder p={24} mb={24} w={720}>
          <Stack>
            <Typography variant="title-lg">Source</Typography>

            <ServerlessLocalFileUploader
              ref={uploaderRef}
              orgId={tenantData.id}
              projectId={activeProjectId}
              clusterId={currentCluster?.id!}
              accept={[MIME_TYPES.csv]}
              region={cluster?.region}
              onBeforeUpload={(file) => {
                if (file.size > MAX_UPLOAD_FILE_SIZE.value) {
                  notifier.warn(
                    <span>
                      The file exceeds the {MAX_UPLOAD_FILE_SIZE.label} limit for local upload. Please upload the file
                      to {cloudImportName} and then import data to TiDB Cloud from {cloudImportName}
                    </span>
                  )
                  return false
                } else if (file.size === 0) {
                  notifier.warn('File should not be empty')
                  return false
                }
                eventTracking(`Upload a local file started`, {
                  type: file.type
                })
              }}
              onReject={(errors) => {
                if (errors.length > 1) {
                  notifier.warn('Only one file is supported')
                  return
                }
                const error = errors[0]
                const size = error.file.size
                if (size > MAX_UPLOAD_FILE_SIZE.value) {
                  notifier.warn(
                    <span>
                      The file exceeds the {MAX_UPLOAD_FILE_SIZE.label} limit for local upload. Please upload the file
                      to {cloudImportName} and then import data to TiDB Cloud from {cloudImportName}
                      then <a onClick={goToCreateImport}>import data to TiDB Cloud from {cloudImportName}</a>.
                    </span>
                  )
                }
              }}
              onSuccess={(res, file) => {
                setTimeout(() => dbInputRef.current?.focus())
                setValue('local_import_cfg.original_file_name', file.name)
                setValue('local_import_cfg.file_name', res.upload_id)
                setValue('local_import_cfg.total_size', file.size)
                eventTracking(`Upload a local file succeed`)
              }}
              onCancel={() => {
                eventTracking(`Upload a local file canceled`)
              }}
              onReset={() => {
                setValue('local_import_cfg.file_name', '')
                setValue('local_import_cfg.original_file_name', '')
                setIsPreviewed(false)
                // Reset import state: re-enable import after a user clears the file following any data validation error
                setCanStartImport(true)
              }}
              onError={(e) => {
                eventTracking(`Upload a local file error`)
                log.warning('Upload a local file error', e as any, {
                  tags: {
                    scene: LOG_ERROR_SCENE.CoreJourney
                  }
                })
              }}
            />

            <Controller
              name="local_import_cfg.file_name"
              control={control}
              render={({ field }) => <input {...field} hidden />}
            />
            <Controller
              name="local_import_cfg.original_file_name"
              control={control}
              render={({ field }) => <input {...field} hidden />}
            />
            <Controller
              name="local_import_cfg.total_size"
              control={control}
              render={({ field }) => <input {...field} hidden />}
            />
          </Stack>
        </Card>

        <Controller name="region" control={control} render={({ field }) => <input {...field} hidden />} />

        <Card shadow="none" withBorder p={24} mb={24} w={720}>
          <Stack>
            <Typography variant="title-lg">Destination</Typography>

            <Controller
              name="local_import_cfg.target_table.schema"
              control={control}
              rules={{ validate: sourceType === DataSourceType.local ? NameValidator : undefined }}
              render={({ field }) => (
                <TargetInput
                  ref={dbInputRef}
                  label="Database"
                  data={targetDatabaseOptions}
                  placeholder="Enter to create a new database"
                  error={formState.errors?.local_import_cfg?.target_table?.schema?.message}
                  getCreateLabel={(val) => (
                    <Group gap={4}>
                      <b>+ Create new database:</b>
                      {val}
                    </Group>
                  )}
                  value={field.value}
                  onChange={(val) => {
                    field.onChange(val)
                  }}
                  width={500}
                  data-testid="create-import-target-database"
                />
              )}
            />

            <Controller
              name="local_import_cfg.target_table.table"
              control={control}
              rules={{ validate: sourceType === DataSourceType.local ? NameValidator : undefined }}
              render={({ field }) => (
                <TargetInput
                  label="Table"
                  placeholder="Enter to create a new table"
                  data={targetTableOptions}
                  disabled={!targetDatabase}
                  error={formState.errors?.local_import_cfg?.target_table?.table?.message}
                  getCreateLabel={(val) => (
                    <Group gap={4}>
                      <b>+ Create new table:</b>
                      {val}
                    </Group>
                  )}
                  value={field.value}
                  onChange={(val) => {
                    field.onChange(val)
                  }}
                  width={500}
                  data-testid="create-import-target-table"
                />
              )}
            />
          </Stack>
        </Card>

        {isPreviewed && localImportConfig?.file_name && (
          <Card shadow="none" withBorder p={24} mb={24} w={720}>
            <PreviewLocalFile
              title="Table Definition"
              csvFormat={csvFormat}
              isTargetingNewTable={isTargetingNewTable}
              localImportConfig={localImportConfig!}
              handleCsvConfigChange={onConfirmCSVChange}
              onDataChange={(data, canStartImport) => {
                tableColumnsRef.current = data
                setCanStartImport(canStartImport)
              }}
            />
          </Card>
        )}

        <FormErrorMessage
          message={getErrorMessage(createImportForServerlesss.error, true)}
          sx={{ wordBreak: 'break-all', marginBottom: 16 }}
        />

        <Group>
          <Button
            type="submit"
            loading={createImportForServerlesss.isLoading}
            disabled={disableSubmit}
            data-mp-event="Create Import Submit Clicked"
            data-testid="create-import-submit"
          >
            {isPreviewed ? `Start Import` : `Define Table`}
          </Button>

          <Button type="button" variant="default" data-mp-event="Create Import Cancel Clicked" onClick={onCancel}>
            Cancel
          </Button>
        </Group>
      </Box>
    </FormProvider>
  )
}

export default observer(ImportFromLocalFile)
