import { Alert, Button, Card, Divider, Group, Stack, Typography } from '@tidbcloud/uikit'
import { FormErrorMessage, FormSelect, FormTextInput } from '@tidbcloud/uikit/biz'
import { IconAlertCircle } from '@tidbcloud/uikit/icons'
import { useMemoizedFn } from 'ahooks'
import { useFormContext } from 'react-hook-form'

import { SharedCluster } from 'dbaas/features/ClusterManagement/cluster'
import { useOrgProjectClusterIds } from 'dbaas/features/Common'
import {
  AzureBlobStorageTemplateLinkForServerlessImport,
  FileExtensions,
  getBucketURIByImportSourceTypeServerless
} from 'dbaas/features/Import'
import { getImportStatusWithClusterStateCheckOnly } from 'dbaas/features/Import/useImportStatus'
import { CloudProviderConst, CloudStorageLabelConst } from 'dbaas/features/Project'
import { FormAzureSasTokenInput } from 'dbaas/screens/Cluster/ImportV3/AzureBlobAccess'
import { FormGCSBucketAccessUploader } from 'dbaas/screens/Cluster/ImportV3/GCSBucketAccessUploader'
import { AliyunBucketAccess, S3BucketAccess } from 'dbaas/screens/Cluster/ImportV3/S3BucketAccess'
import { useImportServiceValidateImport } from 'dbaas/services/api-clients/legacy/serverless'
import {
  ImportFileTypeEnum,
  ImportSourceTypeEnum,
  ImportValidationTypeEnum
} from 'dbaas/services/api-clients/legacy/serverless/interface'
import { getErrorMessage } from 'dbaas/services/errorCodes'
import { eventTracking, MixpanelEvents } from 'dbaas/utils/tracking'

import { normalizeImportSource } from './helper'
import { ImportFromCloudFormData } from './types'

const sourceTypeOptions = [
  {
    label: CloudStorageLabelConst[CloudProviderConst.AWS],
    value: ImportSourceTypeEnum.S3
  },
  {
    label: CloudStorageLabelConst[CloudProviderConst.GCP],
    value: ImportSourceTypeEnum.GCS
  },
  {
    label: CloudStorageLabelConst[CloudProviderConst.AZURE],
    value: ImportSourceTypeEnum.AZURE_BLOB
  },
  {
    label: CloudStorageLabelConst[CloudProviderConst.ALICLOUD],
    value: ImportSourceTypeEnum.OSS
  }
]

export type ConfigureConnectionFormData = {
  storage_provider: ImportSourceTypeEnum
  source_uri: string
  is_single_file: boolean
  s3_bucket_access_method: 'manual' | 'access_key'
  aws_role_arn?: string
  aws_access_key_id?: string
  aws_secret_access_key?: string
  gcs_credential?: {
    content: string
    name: string
  }
  azblob_sas_token?: string
  aliyun_access_key_id?: string
  aliyun_secret_access_key?: string
}

