import { Box, Button, Divider, Group, Paper, Popover, Stack, Typography } from '@tidbcloud/uikit'
import { Form, FormErrorMessage, FormRadioGroup, FormTextInput } from '@tidbcloud/uikit/biz'
import { useMemoizedFn } from 'ahooks'
import { useRef, useState } from 'react'
import { Controller, useForm } from 'react-hook-form'
import { useHistory } from 'react-router-dom'

import {
  AzureBlobStorageTemplateLinkForServerlessExport,
  DataSourceType,
  DefaultCSVFormat
} from 'dbaas/features/Import'
import { validateBucketURI } from 'dbaas/features/Import/validator'
import { CloudProviderConst } from 'dbaas/features/Project'
import { useExportServiceCreateExport } from 'dbaas/services/api-clients/legacy/serverless'
import {
  AzureBlobTarget,
  ExportAzureBlobAuthTypeEnum,
  ExportGcsAuthTypeEnum,
  ExportOSSAuthTypeEnum,
  ExportParquetCompressionTypeEnum,
  ExportServiceCreateExportBody,
  OSSTarget,
  ExportCompressionTypeEnum as ServerlessExportCompressionTypeEnum,
  ExportFileTypeEnum as ServerlessExportFileTypeEnum,
  ExportS3AuthTypeEnum as ServerlessExportS3AuthTypeEnum,
  ExportTargetTypeEnum as ServerlessExportTargetTypeEnum
} from 'dbaas/services/api-clients/legacy/serverless/interface'
import { DataflowCustomCSVFormat } from 'dbaas/services/dataflow/interface'
import { getErrorMessage } from 'dbaas/services/errorCodes'
import useStores from 'dbaas/stores/useStores'
import { MixpanelEvents } from 'dbaas/utils/tracking'

import { FormAzureSasTokenInput } from './AzureBlobAccess'
import { ClusterTableSelect } from './ClusterTableSelect'
import { ExportCSVConfig, initialValues as intialCsvConfig } from './ExportCSVConfig'
import { FormGCSBucketAccessUploader } from './GCSBucketAccessUploader'
import { AliyunBucketAccess, S3BucketAccess } from './S3BucketAccess'

type FormData = {
  taskName: string
  dataFormat: ServerlessExportFileTypeEnum
  compression: ServerlessExportCompressionTypeEnum
  parquet_format: {
    compression: ExportParquetCompressionTypeEnum
  }
  s3: {
    uri: string
    auth_type: 'manual' | 'access_key'
    aws_role_arn: string
    aws_access_key_id: string
    aws_secret_access_key: string
  }
  gcs: {
    uri: string
    auth_type: ExportGcsAuthTypeEnum
    service_account_key: string
    crendential: {
      content: string
      name: string
    }
  }
  azure_blob: AzureBlobTarget
  oss: OSSTarget
}

