import { Alert, Button, Card, Group, notifier, Stack, Typography } from '@tidbcloud/uikit'
import { CodeBlock, Form, FormErrorMessage, FormSelect, FormTextInput } from '@tidbcloud/uikit/biz'
import { IconAlertTriangle, IconInfoCircle } from '@tidbcloud/uikit/icons'
import { useMemoizedFn } from 'ahooks'
import { observer } from 'mobx-react'
import { useMemo, useState } from 'react'
import { useForm } from 'react-hook-form'
import { useHistory, useLocation } from 'react-router-dom'

import TimeComponent from 'dbaas/components/TimeComponent'
import { SharedCluster } from 'dbaas/features/ClusterManagement/cluster'
import { DataPlatformFeatureFlag, useOrgProjectClusterIds } from 'dbaas/features/Common'
import { DataSourceType } from 'dbaas/features/Import'
import { validateBucketURI } from 'dbaas/features/Import/validator'
import { CloudProviderConst } from 'dbaas/features/Project'
import { FormGCSServiceAccountIdReadonly } from 'dbaas/screens/Cluster/ImportV3/GCSBucketAccess'
import { useRegionServiceListRegions } from 'dbaas/services/api-clients/v2023/dedicated/v1beta1'
import {
  useCreateBackupExport,
  useGetBackupExportProfile,
  useGetEnabledDPFeaturesForCluster
} from 'dbaas/services/dataflow'
import { DataflowBackupExport, DataflowGetBackupExportProfileResp } from 'dbaas/services/dataflow/interface'
import { getErrorMessage } from 'dbaas/services/errorCodes'
import useStores from 'dbaas/stores/useStores'
import { DEFAULT_TIME_FORMAT_WITH_TIMEZONE } from 'dbaas/utils/format'
import { MixpanelEvents } from 'dbaas/utils/tracking'

type CreateExportBackupForm = {
  name?: string
} & Pick<DataflowBackupExport, 's3' | 'gcs' | 'az_blob'>

const getS3PolicyCommand = (bucketUrl: string, profile: DataflowGetBackupExportProfileResp) => {
  const url = new URL(bucketUrl)
  const bucketName = url.hostname
  const path = url.pathname.substring(1) // Remove leading slash
  const resourcePath = path.endsWith('/') ? `${path}*` : `${path}/*`

  return `{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowBatchOpsWrite",
            "Effect": "Allow",
            "Principal": {
                "AWS": "${profile.s3_role_arn}"
            },
            "Action": [
                "s3:PutObject",
                "s3:PutObjectAcl"
            ],
            "Resource": "arn:aws:s3:::${bucketName}/${resourcePath}"
        }
    ]
}`
}

