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

import { log } from 'common/log'
import { LOG_ERROR_SCENE } from 'common/log/logger'
import { SharedCluster } from 'dbaas/features/ClusterManagement/cluster'
import {
  AzureBlobStorageTemplateLinkForDedicatedImport,
  DataSourceTypeDisplayName,
  getBucketURIExample
} from 'dbaas/features/Import'
import { useImportStatus } from 'dbaas/features/Import/useImportStatus'
import { validateBucketURI } from 'dbaas/features/Import/validator'
import { FormAzureSasTokenInput } from 'dbaas/screens/Cluster/ImportV3/AzureBlobAccess'
import { FormGCSServiceAccountIdReadonly } from 'dbaas/screens/Cluster/ImportV3/GCSBucketAccess'
import PrivateLinkCheck from 'dbaas/screens/Cluster/ImportV3/PrivateLinkCheck'
import { S3BucketAccess } from 'dbaas/screens/Cluster/ImportV3/S3BucketAccess'
import { getS3BucketRegion, useValidateImport } from 'dbaas/services/dataflow'
import { DataflowImportValidationItem } from 'dbaas/services/dataflow/interface'
import { getErrorMessage } from 'dbaas/services/errorCodes'
import useStores from 'dbaas/stores/useStores'
import { eventTracking, MixpanelEvents } from 'dbaas/utils/tracking'

import { defaultMappingRow, detectDataFormatFromUri, getSingleFileDefaultMapping } from './helper'
import type { ImportFromCloudDedicatedFormData } from './types'

const azureConnectionMethodOptions = [
  {
    label: 'Public',
    value: 'direct'
  },
  {
    label: 'Private Link',
    value: 'private-link'
  }
]

function validateFlexibleBucketUri(sourceUrl: string, dataSourceType: number) {
  const trimmedSourceUrl = sourceUrl.trim()
  if (!trimmedSourceUrl) {
    return 'Required'
  }

  return trimmedSourceUrl.endsWith('/')
    ? validateBucketURI(trimmedSourceUrl, { sourceType: dataSourceType, directory: true })
    : validateBucketURI(trimmedSourceUrl, { sourceType: dataSourceType, directory: false })
}