export function CreateExport() {
  const {
    store: { tenantData, activeProjectId, currentCluster }
  } = useStores()
  const history = useHistory()
  const clusterProvider = currentCluster?.provider
  const target = new URLSearchParams(window.location.search).get('target')
  const isS3 = target === ServerlessExportTargetTypeEnum.S3
  const isGCS = target === ServerlessExportTargetTypeEnum.GCS
  const isAzure = target === ServerlessExportTargetTypeEnum.AZURE_BLOB
  const isOSS = target === ServerlessExportTargetTypeEnum.OSS

  const defaultValues = {
    taskName: '',
    dataFormat: ServerlessExportFileTypeEnum.SQL as ServerlessExportFileTypeEnum,
    compression: ServerlessExportCompressionTypeEnum.ZSTD as ServerlessExportCompressionTypeEnum,
    parquet_format: {
      compression: ExportParquetCompressionTypeEnum.ZSTD as ExportParquetCompressionTypeEnum
    },
    s3: {
      uri: '',
      auth_type: (clusterProvider === CloudProviderConst.ALICLOUD
        ? 'access_key'
        : 'manual') as FormData['s3']['auth_type'],
      aws_role_arn: '',
      aws_access_key_id: '',
      aws_secret_access_key: ''
    },
    gcs: {
      uri: '',
      auth_type: ExportGcsAuthTypeEnum.SERVICE_ACCOUNT_KEY,
      service_account_key: '',
      crendential: {
        content: '',
        name: ''
      }
    },
    azure_blob: {
      uri: '',
      auth_type: ExportAzureBlobAuthTypeEnum.SAS_TOKEN,
      sas_token: ''
    },
    oss: {
      uri: '',
      auth_type: ExportOSSAuthTypeEnum.ACCESS_KEY,
      access_key: {
        id: '',
        secret: ''
      }
    }
  }
  const form = useForm<FormData>({
    defaultValues,
    shouldUnregister: true
  })
  const s3BucketUri = form.watch('s3.uri')
  const dataFormat = form.watch('dataFormat')
  const mutation = useExportServiceCreateExport()
  const selectedTables = useRef<string[]>([])
  const [csvFormat, setCsvFormat] = useState(intialCsvConfig)

  const handleCsvConfigChange = useMemoizedFn((csvFormat: DataflowCustomCSVFormat) => {
    setCsvFormat(csvFormat)
  })

  const onSubmit = async (values: typeof defaultValues) => {
    const data: ExportServiceCreateExportBody = {
      display_name: values.taskName,
      export_options: {
        file_type: values.dataFormat,
        filter: {
          table: {
            patterns: selectedTables.current
          }
        },
        ...(values.dataFormat === ServerlessExportFileTypeEnum.PARQUET
          ? {
              parquet_format: {
                compression: values.parquet_format.compression
              }
            }
          : {
              compression: values.compression
            }),
        ...(values.dataFormat === ServerlessExportFileTypeEnum.CSV ? { csv_format: csvFormat } : {})
      },
      target: {
        type: target as ServerlessExportTargetTypeEnum,
        ...(isS3
          ? {
              s3: {
                uri: values.s3.uri,
                auth_type:
                  values.s3.auth_type === 'manual'
                    ? ServerlessExportS3AuthTypeEnum.ROLE_ARN
                    : ServerlessExportS3AuthTypeEnum.ACCESS_KEY,
                ...(values.s3.auth_type === 'manual'
                  ? {
                      role_arn: values.s3.aws_role_arn
                    }
                  : {
                      access_key: {
                        id: values.s3.aws_access_key_id,
                        secret: values.s3.aws_secret_access_key
                      }
                    })
              }
            }
          : {}),
        ...(isGCS
          ? {
              gcs: {
                uri: values.gcs.uri,
                auth_type: ExportGcsAuthTypeEnum.SERVICE_ACCOUNT_KEY,
                service_account_key: values.gcs.crendential.content
              }
            }
          : {}),
        ...(isAzure
          ? {
              azure_blob: values.azure_blob
            }
          : {}),
        ...(isOSS
          ? {
              oss: {
                uri: values.oss.uri,
                auth_type: ExportOSSAuthTypeEnum.ACCESS_KEY,
                access_key: values.oss.access_key
              }
            }
          : {})
      }
    }

    const result = await mutation.mutateAsync({
      orgId: tenantData.id,
      projectId: activeProjectId,
      clusterId: currentCluster?.id!,
      data
    })

    history.push(`/clusters/${currentCluster?.id}/exports?exportId=${result.export_id}`)
  }

  return (
    <Form form={form} onSubmit={onSubmit} withActions={false} maw={880} m="auto">
      <Paper withBorder shadow="none">
        <Box p={24}>
          <Stack>
            <Typography variant="title-lg">General</Typography>

            <FormTextInput
              name="taskName"
              label="Task Name"
              description="Optional, it will use SNAPSHOT_[SnapshotTime] as default name"
              placeholder="e.g. snapshot-[date]"
            />
          </Stack>
        </Box>

        <Divider />

        <Box p={24}>
          <Stack>
            <Typography variant="title-lg">Exported Data</Typography>

            <Typography variant="body-md">All data will be exported by default.</Typography>

            <ClusterTableSelect
              onChange={useMemoizedFn((values) => {
                selectedTables.current = values
              })}
            />

            <FormRadioGroup
              name="dataFormat"
              label="Data Format"
              data={[
                { label: 'SQL', value: ServerlessExportFileTypeEnum.SQL },
                { label: 'CSV', value: ServerlessExportFileTypeEnum.CSV },
                { label: 'Parquet', value: ServerlessExportFileTypeEnum.PARQUET }
              ]}
            />

            {dataFormat === ServerlessExportFileTypeEnum.CSV && (
              <Box>
                <Popover position="bottom-start" withArrow shadow="md">
                  <Popover.Target>
                    <Button type="button" variant="default" size="xs">
                      Edit CSV Configuration
                    </Button>
                  </Popover.Target>
                  <Popover.Dropdown>
                    <ExportCSVConfig
                      defaultValue={{
                        ...DefaultCSVFormat,
                        ...csvFormat
                      }}
                      onChange={(config) => handleCsvConfigChange(config)}
                    />
                  </Popover.Dropdown>
                </Popover>
              </Box>
            )}

            {dataFormat === ServerlessExportFileTypeEnum.PARQUET ? (
              <FormRadioGroup
                name="parquet_format.compression"
                label="Compression"
                data={[
                  { label: 'Zstd', value: ExportParquetCompressionTypeEnum.ZSTD },
                  { label: 'Gzip', value: ExportParquetCompressionTypeEnum.GZIP },
                  { label: 'Snappy', value: ExportParquetCompressionTypeEnum.SNAPPY },
                  { label: 'None', value: ExportParquetCompressionTypeEnum.NONE }
                ]}
              />
            ) : (
              <FormRadioGroup
                name="compression"
                label="Compression"
                data={[
                  { label: 'Zstd', value: ServerlessExportCompressionTypeEnum.ZSTD },
                  { label: 'Gzip', value: ServerlessExportCompressionTypeEnum.GZIP },
                  { label: 'Snappy', value: ServerlessExportCompressionTypeEnum.SNAPPY },
                  { label: 'None', value: ServerlessExportCompressionTypeEnum.NONE }
                ]}
              />
            )}
          </Stack>
        </Box>

        <Divider />

        {isS3 && (
          <Box p={24}>
            <Stack>
              <Typography variant="title-lg">Amazon S3 Settings</Typography>

              <FormTextInput
                name="s3.uri"
                label="Folder URI"
                rules={{
                  required: 'Required',
                  validate: (val) => validateBucketURI(val, { sourceType: DataSourceType.aws, directory: true })
                }}
                placeholder="s3://export-bucket/export-folder/"
              />

              <S3BucketAccess
                bucketUri={s3BucketUri}
                bucketUriName="Folder URI"
                authTypeFormName="s3.auth_type"
                roleArnFormName="s3.aws_role_arn"
                accessKeyFormName="s3.aws_access_key_id"
                accessKeySecretFormName="s3.aws_secret_access_key"
                cloudformationTemplateUrl="https://tidbcloud-prod-public-cloudformation-templates.s3.us-west-2.amazonaws.com/export-role.json"
                cloudformationStackName={`TiDBCloud-Export-${Date.now()}`}
                roleArnDocLink="https://docs.pingcap.com/tidbcloud/configure-external-storage-access/#configure-amazon-s3-access-using-a-role-arn"
                accessKeyDocLink="https://docs.pingcap.com/tidbcloud/configure-external-storage-access/#configure-amazon-s3-access-using-an-aws-access-key"
                supportedMethods={
                  clusterProvider === CloudProviderConst.ALICLOUD ? ['access_key'] : ['manual', 'access_key']
                }
              />
            </Stack>
          </Box>
        )}

        {isGCS && (
          <Box p={24}>
            <Stack>
              <Typography variant="title-lg">Google Cloud Storage Settings</Typography>

              <FormTextInput
                name="gcs.uri"
                label="Folder URI"
                rules={{
                  required: 'Required',
                  validate: (val) => validateBucketURI(val, { sourceType: DataSourceType.gcs, directory: true })
                }}
                placeholder="gs://bucket/folder/"
              />

              <FormGCSBucketAccessUploader name="gcs.crendential" label="Bucket Access" />

              <Controller name="gcs.auth_type" render={({ field }) => <input {...field} hidden />} />
            </Stack>
          </Box>
        )}

        {isAzure && (
          <Box p={24}>
            <Stack>
              <Typography variant="title-lg">Azure Blob Storage Settings</Typography>

              <FormTextInput
                name="azure_blob.uri"
                label="Folder URI"
                rules={{
                  required: 'Required',
                  validate: (val) => validateBucketURI(val, { sourceType: DataSourceType.azure, directory: true })
                }}
                placeholder="https://myaccount.blob.core.windows.net/mycontainer/exported-data/"
              />

              <FormAzureSasTokenInput
                name="azure_blob.sas_token"
                docLink="https://docs.pingcap.com/tidbcloud/configure-external-storage-access/#configure-azure-blob-storage-access"
                templateLink={AzureBlobStorageTemplateLinkForServerlessExport}
              />

              <Controller name="azure_blob.auth_type" render={({ field }) => <input {...field} hidden />} />
            </Stack>
          </Box>
        )}

        {isOSS && (
          <Box p={24}>
            <Stack>
              <Typography variant="title-lg">Alibaba Cloud OSS Settings</Typography>

              <FormTextInput
                name="oss.uri"
                label="Folder URI"
                rules={{
                  required: 'Required',
                  validate: (val) => validateBucketURI(val, { sourceType: DataSourceType.aliyun, directory: true })
                }}
                placeholder="oss://export-bucket/export-folder/"
              />

              <AliyunBucketAccess
                accessKeyFormName="oss.access_key.id"
                accessKeySecretFormName="oss.access_key.secret"
                accessKeyDocLink="https://docs.pingcap.com/tidbcloud/configure-external-storage-access/#configure-alibaba-cloud-object-storage-service-oss-access"
              />
            </Stack>
          </Box>
        )}

        <Divider />

        {mutation.isError && <FormErrorMessage message={getErrorMessage(mutation.error, true)} />}

        <Group justify="flex-end" p={24}>
          <Button
            type="button"
            variant="default"
            onClick={() => history.push(`/clusters/${currentCluster?.id}/exports`)}
            data-mp-event={MixpanelEvents.ServerlessExport.Create.CancelClicked}
          >
            Cancel
          </Button>
          <Button
            type="submit"
            loading={mutation.isLoading}
            data-mp-event={MixpanelEvents.ServerlessExport.Create.SubmitClicked}
          >
            Export
          </Button>
        </Group>
      </Paper>
    </Form>
  )
}
