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

import { HeroComponent } from 'dbaas/components/Measurement'
import { PageAccessGuard } from 'dbaas/contexts/access-control'
import { useOrgProjectClusterIds } from 'dbaas/features/Common'
import { AzureBlobStorageTemplateLinkForServerlessExport, DataSourceType } from 'dbaas/features/Import'
import { validateBucketURI } from 'dbaas/features/Import/validator'
import { CloudProviderConst } from 'dbaas/features/Project'
import { FormAzureSasTokenInput } from 'dbaas/screens/Cluster/ImportV3/AzureBlobAccess'
import { ClusterTableSelect } from 'dbaas/screens/Cluster/ImportV3/ClusterTableSelect'
import { ExportCSVConfig, initialValues as initialCsvConfig } from 'dbaas/screens/Cluster/ImportV3/ExportCSVConfig'
import { FormGCSBucketAccessUploader } from 'dbaas/screens/Cluster/ImportV3/GCSBucketAccessUploader'
import { AliyunBucketAccess, S3BucketAccess } from 'dbaas/screens/Cluster/ImportV3/S3BucketAccess'
import { useSharedTiDBContext } from 'dbaas/screens/NextGen/tidb-mgmt-container/shared/tidb-context'
import { TiDBPageShell } from 'dbaas/screens/NextGen/tidb-mgmt-container/shared/TiDBPageShell'
import { useExportServiceCreateExport } from 'dbaas/services/api-clients/v2025/tidb-management/v1beta2'
import {
  Nextgenv1beta2AzureBlobCloudStorageAzureBlobAuthType,
  Nextgenv1beta2CloudStorage,
  Nextgenv1beta2CloudStorageTypeEnum,
  Nextgenv1beta2Export,
  Nextgenv1beta2ExportOptionsCompressionType,
  Nextgenv1beta2ExportOptionsFileType,
  Nextgenv1beta2GCSCloudStorageGCSAuthType,
  Nextgenv1beta2OSSCloudStorageOSSAuthType,
  Nextgenv1beta2S3CloudStorageS3AuthType
} from 'dbaas/services/api-clients/v2025/tidb-management/v1beta2/interface'
import { useCheckDatabaseConnectivity } from 'dbaas/services/dataflow'
import { getErrorMessage } from 'dbaas/services/errorCodes'
import { MixpanelEvents } from 'dbaas/utils/tracking'

import { PremiumExportPageTitle } from './PremiumExportPageTitle'

interface FormData {
  taskName: string
  username: string
  password: string
  targetType: Nextgenv1beta2CloudStorageTypeEnum
  dataFormat: Nextgenv1beta2ExportOptionsFileType
  compression: Nextgenv1beta2ExportOptionsCompressionType
  s3: {
    uri: string
    authType: 'manual' | 'access_key'
    roleArn: string
    accessKeyId: string
    secretAccessKey: string
  }
  gcs: {
    uri: string
    credential: {
      content: string
      name: string
    }
  }
  azureBlob: {
    uri: string
    sasToken: string
  }
  oss: {
    uri: string
    accessKey: {
      id: string
      secret: string
    }
  }
}