export const ConfigureConnection = ({
  cluster,
  onCancel,
  onFinish
}: {
  cluster: SharedCluster
  onCancel: () => void
  onFinish: () => void
}) => {
  const {
    store: { tenantData, activeProjectId }
  } = useStores()
  const methods = useFormContext<ImportFromCloudDedicatedFormData>()
  const sourceUrl = methods.watch('source_url')
  const sourceType = methods.watch('data_source_type')
  const connMethod = methods.watch('conn_method')
  const endpointServiceName = methods.watch('endpoint_service_name')
  const isS3 = sourceType === 1
  const isGCS = sourceType === 2
  const isAzure = sourceType === 4
  const isPrivateLink = isAzure && connMethod === 'private-link'
  const { importTips, disableImport } = useImportStatus(tenantData.id, activeProjectId, cluster)

  const validateMutation = useValidateImport({ request: { timeout: 10 * 60 * 1000 } })
  const [privateLinkError, setPrivateLinkError] = useState('')
  const [privateLinkOpen, setPrivateLinkOpen] = useState(false)
  const [privateLinkKey, setPrivateLinkKey] = useState(0)

  const autoDetectRegion = useMemoizedFn(async () => {
    if (!isS3 || !sourceUrl) {
      return
    }

    if (validateFlexibleBucketUri(sourceUrl, sourceType)) {
      return
    }

    try {
      const data = await getS3BucketRegion({ s3_url: sourceUrl.trim() })
      if (data?.region) {
        methods.setValue('region', data.region)
      }
    } catch (error) {
      log.warning(`Auto detect import source region failed: ${getErrorMessage(error, true)}`, {
        tags: {
          scene: LOG_ERROR_SCENE.CoreJourney
        }
      })
    }
  })

  const runConnectionValidation = useMemoizedFn(async () => {
    const data = methods.getValues()
    const trimmedSourceUrl = data.source_url.trim()
    const validationSourceUrl = trimmedSourceUrl.endsWith('/')
      ? trimmedSourceUrl
      : trimmedSourceUrl.substring(0, trimmedSourceUrl.lastIndexOf('/') + 1)

    await validateMutation.mutateAsync(
      {
        orgId: tenantData.id,
        projectId: activeProjectId,
        clusterId: cluster.id,
        data: {
          source_url: validationSourceUrl,
          region: data.region.trim(),
          data_source_type: data.data_source_type,
          data_format: Number(data.data_format === 'UNKNOWN' ? 1 : data.data_format),
          aws_role_arn: data.aws_role_arn || undefined,
          aws_access_key_id: data.aws_access_key_id || undefined,
          aws_secret_access_key: data.aws_secret_access_key || undefined,
          azblob_sas_token: data.azblob_sas_token || undefined,
          check_items: [DataflowImportValidationItem.SOURCE_ACCESS_CHECK]
        }
      },
      {
        onError: (error) => {
          eventTracking(MixpanelEvents.DedicatedImport.TestConnect.RequestFailed, {
            error: getErrorMessage(error, true)
          })
        }
      }
    )
  })

  const finishConnectionStep = useMemoizedFn(() => {
    const nextSourceUrl = methods.getValues('source_url').trim()
    const isSingleFile = !nextSourceUrl.endsWith('/')
    methods.setValue('is_single_file', isSingleFile)

    if (isSingleFile) {
      methods.setValue('data_format', detectDataFormatFromUri(nextSourceUrl))
      methods.setValue('use_tidb_dumpling_naming_conventions', false)
      methods.setValue('target_table_infos', [getSingleFileDefaultMapping(nextSourceUrl)])
    } else if (!methods.getValues('target_table_infos')?.length) {
      methods.setValue('target_table_infos', [defaultMappingRow()])
    }

    onFinish()
  })

  const onSubmit = useMemoizedFn(async () => {
    const isValid = await methods.trigger()
    if (!isValid) {
      return
    }

    setPrivateLinkError('')
    if (isPrivateLink) {
      setPrivateLinkKey((prev) => prev + 1)
      setPrivateLinkOpen(true)
      return
    }

    await runConnectionValidation()
    finishConnectionStep()
  })

  return (
    <>
      <Card p={24} data-testid="import-from-cloud-dedicated-step1">
        <Stack component="form" onSubmit={methods.handleSubmit(onSubmit)}>
          <Typography variant="title-lg">Connection</Typography>

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

          <FormSelect
            name="data_source_type_display"
            label="Storage Provider"
            disabled
            data={[
              {
                label: DataSourceTypeDisplayName[sourceType],
                value: String(sourceType)
              }
            ]}
          />

          {isAzure && <FormSelect name="conn_method" label="Connectivity Method" data={azureConnectionMethodOptions} />}

          {isPrivateLink && (
            <FormTextInput
              name="endpoint_service_name"
              rules={{
                required: 'Required',
                pattern: {
                  value:
                    /^\/subscriptions\/[a-fA-F0-9-]+\/resourceGroups\/[a-zA-Z0-9_.-]+\/providers\/Microsoft\.Storage\/storageAccounts\/[a-zA-Z0-9-]+$/,
                  message: 'Invalid format'
                }
              }}
              label={
                <Group gap={8}>
                  <Typography variant="label-lg">Azure Blob Storage Resource ID</Typography>
                  <HoverCard>
                    <HoverCard.Target>
                      <Box component={IconInfoCircle} size={16} c="carbon.7" sx={{ cursor: 'pointer' }} />
                    </HoverCard.Target>
                    <HoverCard.Dropdown maw={460}>
                      Azure Portal → Storage Account Overview → JSON View, then copy the value of the id property.
                    </HoverCard.Dropdown>
                  </HoverCard>
                </Group>
              }
              placeholder="Enter the Azure Blob Storage Resource ID"
            />
          )}

          <FormTextInput
            name="source_url"
            label="Source URI"
            placeholder={getBucketURIExample(sourceType, 'multiple')}
            description="Use a folder URI for multi-file import, or a file URI for one-file manual mapping."
            onBlur={autoDetectRegion}
            rules={{
              validate: (value) => validateFlexibleBucketUri(value, sourceType)
            }}
          />

          {isS3 && (
            <S3BucketAccess
              authTypeFormName="bucketAccessMethod"
              roleArnFormName="aws_role_arn"
              accessKeyFormName="aws_access_key_id"
              accessKeySecretFormName="aws_secret_access_key"
              bucketUri={sourceUrl ?? ''}
              bucketUriName="Source 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/dedicated-external-storage/#configure-amazon-s3-access-using-a-role-arn"
              accessKeyDocLink="https://docs.pingcap.com/tidbcloud/dedicated-external-storage/#configure-amazon-s3-access-using-an-aws-access-key"
              label="Credentials"
            />
          )}

          {isGCS && (
            <FormGCSServiceAccountIdReadonly docUrl="https://docs.pingcap.com/tidbcloud/dedicated-external-storage#configure-gcs-access" />
          )}

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

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

          <Group justify="flex-end">
            <Button variant="default" onClick={onCancel}>
              Cancel
            </Button>
            <Button
              type="submit"
              disabled={disableImport}
              loading={validateMutation.isLoading}
              data-mp-event={MixpanelEvents.DedicatedImport.TestConnect.ButtonClicked}
            >
              Next
            </Button>
          </Group>
        </Stack>
      </Card>

      <PrivateLinkCheck
        key={privateLinkKey}
        opened={privateLinkOpen}
        onClose={() => {
          setPrivateLinkOpen(false)
          setPrivateLinkKey((prev) => prev + 1)
        }}
        onAvailable={async () => {
          await runConnectionValidation()
          finishConnectionStep()
        }}
        resourceId={endpointServiceName?.trim() || ''}
        onError={setPrivateLinkError}
      />
    </>
  )
}