const CreateExportBackup = ({ cluster }: { cluster: SharedCluster }) => {
  const history = useHistory()
  const location = useLocation()
  const {
    store: { tenantData }
  } = useStores()
  const backup = useMemo(() => {
    const searchParams = new URLSearchParams(location.search)
    try {
      return JSON.parse(atob(searchParams.get('backup')!)) as { id: string; name: string; time: string }
    } catch {
      return null
    }
  }, [location])
  const backupId = backup?.id
  const backupName = backup?.name
  const { orgId, projectId } = useOrgProjectClusterIds()
  const isGCS = cluster.provider === CloudProviderConst.GCP
  const isAzure = cluster.provider === CloudProviderConst.AZURE
  const isS3 = cluster.provider === CloudProviderConst.AWS
  const { data: featuresData } = useGetEnabledDPFeaturesForCluster(tenantData.id, projectId, cluster?.id!)
  const supportBackupExport = featuresData?.features.includes(DataPlatformFeatureFlag.SupportBackupExport)

  const [s3PolicyCommand, setS3PolicyCommand] = useState<string>('')
  const form = useForm<CreateExportBackupForm>()
  const mutation = useCreateBackupExport()
  const s3Uri = form.watch('s3.target_uri')

  const {
    data: profile,
    isLoading: isLoadingProfile,
    error: profileError
  } = useGetBackupExportProfile(orgId, projectId, cluster.id, backupId!, {
    query: {
      enabled: !!backupId,
      refetchOnWindowFocus: false
    }
  })

  const { data } = useRegionServiceListRegions(
    {
      orgId,
      projectId,
      pageSize: 99
    },
    {
      query: {
        refetchOnWindowFocus: false,
        refetchOnMount: false
      }
    }
  )

  const onSubmit = useMemoizedFn(async (data: CreateExportBackupForm) => {
    await mutation.mutateAsync({
      orgId: orgId,
      projectId: projectId,
      clusterId: cluster.id,
      backupId: backupId!,
      data: {
        backup_export: {
          name: data.name,
          backup_id: backupId!,
          gcs: data.gcs!,
          az_blob: data.az_blob!,
          s3: data.s3!
        }
      }
    })

    notifier.success('Export backup task created successfully')
    history.push(`/clusters/${cluster.id}/backup?tab=exported`)
  })

  const disabled = useMemo(() => {
    if (!supportBackupExport) {
      return true
    }

    if (!backup) {
      return true
    }

    if (isGCS) {
      return !profile?.gcp_service_account
    }

    return false
  }, [isGCS, profile, backup, supportBackupExport])
  const isError = !!mutation.error || !!profileError

  return (
    <Form form={form} onSubmit={onSubmit} withActions={false} maw={720}>
      <Card p={24}>
        <Stack>
          <Typography variant="title-lg">Backup</Typography>

          {backup && (
            <Stack bg="carbon.2" sx={{ borderRadius: 8, padding: 16 }}>
              <Group>
                <Typography c="carbon.7" fw={500} miw={100}>
                  Backup Name
                </Typography>
                <Typography variant="body-lg" c="carbon.8">
                  {backupName ?? backupId}
                </Typography>
              </Group>
              <Group>
                <Typography c="carbon.7" fw={500} miw={100}>
                  Backup Time
                </Typography>
                <Typography variant="body-lg" c="carbon.8">
                  <TimeComponent
                    time={backup?.time}
                    utcOffset={tenantData.timezone}
                    format={DEFAULT_TIME_FORMAT_WITH_TIMEZONE}
                  />
                </Typography>
              </Group>
            </Stack>
          )}
        </Stack>
      </Card>

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

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

            <FormSelect
              name="s3.region"
              label="Bucket Region"
              placeholder="Select your bucket region"
              rules={{ required: 'Required' }}
              data={
                data?.regions
                  ?.filter((i) => i.cloudProvider === CloudProviderConst.AWS)
                  .map((i) => {
                    const regionId = i.regionId?.replace('aws-', '')
                    return {
                      value: regionId!,
                      label: i.displayName!
                    }
                  }) || []
              }
            />

            <div>
              <Button
                variant="default"
                disabled={!s3Uri || isLoadingProfile || !!profileError}
                onClick={async () => {
                  await form.trigger('s3.target_uri')
                  setS3PolicyCommand(getS3PolicyCommand(form.getValues('s3.target_uri'), profile!))
                }}
              >
                Generate Bucket Policy
              </Button>
            </div>

            <Typography variant="body-sm" c="carbon.7">
              Retrieve and run the policy command to grant TiDB Cloud access to your AWS sources. Learn more about{' '}
              <a
                href="https://docs.pingcap.com/tidbcloud/backup-and-restore/#export-backups"
                target="_blank"
                rel="noopener noreferrer"
              >
                Export Backup to AWS
              </a>
              .
            </Typography>

            {s3PolicyCommand && <CodeBlock language="json">{s3PolicyCommand}</CodeBlock>}

            {s3PolicyCommand && (
              <>
                <Alert
                  color="peacock"
                  title="How to apply this policy"
                  icon={<IconInfoCircle />}
                  data-testid="create-export-backup-bucket-policy-info-alert"
                >
                  In the AWS Console, navigate to the corresponding S3 bucket, switch to the "Permissions" tab, scroll
                  down to the "Bucket policy", click "Edit", then paste the policy content.
                </Alert>

                <Alert
                  color="yellow"
                  icon={<IconAlertTriangle size={14} />}
                  classNames={{ icon: 'wd-mr-0' }}
                  data-testid="create-export-backup-bucket-policy-warning-alert"
                >
                  If the bucket already has a policy, append the new "Statement" to it instead of replacing it.
                  Replacing the policy will break backups from other clusters that share the same bucket.
                </Alert>
              </>
            )}
          </Stack>
        </Card>
      )}

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

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

            <FormGCSServiceAccountIdReadonly
              gcpAccountId={profile?.gcp_service_account}
              isLoading={isLoadingProfile}
              useDefaultProfile={false}
              docUrl="https://docs.pingcap.com/tidbcloud/backup-and-restore/#export-backups-to-google-cloud-storage"
            />
          </Stack>
        </Card>
      )}

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

            <FormTextInput
              name="az_blob.target_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/"
            />
          </Stack>
        </Card>
      )}

      {isError && (
        <FormErrorMessage
          title={profileError ? 'Failed to get backup export profile' : 'Failed to export backup'}
          message={getErrorMessage(mutation.error ?? profileError)}
        />
      )}

      <Group>
        <Button
          type="submit"
          loading={mutation.isLoading}
          data-mp-event={MixpanelEvents.DedicatedBackupExport.Create.SubmitClicked}
          disabled={disabled}
        >
          Export
        </Button>
        <Button
          type="button"
          variant="default"
          onClick={() => history.push(`/clusters/${cluster.id}/backup`)}
          data-mp-event={MixpanelEvents.DedicatedBackupExport.Create.CancelClicked}
        >
          Cancel
        </Button>
      </Group>
    </Form>
  )
}

export default observer(CreateExportBackup)