export const ConfigureConnection = ({
  onCancel,
  cluster,
  onFinish
}: {
  onCancel: () => void
  cluster: SharedCluster
  onFinish: (data: any) => void
}) => {
  const methods = useFormContext<ImportFromCloudFormData>()
  const sourceUri = methods.watch('source_uri')
  const storageProvider = methods.watch('storage_provider')
  const isS3 = storageProvider === ImportSourceTypeEnum.S3
  const isGCS = storageProvider === ImportSourceTypeEnum.GCS
  const isAzure = storageProvider === ImportSourceTypeEnum.AZURE_BLOB
  const isAliyun = storageProvider === ImportSourceTypeEnum.OSS
  const isAliyunCluster = cluster?.provider === CloudProviderConst.ALICLOUD
  const stateCheckTips = getImportStatusWithClusterStateCheckOnly(cluster)

  const validateMutation = useImportServiceValidateImport({ request: { timeout: 10 * 60 * 1000 } })
  const { orgId, projectId } = useOrgProjectClusterIds()

  const testBucketURIConnection = useMemoizedFn(async (data: ImportFromCloudFormData): Promise<boolean> => {
    const source = normalizeImportSource(data)
    if (!source) {
      return false
    }

    try {
      await validateMutation.mutateAsync({
        orgId,
        projectId,
        clusterId: cluster.id,
        data: {
          validation_type: ImportValidationTypeEnum.SOURCE_ACCESS_CHECK,
          source
        }
      })

      return true
    } catch (error) {
      eventTracking(MixpanelEvents.DedicatedImport.TestConnect.RequestFailed, {
        error: getErrorMessage(error, true)
      })
      return false
    }
  })

  const onSubmit = useMemoizedFn(async (data: ImportFromCloudFormData) => {
    const isConnected = await testBucketURIConnection(data)
    if (!isConnected) {
      return
    }

    // auto detect is single file
    const isSingleFile = !data.source_uri.endsWith('/')
    methods.setValue('is_single_file', isSingleFile)

    if (isSingleFile) {
      // auto detect data format if single file
      const uri = data.source_uri.toLowerCase()
      if (uri.endsWith(FileExtensions.CSV)) {
        methods.setValue('data_format', ImportFileTypeEnum.CSV)
      } else if (uri.endsWith(FileExtensions.SQL)) {
        methods.setValue('data_format', ImportFileTypeEnum.SQL)
      } else if (uri.endsWith(FileExtensions.PARQUET)) {
        methods.setValue(
          'data_format',
          uri.includes('aurora') ? ImportFileTypeEnum.AURORA_SNAPSHOT : ImportFileTypeEnum.PARQUET
        )
      }

      // auto detect if following [tidb dumpling naming conventions](https://docs.pingcap.com/tidbcloud/naming-conventions-for-data-import/)
      const filename = data.source_uri.split('/').pop() || ''
      const parts = filename.split('.')

      methods.setValue('use_tidb_dumpling_naming_conventions', false)

      methods.setValue('target_table_infos', [
        {
          target_table: {
            database: parts[0] || '',
            table: parts[1] || ''
          },
          custom_file: filename
        }
      ])
    }

    onFinish(data)
  })

  return (
    <Card p={24}>
      <Stack component="form" onSubmit={methods.handleSubmit(onSubmit)}>
        <Typography variant="title-lg">Source Connection</Typography>

        <FormSelect name="storage_provider" label="Storage Provider" data={sourceTypeOptions} />

        <FormTextInput
          name="source_uri"
          label="Source Files URI"
          placeholder={getBucketURIByImportSourceTypeServerless(storageProvider)}
        />

        {isS3 && (
          <S3BucketAccess
            label="Credential"
            authTypeFormName="s3_bucket_access_method"
            roleArnFormName="aws_role_arn"
            accessKeyFormName="aws_access_key_id"
            accessKeySecretFormName="aws_secret_access_key"
            bucketUri={sourceUri}
            bucketUriName="Source Files URI"
            cloudformationTemplateUrl="https://tidbcloud-prod-public-cloudformation-templates.s3.us-west-2.amazonaws.com/import-role.json"
            cloudformationStackName={`TiDBCloud-Import-${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={isAliyunCluster ? ['access_key'] : ['manual', 'access_key']}
          />
        )}

        {isGCS && <FormGCSBucketAccessUploader name="gcs_credential" label="Credential" />}

        {isAzure && (
          <FormAzureSasTokenInput
            name="azblob_sas_token"
            label="SAS Token"
            docLink="https://docs.pingcap.com/tidbcloud/configure-external-storage-access/#configure-azure-blob-storage-access"
            templateLink={AzureBlobStorageTemplateLinkForServerlessImport}
          />
        )}

        {isAliyun && (
          <AliyunBucketAccess
            accessKeyFormName="aliyun_access_key_id"
            accessKeySecretFormName="aliyun_secret_access_key"
            accessKeyDocLink="https://docs.pingcap.com/tidbcloud/configure-external-storage-access/#configure-alibaba-cloud-object-storage-service-oss-access"
          />
        )}

        <Divider mx={-24} />

        <FormErrorMessage message={getErrorMessage(validateMutation.error, true)} />

        {stateCheckTips && (
          <Alert color="yellow" icon={<IconAlertCircle />}>
            {stateCheckTips}
          </Alert>
        )}

        <Group justify="flex-end">
          <Button variant="default" onClick={onCancel}>
            Cancel
          </Button>
          <Button type="submit" disabled={!!stateCheckTips} loading={validateMutation.isLoading}>
            Next
          </Button>
        </Group>
      </Stack>
    </Card>
  )
}
