import {
  ActionIcon,
  Alert,
  Anchor,
  Box,
  Button,
  Card,
  Code,
  Divider,
  Grid,
  Group,
  List,
  Loader,
  Modal,
  Radio,
  Select,
  Stack,
  TextInput,
  Typography,
  notifier
} from '@tidbcloud/uikit'
import { CodeBlock, DotBadge, FormErrorMessage } from '@tidbcloud/uikit/biz'
import { useDisclosure } from '@tidbcloud/uikit/hooks'
import { IconAlertTriangle, IconLinkExternal01, IconXClose } from '@tidbcloud/uikit/icons'
import { useEffect, useMemo, useState } from 'react'
import { useQueryClient } from 'react-query'

import { ReasonHoverHint, type HoverHintReason } from 'dbaas/components/HoverHint/ReasonHoverHint'
import { CloudProviderLabelConst, getCloudProviderLabel } from 'dbaas/features/Project'
import { useDualLayerDataEncryptionFeatEnabled } from 'dbaas/screens/NextGen/my-tidb-creation/shared/premium-byoc/dual-layer-data-encryption/use-dual-layer-data-encryption-feat-enabled'
import { getTidbStateToPassivePhrase } from 'dbaas/screens/NextGen/shared/model/tidb-state'
import { useGetTidbCloudRegions } from 'dbaas/screens/NextGen/tidb-creation/shared/use-cloud-providers-regions'
import { getTiDBServiceGetTidbQueryKey } from 'dbaas/services/api-clients/v2023/cluster-management/v1beta2'
import {
  getTidbServiceGetTidbDualLayerEncryptionQueryKey,
  getTidbServiceGetTidbQueryKey
} from 'dbaas/services/api-clients/v2025/tidb-management/v1beta2'
import {
  useCustomerManagedEncryptionKeyServiceGetCmekAccessIamPrincipal,
  useTidbServiceGetTidbDualLayerEncryption,
  useCustomerManagedEncryptionKeyServiceVerifyCmekAccessIamPrincipal,
  useTidbServiceUpdateTidbDualLayerEncryption
} from 'dbaas/services/api-clients/v2025/tidb-management/v1beta2'
import { getErrorMessage } from 'dbaas/services/errorCodes'
import useStores from 'dbaas/stores/useStores'

import { SharedTiDB } from '../../shared/model/shared-tidb-state'
import { default as aliyunKmsKeyPolicy } from '../creation/shared/dual-layer-data-encryption/aliyun-kms-key-policy.json'
import { default as awsKmsKeyPolicy } from '../creation/shared/dual-layer-data-encryption/aws-kms-key-policy.json'

import {
  getEffectiveEncryptionKeyType,
  supportsCustomerManagedEncryptionKey,
  type EncryptionKeyType
} from './encryption-capabilities'