const ExportCreationPremium = () => {
  const tidb = useSharedTiDBContext()
  const history = useHistory()
  const { orgId, projectId } = useOrgProjectClusterIds()
  const createMutation = useExportServiceCreateExport()
  const testConnectionMutation = useCheckDatabaseConnectivity()
  const selectedTables = useRef<string[]>([])
  const [verifiedCredentials, setVerifiedCredentials] = useState<{ username: string; password: string } | null>(null)
  const [csvFormat, setCsvFormat] = useState({
    delimiter: initialCsvConfig.delimiter,
    nullValue: initialCsvConfig.null_value,
    separator: initialCsvConfig.separator,
    skipHeader: initialCsvConfig.skip_header
  })
  const s3SupportedMethods: FormData['s3']['authType'][] =
    tidb.cloudProvider === CloudProviderConst.AWS ? ['manual', 'access_key'] : ['access_key']

  const form = useForm<FormData>({
    defaultValues: {
      taskName: '',
      username: '',
      password: '',
      targetType: Nextgenv1beta2CloudStorageTypeEnum.S3,
      dataFormat: Nextgenv1beta2ExportOptionsFileType.SQL,
      compression: Nextgenv1beta2ExportOptionsCompressionType.ZSTD,
      s3: {
        uri: '',
        authType: s3SupportedMethods[0],
        roleArn: '',
        accessKeyId: '',
        secretAccessKey: ''
      },
      gcs: {
        uri: '',
        credential: {
          content: '',
          name: ''
        }
      },
      azureBlob: {
        uri: '',
        sasToken: ''
      },
      oss: {
        uri: '',
        accessKey: {
          id: '',
          secret: ''
        }
      }
    },
    shouldUnregister: true
  })

  const targetType = form.watch('targetType')
  const dataFormat = form.watch('dataFormat')
  const s3BucketUri = form.watch('s3.uri')
  const username = form.watch('username')
  const password = form.watch('password')

  useEffect(() => {
    if (!verifiedCredentials) {
      return
    }

    if (verifiedCredentials.username !== username || verifiedCredentials.password !== password) {
      setVerifiedCredentials(null)
    }
  }, [password, username, verifiedCredentials])

  const handleCsvConfigChange = useMemoizedFn(
    (config: { delimiter?: string; null_value?: string; separator?: string; skip_header?: boolean }) => {
      setCsvFormat({
        delimiter: config.delimiter,
        nullValue: config.null_value,
        separator: config.separator,
        skipHeader: config.skip_header
      })
    }
  )

  const buildCloudStorage = (values: FormData): Nextgenv1beta2CloudStorage => {
    switch (values.targetType) {
      case Nextgenv1beta2CloudStorageTypeEnum.S3:
        return {
          type: Nextgenv1beta2CloudStorageTypeEnum.S3,
          s3: {
            uri: values.s3.uri,
            authType:
              values.s3.authType === 'manual'
                ? Nextgenv1beta2S3CloudStorageS3AuthType.ROLE_ARN
                : Nextgenv1beta2S3CloudStorageS3AuthType.ACCESS_KEY,
            ...(values.s3.authType === 'manual'
              ? { roleArn: values.s3.roleArn }
              : {
                  accessKey: {
                    id: values.s3.accessKeyId,
                    secret: values.s3.secretAccessKey
                  }
                })
          }
        }
      case Nextgenv1beta2CloudStorageTypeEnum.GCS:
        return {
          type: Nextgenv1beta2CloudStorageTypeEnum.GCS,
          gcs: {
            uri: values.gcs.uri,
            authType: Nextgenv1beta2GCSCloudStorageGCSAuthType.SERVICE_ACCOUNT_KEY,
            serviceAccountKey: values.gcs.credential.content
          }
        }
      case Nextgenv1beta2CloudStorageTypeEnum.AZURE_BLOB:
        return {
          type: Nextgenv1beta2CloudStorageTypeEnum.AZURE_BLOB,
          azureBlob: {
            uri: values.azureBlob.uri,
            authType: Nextgenv1beta2AzureBlobCloudStorageAzureBlobAuthType.SAS_TOKEN,
            sasToken: values.azureBlob.sasToken
          }
        }
      case Nextgenv1beta2CloudStorageTypeEnum.OSS:
        return {
          type: Nextgenv1beta2CloudStorageTypeEnum.OSS,
          oss: {
            uri: values.oss.uri,
            authType: Nextgenv1beta2OSSCloudStorageOSSAuthType.ACCESS_KEY,
            accessKey: values.oss.accessKey
          }
        }
      default:
        return {
          type: Nextgenv1beta2CloudStorageTypeEnum.S3,
          s3: {
            uri: values.s3.uri,
            authType: Nextgenv1beta2S3CloudStorageS3AuthType.ROLE_ARN,
            roleArn: values.s3.roleArn
          }
        }
    }
  }

  const buildPayload = (values: FormData): Nextgenv1beta2Export => {
    return {
      displayName: values.taskName || undefined,
      username: values.username,
      password: values.password,
      cloudStorage: buildCloudStorage(values),
      exportOptions: {
        fileType: values.dataFormat,
        compression: values.compression,
        filter: {
          table: {
            patterns: selectedTables.current
          }
        },
        ...(values.dataFormat === Nextgenv1beta2ExportOptionsFileType.CSV
          ? {
              csvFormat
            }
          : {})
      }
    }
  }

  const handleTestConnection = async () => {
    const isValid = await form.trigger(['username', 'password'])

    if (!isValid) {
      return
    }

    const { username, password } = form.getValues()

    try {
      const data = await testConnectionMutation.mutateAsync({
        tidbId: tidb.tidbId!,
        data: {
          username,
          password
        }
      })

      if (data.success) {
        setVerifiedCredentials({ username, password })
        notifier.success('Connection test succeeded')
      } else if (data.errorMsg) {
        setVerifiedCredentials(null)
        notifier.error(`Connection test failed: ${data.errorMsg}`)
      } else {
        setVerifiedCredentials(null)
        notifier.error('Connection test failed')
      }
    } catch (error) {
      setVerifiedCredentials(null)
      notifier.error(getErrorMessage(error, true))
    }
  }

  const onSubmit = async (values: FormData) => {
    try {
      const result = await createMutation.mutateAsync({
        tidbId: tidb.tidbId!,
        data: buildPayload(values),
        params: {
          orgId,
          projectId
        }
      })

      history.push(`/tidbs/${tidb.tidbId}/exports?exportId=${result.exportId}`)
    } catch (error) {
      notifier.error(getErrorMessage(error, true))
    }
  }

  return (
    <TiDBPageShell
      title={<PremiumExportPageTitle page="create">Export Data</PremiumExportPageTitle>}
      headerProps={{ withBack: true }}
    >
      <HeroComponent />
      <PageAccessGuard id="TidbxExportAction" scopeProps={{ tidbId: tidb.tidbId }}>
        <Form
          form={form}
          onSubmit={onSubmit}
          withActions={false}
          maw={880}
          m="auto"
          data-testid="premium-export-create-page"
        >
          <Paper withBorder shadow="none" data-testid="premium-export-create-container">
            <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">Source Connection</Typography>

                <Box
                  p={24}
                  sx={{
                    borderRadius: 12,
                    backgroundColor: 'var(--mantine-color-carbon-2)'
                  }}
                >
                  <Stack gap={16}>
                    <Typography variant="body-lg">Enter the TiDB user credentials for this export job.</Typography>

                    <Box component="ul" m={0} pl={20}>
                      <Box component="li">
                        <Typography variant="body-lg">
                          The user must have SELECT privileges on the tables to be exported.
                        </Typography>
                      </Box>
                      <Box component="li">
                        <Typography variant="body-lg">
                          REPLICATION CLIENT is required to capture the snapshot point.
                        </Typography>
                      </Box>
                      <Box component="li">
                        <Typography variant="body-lg">SHOW VIEW is required if the export includes views.</Typography>
                      </Box>
                    </Box>

                    <Group grow align="flex-start">
                      <Box>
                        <Typography variant="body-lg" mb={8}>
                          Instance Name
                        </Typography>
                        <Typography variant="title-sm">{tidb.displayName}</Typography>
                      </Box>
                      <Box>
                        <Typography variant="body-lg" mb={8}>
                          Region
                        </Typography>
                        <Typography variant="title-sm">{tidb.regionDisplayName || tidb.regionId}</Typography>
                      </Box>
                      <Box>
                        <Typography variant="body-lg" mb={8}>
                          Instance ID
                        </Typography>
                        <Typography variant="title-sm">{tidb.tidbId}</Typography>
                      </Box>
                    </Group>
                  </Stack>
                </Box>

                <Group align="flex-end" wrap="nowrap">
                  <Box sx={{ flex: 1 }}>
                    <FormTextInput
                      name="username"
                      label="Username"
                      placeholder="Enter username"
                      rules={{ required: 'Required' }}
                    />
                  </Box>
                  <Box sx={{ flex: 1 }}>
                    <FormTextInput
                      name="password"
                      label="Password"
                      type="password"
                      placeholder="Enter password"
                      rules={{ required: 'Required' }}
                    />
                  </Box>
                  <Button
                    type="button"
                    variant="default"
                    loading={testConnectionMutation.isLoading}
                    disabled={!username || !password}
                    data-testid="premium-export-test-connection-button"
                    data-mp-event={MixpanelEvents.PremiumExport.TestConnect.ButtonClicked}
                    onClick={handleTestConnection}
                  >
                    Test Connection
                  </Button>
                </Group>

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

            <Divider />

            <Box p={24}>
              <Stack>
                <Typography variant="title-lg">Target Connection</Typography>
                <FormRadioGroup
                  name="targetType"
                  label="Storage Provider"
                  data={[
                    { label: 'Amazon S3', value: Nextgenv1beta2CloudStorageTypeEnum.S3 },
                    { label: 'Azure Blob Storage', value: Nextgenv1beta2CloudStorageTypeEnum.AZURE_BLOB },
                    { label: 'Alibaba Cloud OSS', value: Nextgenv1beta2CloudStorageTypeEnum.OSS }
                    // { label: 'Google Cloud Storage', value: Nextgenv1beta2CloudStorageTypeEnum.GCS },
                  ]}
                />

                {targetType === Nextgenv1beta2CloudStorageTypeEnum.S3 && (
                  <>
                    <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.authType"
                      roleArnFormName="s3.roleArn"
                      accessKeyFormName="s3.accessKeyId"
                      accessKeySecretFormName="s3.secretAccessKey"
                      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={s3SupportedMethods}
                    />
                  </>
                )}

                {targetType === Nextgenv1beta2CloudStorageTypeEnum.GCS && (
                  <>
                    <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.credential" label="Bucket Access" />
                  </>
                )}

                {targetType === Nextgenv1beta2CloudStorageTypeEnum.AZURE_BLOB && (
                  <>
                    <FormTextInput
                      name="azureBlob.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="azureBlob.sasToken"
                      docLink="https://docs.pingcap.com/tidbcloud/configure-external-storage-access/#configure-azure-blob-storage-access"
                      templateLink={AzureBlobStorageTemplateLinkForServerlessExport}
                      rules={{ required: 'Required' }}
                    />
                  </>
                )}

                {targetType === Nextgenv1beta2CloudStorageTypeEnum.OSS && (
                  <>
                    <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.accessKey.id"
                      accessKeySecretFormName="oss.accessKey.secret"
                      accessKeyDocLink="https://docs.pingcap.com/tidbcloud/configure-external-storage-access/#configure-alibaba-cloud-object-storage-service-oss-access"
                    />
                  </>
                )}
              </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
                  username={verifiedCredentials?.username}
                  password={verifiedCredentials?.password}
                  onChange={useMemoizedFn((values) => {
                    selectedTables.current = values
                  })}
                />
                <FormRadioGroup
                  name="dataFormat"
                  label="Data Format"
                  data={[
                    { label: 'SQL', value: Nextgenv1beta2ExportOptionsFileType.SQL },
                    { label: 'CSV', value: Nextgenv1beta2ExportOptionsFileType.CSV }
                  ]}
                />
                {dataFormat === Nextgenv1beta2ExportOptionsFileType.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={{
                            delimiter: csvFormat.delimiter,
                            null_value: csvFormat.nullValue,
                            separator: csvFormat.separator,
                            skip_header: csvFormat.skipHeader
                          }}
                          onChange={(config) => handleCsvConfigChange(config)}
                        />
                      </Popover.Dropdown>
                    </Popover>
                  </Box>
                )}
                <FormRadioGroup
                  name="compression"
                  label="Compression"
                  data={[
                    { label: 'Zstd', value: Nextgenv1beta2ExportOptionsCompressionType.ZSTD },
                    { label: 'Gzip', value: Nextgenv1beta2ExportOptionsCompressionType.GZIP },
                    { label: 'Snappy', value: Nextgenv1beta2ExportOptionsCompressionType.SNAPPY },
                    { label: 'None', value: Nextgenv1beta2ExportOptionsCompressionType.NONE }
                  ]}
                />
              </Stack>
            </Box>

            <Divider />

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

            <Group justify="flex-end" p={24}>
              <Button
                type="button"
                variant="default"
                data-testid="premium-export-create-cancel-button"
                data-mp-event={MixpanelEvents.PremiumExport.Create.CancelClicked}
                onClick={() => history.push(`/tidbs/${tidb.tidbId}/exports`)}
              >
                Cancel
              </Button>
              <Button
                type="submit"
                loading={createMutation.isLoading}
                data-testid="premium-export-create-submit-button"
                data-mp-event={MixpanelEvents.PremiumExport.Create.SubmitClicked}
              >
                Export
              </Button>
            </Group>
          </Paper>
        </Form>
      </PageAccessGuard>
    </TiDBPageShell>
  )
}

export default ExportCreationPremium