export const Encryption = ({ tidb }: { tidb: SharedTiDB }) => {
  const queryClient = useQueryClient()
  const {
    store: {
      lastVisitLocation: { orgId, projectId }
    }
  } = useStores()
  const { isEnabled: isDataEncryptionFeatEnabled } = useDualLayerDataEncryptionFeatEnabled({
    servicePlan: tidb.servicePlan,
    cloudProvider: tidb.cloudProvider
  })
  const [dualLayerDataEncryptionOverride, setDualLayerDataEncryptionOverride] = useState(tidb.dualLayerDataEncryption)
  const [dualLayerEncryptionStateOverride, setDualLayerEncryptionStateOverride] = useState<
    'MODIFYING' | 'ENABLED' | undefined
  >()
  const dualLayerDataEncryptionQuery = useTidbServiceGetTidbDualLayerEncryption(
    tidb.tidbId || '',
    { orgId, projectId },
    {
      query: {
        enabled: Boolean(tidb.tidbId && orgId && projectId),
        refetchInterval: (data) => (data?.state === 'MODIFYING' ? 5_000 : false)
      }
    }
  )
  const dualLayerDataEncryption =
    dualLayerDataEncryptionQuery.data?.encryption ?? dualLayerDataEncryptionOverride ?? tidb.dualLayerDataEncryption
  const dualLayerEncryptionState = dualLayerDataEncryptionQuery.data?.state ?? dualLayerEncryptionStateOverride
  const isEncryptionModifying = dualLayerEncryptionState === 'MODIFYING'
  const cloudProvider = getCloudProviderLabel(tidb.cloudProvider)
  const isAliyun = tidb.cloudProvider === 'alicloud'
  const tidbReady = tidb?.computed.ready
  const passivePhrase = getTidbStateToPassivePhrase(tidb?.state)
  const hasEncryptionConfig = !!dualLayerDataEncryption && !dualLayerDataEncryption.noEncryption
  const isDisabled = !hasEncryptionConfig || isEncryptionModifying
  const isCustomManaged = hasEncryptionConfig && !isEncryptionModifying && !!dualLayerDataEncryption?.cmek
  const isServiceManaged = hasEncryptionConfig && !isEncryptionModifying && !isCustomManaged
  const [enableModalOpened, enableModalHandlers] = useDisclosure(false)
  const [selectedKeyType, setSelectedKeyType] = useState<EncryptionKeyType>('service-managed')
  const supportsCmek = supportsCustomerManagedEncryptionKey(tidb.servicePlan)
  const effectiveSelectedKeyType = getEffectiveEncryptionKeyType(tidb.servicePlan, selectedKeyType)
  const [selectedRegionId, setSelectedRegionId] = useState(tidb.regionId)
  const [awsCmek, setAwsCmek] = useState<{ kmsKeyArn: string; accountId: string; externalId: string }>({
    kmsKeyArn: '',
    accountId: '',
    externalId: ''
  })
  const [kmsKeyArnError, setKmsKeyArnError] = useState('')
  const { regions, isLoading: isLoadingRegions } = useGetTidbCloudRegions(tidb.servicePlan, tidb.cloudProvider ?? '')
  const notReadyReason = useMemo<HoverHintReason>(
    () => ({
      id: 'notReady',
      when: !tidbReady,
      content: `This feature is unavailable while the instance is ${passivePhrase}.`
    }),
    [passivePhrase, tidbReady]
  )

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

    const nextRegionId =
      regions.find((region) => region.regionId === selectedRegionId)?.regionId ??
      regions.find((region) => region.regionId === tidb.regionId)?.regionId ??
      regions[0]?.regionId ??
      ''

    if (nextRegionId && nextRegionId !== selectedRegionId) {
      setSelectedRegionId(nextRegionId)
    }
  }, [enableModalOpened, regions, selectedRegionId, tidb.regionId])
  const awsCmekPrincipalQuery = useCustomerManagedEncryptionKeyServiceGetCmekAccessIamPrincipal(
    {
      orgId,
      projectId,
      servicePlan: tidb.servicePlan,
      regionId: selectedRegionId
    },
    {
      query: {
        enabled: enableModalOpened && effectiveSelectedKeyType === 'customer-managed' && !!selectedRegionId
      }
    }
  )
  const keyPolicyString = useMemo(() => {
    if (tidb.cloudProvider === 'alicloud') {
      const accountId =
        awsCmekPrincipalQuery.data?.aliyun?.accountId || dualLayerDataEncryption?.cmek?.aliyunPrincipal?.accountId

      return JSON.stringify(aliyunKmsKeyPolicy, null, 2).replace('<account-id>', accountId ?? '<account-id>')
    }

    const accountId =
      awsCmek.accountId ||
      awsCmekPrincipalQuery.data?.aws?.accountId ||
      dualLayerDataEncryption?.cmek?.awsPrincipal?.accountId
    const externalId =
      awsCmek.externalId ||
      awsCmekPrincipalQuery.data?.aws?.externalId ||
      dualLayerDataEncryption?.cmek?.awsPrincipal?.externalId

    const region = selectedRegionId?.replace(/^(aws|alicloud)-/, '') ?? '<region>'

    return JSON.stringify(awsKmsKeyPolicy, null, 2)
      .replaceAll('<pingcap-account>', accountId ?? '<pingcap-account>')
      .replaceAll('<external-id>', externalId ?? '<external-id>')
      .replace('<region>', region)
  }, [
    awsCmek.accountId,
    awsCmek.externalId,
    awsCmekPrincipalQuery.data?.aliyun?.accountId,
    awsCmekPrincipalQuery.data?.aws?.accountId,
    awsCmekPrincipalQuery.data?.aws?.externalId,
    dualLayerDataEncryption?.cmek?.aliyunPrincipal?.accountId,
    dualLayerDataEncryption?.cmek?.awsPrincipal?.accountId,
    dualLayerDataEncryption?.cmek?.awsPrincipal?.externalId,
    selectedRegionId,
    tidb.cloudProvider
  ])

  const enableMutator = useTidbServiceUpdateTidbDualLayerEncryption({
    mutation: {
      onSuccess: async (data) => {
        setDualLayerDataEncryptionOverride(data.encryption)
        setDualLayerEncryptionStateOverride(data.state)
        const v2023QueryKey = getTiDBServiceGetTidbQueryKey(tidb.tidbId || '', { orgId })
        const v2025QueryKey = getTidbServiceGetTidbQueryKey(tidb.tidbId || '', { orgId, projectId })
        const encryptionQueryKey = getTidbServiceGetTidbDualLayerEncryptionQueryKey(tidb.tidbId || '', {
          orgId,
          projectId
        })

        await Promise.all([
          queryClient.invalidateQueries(v2023QueryKey, { refetchInactive: true }),
          queryClient.invalidateQueries(v2025QueryKey, { refetchInactive: true }),
          queryClient.invalidateQueries(encryptionQueryKey, { refetchInactive: true })
        ])
        setSelectedKeyType('service-managed')
        setAwsCmek({ kmsKeyArn: '', accountId: '', externalId: '' })
        setKmsKeyArnError('')
        enableModalHandlers.close()
        if (data.state === 'MODIFYING') {
          notifier.success('Dual-Layer Data Encryption is being enabled.')
          return
        }
        notifier.success('Dual-Layer Data Encryption has been enabled.')
      }
    }
  })

  const verifyCmekAccessIamPrincipalQuery = useCustomerManagedEncryptionKeyServiceVerifyCmekAccessIamPrincipal()
  const mutationError = enableMutator.error || verifyCmekAccessIamPrincipalQuery.error

  const resetMutationErrors = () => {
    enableMutator.reset()
    verifyCmekAccessIamPrincipalQuery.reset()
  }

  const handleEnable = async () => {
    if (!tidb.tidbId) {
      notifier.error('The instance is not found.')
      return
    }

    let encryption: {
      defaultKey?: {}
      cmek?:
        | {
            servicePlan: typeof tidb.servicePlan
            regionId: string
            awsKms: {
              kmsKeyArn: string
            }
            awsPrincipal: {
              accountId: string
            }
          }
        | {
            servicePlan: typeof tidb.servicePlan
            regionId: string
            aliyunKms: {
              kmsKeyArn: string
            }
            aliyunPrincipal: {
              accountId: string
            }
          }
    }

    if (effectiveSelectedKeyType === 'service-managed') {
      encryption = {
        defaultKey: {}
      }
    } else {
      const kmsKeyArn = awsCmek.kmsKeyArn.trim()
      const accountId = isAliyun
        ? (awsCmekPrincipalQuery.data?.aliyun?.accountId ?? awsCmek.accountId)
        : (awsCmekPrincipalQuery.data?.aws?.accountId ?? awsCmek.accountId)

      if (!kmsKeyArn) {
        setKmsKeyArnError('KMS Key ARN is required')
        return
      }

      const verifyResult = await verifyCmekAccessIamPrincipalQuery.mutateAsync({
        data: {
          ...(isAliyun
            ? {
                aliyunKms: {
                  kmsKeyArn
                },
                aliyunPrincipal: {
                  accountId: accountId ?? ''
                }
              }
            : {
                awsKms: {
                  kmsKeyArn
                },
                awsPrincipal: {
                  accountId: accountId ?? ''
                }
              }),
          servicePlan: tidb.servicePlan,
          regionId: selectedRegionId
        },
        params: {
          orgId,
          projectId
        }
      })

      if (!verifyResult.valid) {
        setKmsKeyArnError(verifyResult.failReason ?? 'Failed to verify KMS Key ARN')
        return
      }

      encryption = {
        cmek: {
          servicePlan: tidb.servicePlan,
          regionId: selectedRegionId,
          ...(isAliyun
            ? {
                aliyunKms: {
                  kmsKeyArn
                },
                aliyunPrincipal: {
                  accountId: accountId ?? ''
                }
              }
            : {
                awsKms: {
                  kmsKeyArn
                },
                awsPrincipal: {
                  accountId: accountId ?? ''
                }
              })
        }
      }
    }

    return enableMutator.mutateAsync({
      tidbId: tidb.tidbId,
      data: {
        encryption
      },
      params: {
        orgId,
        projectId
      }
    })
  }

  const handleCloseEnableModal = () => {
    setSelectedKeyType('service-managed')
    setSelectedRegionId(tidb.regionId)
    setAwsCmek({ kmsKeyArn: '', accountId: '', externalId: '' })
    setKmsKeyArnError('')
    resetMutationErrors()
    enableModalHandlers.close()
  }

  const provider = CloudProviderLabelConst[tidb.cloudProvider ?? '']

  if (!isDataEncryptionFeatEnabled) {
    return null
  }

  return (
    <Card mt={24}>
      <Stack gap="xs">
        <Group justify="space-between">
          <Group gap="xs">
            <Typography variant="title-lg">Dual-Layer Data Encryption</Typography>
            <DotBadge color={isDisabled ? 'gray' : 'green'}>{isDisabled ? 'Disabled' : 'Enabled'}</DotBadge>
          </Group>
          {isDisabled && (
            <ReasonHoverHint reasons={[notReadyReason]}>
              {({ matched }) => (
                <Button
                  size="sm"
                  variant="default"
                  disabled={matched}
                  onClick={enableModalHandlers.open}
                  loading={isEncryptionModifying}
                >
                  Enable
                </Button>
              )}
            </ReasonHoverHint>
          )}
        </Group>

        {isDisabled && (
          <Typography variant="body-md">
            This TiDB instance does not have Dual-Layer Data Encryption enabled. Your data will still be stored
            encrypted on disk. You can configure a KMS master key to enable Dual-Layer Data Encryption.
          </Typography>
        )}

        {isServiceManaged && (
          <Typography variant="body-md">
            This TiDB instance uses encryption at rest based on a{' '}
            <Typography variant="body-md" component="span" c="carbon.7" fw={600}>
              Service-Managed Encryption Key
            </Typography>{' '}
            from {cloudProvider} KMS for data encryption at rest. This key is for your exclusive use.
          </Typography>
        )}

        {isCustomManaged && (
          <>
            <Typography variant="body-md">
              This TiDB instance uses encryption at rest based on a{' '}
              <Typography variant="body-md" component="span" c="carbon.7" fw={600}>
                Customer-Managed Encryption Key (CMEK)
              </Typography>{' '}
              based on {cloudProvider} KMS for data encryption at rest. Please ensure your key is secure and reliable.
            </Typography>

            <Grid mt={16} align="stretch">
              <Grid.Col span={4}>
                <Box
                  p={24}
                  h="100%"
                  sx={(theme) => ({
                    borderRadius: theme.defaultRadius,
                    border: `1px solid ${theme.colors.carbon[4]}`,
                    backgroundColor: theme.colors.carbon[2]
                  })}
                >
                  <Stack h="100%">
                    <Group justify="space-between" wrap="nowrap" sx={{ flex: 1 }}>
                      <Typography variant="title-md">Cloud</Typography>
                      <Typography variant="body-lg" c="carbon.8">
                        {cloudProvider}
                      </Typography>
                    </Group>
                    <Group justify="space-between" wrap="nowrap" sx={{ flex: 1 }}>
                      <Typography variant="title-md">Region</Typography>
                      <Typography variant="body-lg" c="carbon.8" sx={{ textAlign: 'right' }}>
                        {tidb.regionDisplayName}
                      </Typography>
                    </Group>
                    <Group justify="space-between" wrap="nowrap" sx={{ flex: 1 }}>
                      <Typography variant="title-md">Key ID</Typography>
                      <Typography variant="body-lg" c="carbon.8">
                        {dualLayerDataEncryption.cmek?.customerManagedEncryptionKeyId}
                      </Typography>
                    </Group>
                  </Stack>
                </Box>
              </Grid.Col>

              <Grid.Col span={8}>
                <Box
                  sx={(theme) => ({
                    borderRadius: theme.defaultRadius,
                    border: `1px solid ${theme.colors.carbon[4]}`,
                    overflow: 'hidden',
                    '& [class*="CodeBlock_root"]': {
                      border: 0,
                      borderRadius: 0
                    }
                  })}
                >
                  <Group px={16} wrap="nowrap">
                    <Typography lh="24px" variant="title-md">
                      Suggested Key Policy
                    </Typography>
                  </Group>
                  <Divider px={16} />
                  <CodeBlock
                    language="json"
                    foldProps={{ defaultHeight: 140 }}
                    copyContent={keyPolicyString}
                    withBorder={false}
                  >
                    {keyPolicyString}
                  </CodeBlock>
                </Box>
              </Grid.Col>
            </Grid>
          </>
        )}
      </Stack>

      <Modal
        size={720}
        centered
        opened={enableModalOpened}
        onClose={handleCloseEnableModal}
        title="Enable Dual-Layer Encryption"
      >
        <Stack gap={0}>
          <Box py="xl">
            <Radio.Group
              value={effectiveSelectedKeyType}
              onChange={(value) => {
                setSelectedKeyType(value as typeof selectedKeyType)
                setKmsKeyArnError('')
                resetMutationErrors()
              }}
            >
              <Group gap="xl">
                <Radio label="Service-Managed Encryption Key" value="service-managed" />
                {supportsCmek && <Radio label="Customer-Managed Encryption Key (CMEK)" value="customer-managed" />}
              </Group>
            </Radio.Group>
          </Box>

          {effectiveSelectedKeyType === 'customer-managed' && (
            <Stack gap="md" mb="xl">
              <Box>
                <Typography variant="title-md" mb={4}>
                  Configure Key Policy
                </Typography>
                <Typography variant="body-lg">
                  Append the statements below to your {provider} KMS key policy to grant TiDB Cloud access. For details,
                  see{' '}
                  <Anchor
                    href="https://docs.aws.amazon.com/kms/latest/developerguide/key-policies.html"
                    target="_blank"
                    rel="noreferrer"
                    sx={{ display: 'inline-flex', alignItems: 'center', gap: 4 }}
                  >
                    Key Policies in {provider} KMS <IconLinkExternal01 />
                  </Anchor>
                  .
                </Typography>
              </Box>
              <Alert>
                This is a full policy document. <strong>Do not overwrite your existing policy.</strong> Copy the entries
                in <Code>Statement</Code> and append them to your {provider} KMS key policy.
              </Alert>
              <Box>
                <CodeBlock
                  language="json"
                  foldProps={{ defaultHeight: 120 }}
                  copyContent={keyPolicyString}
                  withBorder={false}
                >
                  {keyPolicyString}
                </CodeBlock>
              </Box>
              <Grid>
                <Grid.Col span={{ base: 12, sm: 4 }}>
                  <Select
                    label="Region"
                    value={selectedRegionId}
                    data={regions.map((region) => ({ value: region.regionId!, label: region.displayName! }))}
                    disabled={isLoadingRegions || regions.length <= 1}
                    rightSection={isLoadingRegions ? <Loader size="xs" /> : undefined}
                    onChange={(value) => {
                      setKmsKeyArnError('')
                      resetMutationErrors()
                      setSelectedRegionId(value ?? '')
                      setAwsCmek({ kmsKeyArn: '', accountId: '', externalId: '' })
                    }}
                  />
                </Grid.Col>
                <Grid.Col span={{ base: 12, sm: 8 }}>
                  <TextInput
                    label="KMS Key ARN"
                    placeholder={`Enter the KMS Key ARN you configured in ${provider}`}
                    value={awsCmek.kmsKeyArn}
                    error={kmsKeyArnError}
                    onChange={(event) => {
                      setKmsKeyArnError('')
                      resetMutationErrors()
                      setAwsCmek({
                        kmsKeyArn: event.currentTarget.value,
                        accountId: awsCmekPrincipalQuery.data?.aws?.accountId ?? awsCmek.accountId,
                        externalId: awsCmekPrincipalQuery.data?.aws?.externalId ?? awsCmek.externalId
                      })
                    }}
                    rightSection={
                      awsCmek.kmsKeyArn ? (
                        <ActionIcon
                          variant="transparent"
                          size="sm"
                          c="carbon.7"
                          onClick={() => {
                            setKmsKeyArnError('')
                            resetMutationErrors()
                            setAwsCmek({
                              kmsKeyArn: '',
                              accountId: awsCmekPrincipalQuery.data?.aws?.accountId ?? '',
                              externalId: awsCmekPrincipalQuery.data?.aws?.externalId ?? ''
                            })
                          }}
                        >
                          <IconXClose />
                        </ActionIcon>
                      ) : undefined
                    }
                  />
                </Grid.Col>
              </Grid>

              {mutationError && (
                <FormErrorMessage message={getErrorMessage(mutationError, true)} onDismiss={resetMutationErrors} />
              )}
              <Alert color="yellow" icon={<IconAlertTriangle />} mb="xl">
                Enabling Dual-layer Data Encryption is irreversible.
              </Alert>
            </Stack>
          )}
          {effectiveSelectedKeyType === 'service-managed' && (
            <Alert color="yellow" icon={<IconAlertTriangle />} mb="xl">
              Enabling Dual-layer Data Encryption is irreversible.
              <br />
              After encryption is enabled:
              <List>
                <List.Item>The encryption configuration of the instance cannot be disabled or modified.</List.Item>
                <List.Item>
                  Restore operations for this instance require access to the configured KMS key. If the KMS key becomes
                  unavailable, disabled, deleted, or access is revoked, restore operations may fail.
                </List.Item>
                <List.Item>
                  Restored instances will inherit the same encryption configuration and KMS key requirements.
                </List.Item>
              </List>
            </Alert>
          )}
          <Divider mx={-24} />
          <Group justify="flex-end" pt="xl">
            <Button variant="default" onClick={handleCloseEnableModal}>
              Cancel
            </Button>
            <Button
              loading={enableMutator.isLoading || verifyCmekAccessIamPrincipalQuery.isLoading}
              disabled={effectiveSelectedKeyType === 'customer-managed' && (!selectedRegionId || isLoadingRegions)}
              onClick={handleEnable}
            >
              {effectiveSelectedKeyType === 'customer-managed' ? 'Test and Add KMS Key' : 'Enable'}
            </Button>
          </Group>
        </Stack>
      </Modal>
    </Card>
  )
}
