import {
  ActionIcon,
  Alert,
  Box,
  Button,
  Card,
  Center,
  Divider,
  Grid,
  Group,
  Loader,
  NumberInput,
  Paper,
  Select,
  Stack,
  Switch,
  TextInput,
  Typography,
  notifier
} from '@tidbcloud/uikit'
import { ExpandNavbarButtonPlaceholder, PageShellBase } from '@tidbcloud/uikit/biz'
import { useDebouncedValue } from '@tidbcloud/uikit/hooks'
import { IconInfoCircle, IconPlus, IconXClose } from '@tidbcloud/uikit/icons'
import { Fragment, useCallback, useEffect, useMemo, useState } from 'react'
import { useQuery, useQueryClient } from 'react-query'
import { useHistory, useLocation } from 'react-router-dom'

import { PropertyKV, Tag } from 'dbaas/components'
import { AccessDeniedHoverHint, PageAccessGuard } from 'dbaas/contexts/access-control'
import { ComponentBlock } from 'dbaas/features/ClusterManagement/dedicated'
import { TwoPaneLayout } from 'dbaas/screens/NextGen/my-tidb-creation/layout/TwoPaneLayout'
import { FormItem } from 'dbaas/screens/NextGen/my-tidb-creation/shared/FormItem'
import {
  useGetTidbCloudProviders,
  useGetTidbCloudRegions
} from 'dbaas/screens/NextGen/my-tidb-creation/shared/premium-byoc/basic-settings/use-cloud-providers-regions'
import { useAddCreditCard } from 'dbaas/screens/NextGen/my-tidb-creation/shared/use-add-credit-card'
import { PaymentMethodModal } from 'dbaas/screens/Stripe/PaymentMethod/PaymentMethodModal'
import {
  useRegionServiceListNodeSpecs,
  useResourcePoolServiceCreateResourcePool
} from 'dbaas/services/api-clients/v2025/tidb-management/v1beta2'
import {
  Commonv1beta1ServicePlan,
  Nextgenv1beta2ComponentCapacity,
  Nextgenv1beta2NodeSpec,
  RegionServiceListNodeSpecsComponentType
} from 'dbaas/services/api-clients/v2025/tidb-management/v1beta2/interface'
import { getErrorMessage } from 'dbaas/services/errorCodes'
import useStores from 'dbaas/stores/useStores'
import { cidrRegEx } from 'dbaas/utils/regex'
import { MixpanelEvents } from 'dbaas/utils/tracking'

import { planResourcePoolCidr, toHighAvailabilityType, validateResourcePoolCidr } from './api'
import { ResourcePoolFormValue } from './types'

const SILENT_BYOC_QUERY_REQUEST = { isShowToast: false, isHandleErr: false }
const RESOURCE_POOLS_QUERY_KEY = ['/api/v2025/cloud/v1beta2/resourcePools']

const DEFAULT_VALUE: ResourcePoolFormValue = {
  name: '',
  cloud: 'AWS',
  region: '',
  poolVcpuLimitEnabled: false,
  poolVcpuLimit: 512,
  poolCidr: '',
  highAvailability: null,
  tikvNodeSpecKey: '',
  tikvVcpu: '4 vCPU',
  tikvMemory: '16 GiB',
  tikvStorage: '300 GiB',
  tikvNodes: 3,
  tiflashEnabled: false,
  tiflashNodeSpecKey: '',
  tiflashVcpu: '4 vCPU',
  tiflashMemory: '16 GiB',
  tiflashStorage: '300 GiB',
  tiflashNodes: 3,
  tags: []
}

const DEFAULT_TIKV_NODES = 3
const DEFAULT_TIFLASH_NODES = 3

const getPrimaryStorage = (spec?: Nextgenv1beta2NodeSpec) =>
  spec?.storages?.find((storage) => storage.name === 'data') ?? spec?.storages?.[0]

const toStorageSize = (value: string, fallback = '0') => String(Number.parseInt(value, 10) || Number(fallback))
const toOptionalNumber = (value?: string) => (value ? Number(value) : undefined)
const getNodesSuffix = (value: number) => (value === 1 ? ' node' : ' nodes')
const INVALID_CIDR_MESSAGE = 'This is not a valid CIDR for TiDB Cloud.'
const CIDR_PLAN_ERROR_MESSAGE = 'CIDR recommendations could not be loaded. You can enter a CIDR manually and retry.'
const INITIAL_CAPACITY_LIMIT_MESSAGE =
  'If a Pool vCPU Limit is set, the initial TiKV and TiFlash vCPU cannot exceed 50% of the limit. The remaining capacity is reserved for TiDB and other database-related components.'
const AWS_TAG_KEY_MAX_LENGTH = 128
const AWS_TAG_VALUE_MAX_LENGTH = 256
const AWS_RESERVED_TAG_KEY = 'aws'
const AWS_RESERVED_TAG_KEY_PREFIX = `${AWS_RESERVED_TAG_KEY}:`
const isAwsReservedTagKey = (key: string) => {
  const normalizedKey = key.trim().toLowerCase()

  return normalizedKey === AWS_RESERVED_TAG_KEY || normalizedKey.startsWith(AWS_RESERVED_TAG_KEY_PREFIX)
}
const toAddressRangeLabel = (cidr: string) =>
  cidr === '10.0.0.0/8'
    ? '10.0.0.0 – 10.255.255.255'
    : cidr === '172.16.0.0/12'
      ? '172.16.0.0 – 172.31.255.255'
      : cidr === '192.168.0.0/16'
        ? '192.168.0.0 – 192.168.255.255'
        : cidr
const toPrefixRangeLabel = ({
  minPrefixLength,
  maxPrefixLength
}: {
  minPrefixLength?: number
  maxPrefixLength?: number
}) => {
  if (!minPrefixLength || !maxPrefixLength) {
    return ''
  }

  return minPrefixLength === maxPrefixLength ? `/${minPrefixLength}` : `/${minPrefixLength} and /${maxPrefixLength}`
}

const toComponentCapacity = (
  spec: Nextgenv1beta2NodeSpec,
  nodeCount: number,
  primaryStorageSize: string
): Nextgenv1beta2ComponentCapacity => {
  const primaryStorage = getPrimaryStorage(spec)

  return {
    nodeSpec: { cpu: spec.cpu!, memoryGi: spec.memoryGi! },
    nodeCount: String(nodeCount),
    storages: spec.storages
      ?.filter((storage) => storage.name && storage.defaultSizeGi)
      .map((storage) => ({
        name: storage.name!,
        sizeGi:
          storage.name === primaryStorage?.name
            ? toStorageSize(primaryStorageSize, storage.defaultSizeGi)
            : storage.defaultSizeGi!
      }))
  }
}

export const ResourcePoolCreatePage = () => {
  const history = useHistory()
  const location = useLocation()
  const [value, setValue] = useState(DEFAULT_VALUE)
  const [formError, setFormError] = useState('')
  const [poolCidrDirty, setPoolCidrDirty] = useState(false)
  const [poolCidrError, setPoolCidrError] = useState('')
  const [highAvailabilityTouched, setHighAvailabilityTouched] = useState(false)
  const {
    store: {
      lastVisitLocation: { orgId, projectId }
    }
  } = useStores()
  const queryClient = useQueryClient()
  const servicePlan = Commonv1beta1ServicePlan.BYOC
  const { creditCardNeeded, creditCardModalVisible, creditCardModalHandlers } = useAddCreditCard({
    checkRemainingCredits: true
  })
  const {
    cloudProviders,
    isLoading: cloudProvidersLoading,
    isError: cloudProvidersError
  } = useGetTidbCloudProviders(servicePlan, { projectId })
  const {
    regions,
    isLoading: regionsLoading,
    isError: regionsError
  } = useGetTidbCloudRegions(servicePlan, value.cloud.toLowerCase(), { projectId })
  const nodeSpecParams = {
    organizationId: orgId,
    orgId,
    projectId,
    pageSize: 100
  }
  const tikvSpecsQuery = useRegionServiceListNodeSpecs(
    value.region,
    {
      ...nodeSpecParams,
      componentType: RegionServiceListNodeSpecsComponentType.NODE_SPEC_COMPONENT_TYPE_TIKV
    },
    {
      query: {
        enabled: !!orgId && !!projectId && !!value.region,
        retry: 1,
        refetchOnWindowFocus: false,
        refetchOnMount: false
      },
      request: SILENT_BYOC_QUERY_REQUEST
    }
  )
  const tiflashSpecsQuery = useRegionServiceListNodeSpecs(
    value.region,
    {
      ...nodeSpecParams,
      componentType: RegionServiceListNodeSpecsComponentType.NODE_SPEC_COMPONENT_TYPE_TIFLASH
    },
    {
      query: { enabled: !!orgId && !!projectId && !!value.region, retry: 1 },
      request: SILENT_BYOC_QUERY_REQUEST
    }
  )
  const createMutation = useResourcePoolServiceCreateResourcePool()
  const configuredTags = useMemo(() => value.tags.filter((tag) => tag.key.trim() || tag.value.trim()), [value.tags])

  const update = useCallback(<K extends keyof ResourcePoolFormValue>(key: K, nextValue: ResourcePoolFormValue[K]) => {
    setFormError('')
    if (key === 'poolCidr') {
      setPoolCidrError('')
    }
    setValue((current) => ({ ...current, [key]: nextValue }))
  }, [])

  const addTag = () => update('tags', [...value.tags, { key: '', value: '' }])
  const tikvSpec = useMemo(
    () => tikvSpecsQuery.data?.nodeSpecs?.find((spec) => spec.nodeSpecKey === value.tikvNodeSpecKey),
    [tikvSpecsQuery.data?.nodeSpecs, value.tikvNodeSpecKey]
  )
  const tiflashSpec = useMemo(
    () => tiflashSpecsQuery.data?.nodeSpecs?.find((spec) => spec.nodeSpecKey === value.tiflashNodeSpecKey),
    [tiflashSpecsQuery.data?.nodeSpecs, value.tiflashNodeSpecKey]
  )
  const duplicateTagKeys = useMemo(() => {
    const seen = new Set<string>()
    const duplicates = new Set<string>()

    configuredTags.forEach((tag) => {
      const key = tag.key.trim()
      if (!key) {
        return
      }
      if (seen.has(key)) {
        duplicates.add(key)
      } else {
        seen.add(key)
      }
    })

    return duplicates
  }, [configuredTags])
  const hasIncompleteTags = configuredTags.some((tag) => !tag.key.trim())
  const invalidTagKeyLengths = useMemo(
    () =>
      new Set(
        configuredTags.filter((tag) => tag.key.trim().length > AWS_TAG_KEY_MAX_LENGTH).map((tag) => tag.key.trim())
      ),
    [configuredTags]
  )
  const awsReservedTagKeys = useMemo(
    () => new Set(configuredTags.filter((tag) => isAwsReservedTagKey(tag.key)).map((tag) => tag.key.trim())),
    [configuredTags]
  )
  const invalidTagValueIndexes = useMemo(
    () =>
      new Set(
        value.tags
          .map((tag, index) => ({ index, value: tag.value.trim() }))
          .filter((tag) => tag.value.length > AWS_TAG_VALUE_MAX_LENGTH)
          .map((tag) => tag.index)
      ),
    [value.tags]
  )
  const hasInvalidTags =
    hasIncompleteTags ||
    duplicateTagKeys.size > 0 ||
    invalidTagKeyLengths.size > 0 ||
    awsReservedTagKeys.size > 0 ||
    invalidTagValueIndexes.size > 0
  const nodeSpecsError = tikvSpecsQuery.isError || tiflashSpecsQuery.isError
  const basicSettingsError = cloudProvidersError || regionsError
  const highAvailabilityType = value.highAvailability ? toHighAvailabilityType(value.highAvailability) : undefined

  const buildCreateRequest = useCallback(
    (poolCidr = value.poolCidr) => {
      if (!tikvSpec || !highAvailabilityType) {
        return undefined
      }

      return {
        displayName: value.name.trim(),
        regionId: value.region,
        highAvailabilityType,
        vpcCidr: poolCidr.trim(),
        vcpuLimit: value.poolVcpuLimitEnabled ? { limitedVcpus: String(value.poolVcpuLimit) } : { unlimited: true },
        capacity: {
          tikv: toComponentCapacity(tikvSpec, value.tikvNodes, value.tikvStorage),
          ...(value.tiflashEnabled && tiflashSpec
            ? { tiflash: toComponentCapacity(tiflashSpec, value.tiflashNodes, value.tiflashStorage) }
            : {})
        },
        ...(configuredTags.length
          ? { cloudResourceTags: Object.fromEntries(configuredTags.map((tag) => [tag.key.trim(), tag.value.trim()])) }
          : {})
      }
    },
    [
      configuredTags,
      tiflashSpec,
      tikvSpec,
      value.name,
      value.poolCidr,
      value.poolVcpuLimit,
      value.poolVcpuLimitEnabled,
      value.region,
      highAvailabilityType,
      value.tiflashEnabled,
      value.tiflashNodes,
      value.tiflashStorage,
      value.tikvNodes,
      value.tikvStorage
    ]
  )

  const cidrPlanQuery = useQuery(
    ['planResourcePoolCidr', orgId, value.region, highAvailabilityType],
    () => planResourcePoolCidr(value.region, orgId, { highAvailabilityType: highAvailabilityType! }),
    {
      enabled: !!(orgId && value.region && highAvailabilityType),
      refetchOnWindowFocus: false,
      retry: false,
      onSuccess: (data) => {
        const recommendedCidr = data.recommendationStatus === 'AVAILABLE' ? data.recommendation?.cidr : ''
        if (!poolCidrDirty || poolCidrError) {
          update('poolCidr', recommendedCidr ?? '')
          setPoolCidrError('')
        }
      }
    }
  )
  const [debouncedPoolCidr] = useDebouncedValue(value.poolCidr, 400)
  const poolCidrFormatError =
    debouncedPoolCidr.trim() && !cidrRegEx.test(debouncedPoolCidr.trim()) ? INVALID_CIDR_MESSAGE : ''
  const cidrValidationQuery = useQuery(
    ['validateResourcePoolCidr', orgId, value.region, debouncedPoolCidr, highAvailabilityType],
    () =>
      validateResourcePoolCidr(value.region, orgId, {
        cidr: debouncedPoolCidr.trim(),
        highAvailabilityType: highAvailabilityType!
      }),
    {
      enabled: !!(
        orgId &&
        value.region &&
        highAvailabilityType &&
        debouncedPoolCidr.trim() &&
        cidrRegEx.test(debouncedPoolCidr.trim())
      ),
      refetchOnWindowFocus: false,
      retry: false,
      onSuccess: (data) => {
        setPoolCidrError(
          data.blockingFindings
            ?.map((finding) => finding.message)
            .filter(Boolean)
            .join('\n') ?? ''
        )
      },
      onError: (error) => {
        const message = getErrorMessage(error, true) || INVALID_CIDR_MESSAGE
        setPoolCidrError(message)
      }
    }
  )
  const supportedPrivateCidrs = cidrPlanQuery.data?.policy?.supportedPrivateCidrs ?? [
    '10.0.0.0/8',
    '172.16.0.0/12',
    '192.168.0.0/16'
  ]
  const allowedPrefixRanges = cidrPlanQuery.data?.policy?.allowedPrefixRanges ?? [
    { minPrefixLength: 16, maxPrefixLength: 23 }
  ]
  const prefixRangeLabel = allowedPrefixRanges.map(toPrefixRangeLabel).filter(Boolean).join(', ')
  const exclusionCidrs = cidrPlanQuery.data?.exclusions?.map((exclusion) => exclusion.cidr).filter(Boolean) ?? []
  const trimmedPoolCidr = value.poolCidr.trim()
  const recommendation = cidrPlanQuery.data?.recommendation
  const supportedNodeCount =
    cidrValidationQuery.data?.cidr === trimmedPoolCidr
      ? cidrValidationQuery.data?.supportedNodeCount
      : recommendation?.cidr === trimmedPoolCidr
        ? recommendation.supportedNodeCount
        : undefined
  const blockingFindings = cidrValidationQuery.data?.blockingFindings ?? []
  const cidrError = poolCidrFormatError || poolCidrError
  const cidrValidationPending = value.poolCidr.trim() !== debouncedPoolCidr.trim() || cidrValidationQuery.isFetching
  const cidrValidationRequired = !!(value.poolCidr.trim() && cidrRegEx.test(value.poolCidr.trim()))
  const cidrLoading = !!highAvailabilityType && (cidrPlanQuery.isLoading || cidrValidationPending)
  const initialCapacityVcpu =
    Number(tikvSpec?.cpu ?? 0) * value.tikvNodes +
    (value.tiflashEnabled ? Number(tiflashSpec?.cpu ?? 0) * value.tiflashNodes : 0)
  const initialCapacityExceedsLimit = !!(
    value.poolVcpuLimitEnabled &&
    value.poolVcpuLimit &&
    initialCapacityVcpu > value.poolVcpuLimit / 2
  )
  const submitDisabled =
    !value.name.trim() ||
    !value.highAvailability ||
    !value.poolCidr.trim() ||
    !!cidrError ||
    cidrValidationQuery.isError ||
    cidrValidationPending ||
    (cidrValidationRequired && (!cidrValidationQuery.isSuccess || blockingFindings.length > 0)) ||
    !value.region ||
    !tikvSpec?.cpu ||
    !tikvSpec.memoryGi ||
    (value.poolVcpuLimitEnabled && !value.poolVcpuLimit) ||
    initialCapacityExceedsLimit ||
    (value.tiflashEnabled && (!tiflashSpec?.cpu || !tiflashSpec.memoryGi)) ||
    hasInvalidTags ||
    createMutation.isLoading

  useEffect(() => {
    const provider = cloudProviders[0]
    if (provider && !cloudProviders.some((item) => item.toUpperCase() === value.cloud)) {
      update('cloud', provider.toUpperCase() === 'AZURE' ? 'Azure' : 'AWS')
    }
  }, [cloudProviders, update, value.cloud])

  useEffect(() => {
    if (!regions.some((region) => region.regionId === value.region)) {
      update('region', regions[0]?.regionId ?? '')
    }
  }, [regions, update, value.region])

  useEffect(() => {
    const spec = tikvSpecsQuery.data?.nodeSpecs?.find((item) => item.isDefault) ?? tikvSpecsQuery.data?.nodeSpecs?.[0]
    if (
      !spec?.nodeSpecKey ||
      tikvSpecsQuery.data?.nodeSpecs?.some((item) => item.nodeSpecKey === value.tikvNodeSpecKey)
    ) {
      return
    }
    update('tikvNodeSpecKey', spec.nodeSpecKey)
    update('tikvVcpu', `${spec.cpu} vCPU`)
    update('tikvMemory', `${spec.memoryGi} GiB`)
    update('tikvStorage', `${getPrimaryStorage(spec)?.defaultSizeGi ?? 0} GiB`)
    update('tikvNodes', Number(tikvSpecsQuery.data?.nodeCountOptions?.defaultNodeCount ?? DEFAULT_TIKV_NODES))
  }, [tikvSpecsQuery.data, update, value.tikvNodeSpecKey])

  useEffect(() => {
    const spec =
      tiflashSpecsQuery.data?.nodeSpecs?.find((item) => item.isDefault) ?? tiflashSpecsQuery.data?.nodeSpecs?.[0]
    if (
      !spec?.nodeSpecKey ||
      tiflashSpecsQuery.data?.nodeSpecs?.some((item) => item.nodeSpecKey === value.tiflashNodeSpecKey)
    ) {
      return
    }
    update('tiflashNodeSpecKey', spec.nodeSpecKey)
    update('tiflashVcpu', `${spec.cpu} vCPU`)
    update('tiflashMemory', `${spec.memoryGi} GiB`)
    update('tiflashStorage', `${getPrimaryStorage(spec)?.defaultSizeGi ?? 0} GiB`)
    update('tiflashNodes', Number(tiflashSpecsQuery.data?.nodeCountOptions?.defaultNodeCount ?? DEFAULT_TIFLASH_NODES))
  }, [tiflashSpecsQuery.data, update, value.tiflashNodeSpecKey])

  const submit = async () => {
    if (submitDisabled || !tikvSpec) {
      return
    }

    const request = buildCreateRequest()
    if (!request) {
      return
    }

    try {
      setFormError('')
      await createMutation.mutateAsync({ data: request, params: { orgId, projectId, validateOnly: true } })
      const pool = await createMutation.mutateAsync({ data: request, params: { orgId, projectId } })
      await queryClient.invalidateQueries(RESOURCE_POOLS_QUERY_KEY, { refetchInactive: true })
      notifier.success('Resource pool creation submitted.')
      history.push(`/byoc/resource-pools/${pool.resourcePoolId}${location.search}`)
    } catch (error) {
      const message = getErrorMessage(error, true) || 'Failed to create the resource pool.'
      setFormError(message)
      notifier.error(message)
    }
  }

  const header = (
    <PageShellBase.Header
      sticky
      leftSection={
        <Group wrap="nowrap" gap={0}>
          <ExpandNavbarButtonPlaceholder />
          <PageShellBase.BackButton mr="md" onClick={() => history.push(`/byoc/resource-pools${location.search}`)} />
        </Group>
      }
      sx={{ backgroundColor: 'var(--mantine-color-carbon-1)' }}
    >
      <PageShellBase.Title>Create Resource Pool</PageShellBase.Title>
    </PageShellBase.Header>
  )

  const summary = (
    <>
      <Box>
        <Typography variant="title-lg">Summary</Typography>
        <Typography variant="body-lg" c="carbon.7" mt={4}>
          Your new resource pool will be:
        </Typography>
        <Divider mt="md" />
        <Stack gap={8} py="md">
          <PropertyKV label="Name" labelProps={{ w: 144 }}>
            {value.name || '-'}
          </PropertyKV>
          <PropertyKV label="Provider" labelProps={{ w: 144 }}>
            {value.cloud}
          </PropertyKV>
          <PropertyKV label="Region" labelProps={{ w: 144 }}>
            {regions.find((region) => region.regionId === value.region)?.displayName ?? value.region ?? '-'}
          </PropertyKV>
        </Stack>
        <Divider />
        <Stack gap={8} py="md">
          <PropertyKV label="Pool vCPU Limit" labelProps={{ w: 144 }}>
            {value.poolVcpuLimitEnabled ? `${value.poolVcpuLimit} vCPU` : 'Not set'}
          </PropertyKV>
          <PropertyKV label="TiKV" labelProps={{ w: 144 }}>
            {tikvSpec ? (
              <Stack gap={0}>
                <Typography variant="body-lg">{`${value.tikvVcpu}, ${value.tikvMemory}`}</Typography>
                <Typography variant="body-lg">{`${value.tikvStorage} storage × ${value.tikvNodes} Nodes`}</Typography>
              </Stack>
            ) : (
              '-'
            )}
          </PropertyKV>
          {value.tiflashEnabled && (
            <PropertyKV label="TiFlash" labelProps={{ w: 144 }}>
              <Stack gap={0}>
                <Typography variant="body-lg">{`${value.tiflashVcpu}, ${value.tiflashMemory}`}</Typography>
                <Typography variant="body-lg">{`${value.tiflashStorage} storage × ${value.tiflashNodes} Nodes`}</Typography>
              </Stack>
            </PropertyKV>
          )}
        </Stack>
        <Divider />
        <Stack gap={8} py="md">
          <PropertyKV label="Pool CIDR" labelProps={{ w: 144 }}>
            {value.poolCidr}
          </PropertyKV>
          <PropertyKV label="High Availability" labelProps={{ w: 144 }}>
            {value.highAvailability || '-'}
          </PropertyKV>
          <PropertyKV label="Tags" labelProps={{ w: 144 }}>
            {configuredTags.length ? (
              <Stack gap={0}>
                {configuredTags.map((tag, index) => (
                  <Typography key={index} variant="body-lg">
                    {tag.key || '-'}: {tag.value || '-'}
                  </Typography>
                ))}
              </Stack>
            ) : (
              'Not set'
            )}
          </PropertyKV>
        </Stack>
        <Divider />
      </Box>
      <Box sx={{ marginTop: 'auto' }}>
        <Divider mb="md" />
        <Group justify="space-between">
          <Button variant="default" onClick={() => history.push(`/byoc/resource-pools${location.search}`)}>
            Cancel
          </Button>
          {creditCardNeeded ? (
            <AccessDeniedHoverHint id="BillingPaymentCreation">
              {({ accessible }) => (
                <Button
                  disabled={!accessible}
                  onClick={creditCardModalHandlers.open}
                  data-mp-event="Add Credit Card Button Clicked"
                  data-testid="resource-pool-create-submit"
                  sx={{ flex: 1 }}
                >
                  Add Credit Card
                </Button>
              )}
            </AccessDeniedHoverHint>
          ) : (
            <Button
              disabled={submitDisabled}
              onClick={submit}
              loading={createMutation.isLoading}
              data-mp-event={MixpanelEvents.TidbResource.Create.SubmitClicked}
              data-testid="resource-pool-create-submit"
              sx={{ flex: 1 }}
            >
              Create
            </Button>
          )}
        </Group>
      </Box>
      {creditCardNeeded && (
        <PaymentMethodModal
          open={creditCardModalVisible}
          onClose={creditCardModalHandlers.close}
          onComplete={() => {
            creditCardModalHandlers.close()
            notifier.success('Your credit card was added successfully!')
          }}
        />
      )}
    </>
  )

  return (
    <PageAccessGuard id="ByocResourcePoolManagementAction">
      <PageShellBase.Root
        data-testid="resource-pool-create-page"
        sx={{ backgroundColor: 'var(--mantine-color-carbon-1)', height: '100%', overflow: 'hidden' }}
      >
        <TwoPaneLayout visible header={header} rightSection={summary} contentBottomPadding={40}>
          <Paper withBorder shadow="none">
            {formError && (
              <Alert color="red" icon={<IconInfoCircle size={16} />} m="xl" mb={0}>
                {formError}
              </Alert>
            )}
            <FormItem
              label="Basic Settings"
              description="Set the name of your new resource pool, cloud provider and region."
            >
              <Stack gap="lg">
                {basicSettingsError && (
                  <Alert color="red" title="Cloud providers and regions could not be loaded">
                    Refresh the page or try again later.
                  </Alert>
                )}
                <Stack gap={4}>
                  <TextInput
                    label="Resource Pool Name"
                    placeholder="Name your resource pool for easy finding"
                    value={value.name}
                    onChange={(event) => update('name', event.currentTarget.value)}
                  />
                  <Typography variant="body-sm" c="carbon.7">
                    A Resource Pool provides shared infrastructure and capacity for BYOC instances in the selected cloud
                    provider and region.
                  </Typography>
                </Stack>
                <Grid>
                  <Grid.Col span={4}>
                    <Select
                      label="Cloud Provider"
                      value={value.cloud}
                      onChange={(provider) => {
                        setPoolCidrDirty(false)
                        setPoolCidrError('')
                        update('cloud', provider === 'Azure' ? 'Azure' : 'AWS')
                      }}
                      data={cloudProviders.map((provider) => ({
                        value: provider === 'azure' ? 'Azure' : 'AWS',
                        label: provider === 'azure' ? 'Azure' : 'AWS'
                      }))}
                      disabled={cloudProvidersLoading || basicSettingsError || cloudProviders.length <= 1}
                    />
                  </Grid.Col>
                  <Grid.Col span={8}>
                    <Select
                      label="Region"
                      value={value.region}
                      onChange={(region) => {
                        setPoolCidrDirty(false)
                        setPoolCidrError('')
                        update('region', region ?? '')
                      }}
                      data={regions.map((region) => ({
                        label: region.displayName ?? region.regionId ?? '',
                        value: region.regionId ?? ''
                      }))}
                      disabled={regionsLoading || basicSettingsError}
                    />
                  </Grid.Col>
                </Grid>
              </Stack>
            </FormItem>

            <Divider />

            <Stack p="xl">
              <Group justify="space-between" align="flex-start" wrap="nowrap" gap="xl">
                <Box>
                  <Typography variant="title-lg">Pool vCPU Limit</Typography>
                  <Typography variant="body-lg" c="carbon.7">
                    Configure the maximum total provisioned vCPU allowed for this Resource Pool.
                  </Typography>
                </Box>
                <Switch
                  aria-label="Set Pool vCPU Limit"
                  checked={value.poolVcpuLimitEnabled}
                  onChange={(event) => update('poolVcpuLimitEnabled', event.currentTarget.checked)}
                />
              </Group>
              {value.poolVcpuLimitEnabled && (
                <Stack gap="sm">
                  <NumberInput
                    min={100}
                    value={value.poolVcpuLimit}
                    onChange={(next) => update('poolVcpuLimit', typeof next === 'number' ? next : undefined)}
                    suffix=" vCPU"
                  />
                  <Typography variant="body-sm" c="carbon.7">
                    Minimum limit: 100 vCPU. This limit acts as a budget guardrail and applies to the total provisioned
                    vCPU of all database-related components, including TiDB, TiKV, TiFlash, PD, TiProxy, workers, and
                    associated services. Admin and observability nodes are excluded. Resource usage is billed based on
                    actual consumption and remains subject to platform and cloud resource limits.
                  </Typography>
                </Stack>
              )}
            </Stack>

            <Divider />
            <FormItem
              label="Pool Capacity"
              description="Configure the initial TiKV and TiFlash resources for this Pool."
            >
              <Stack gap="lg">
                {nodeSpecsError && (
                  <Alert color="red" title="Node specifications could not be loaded">
                    Refresh the page or try again later.
                  </Alert>
                )}
                {initialCapacityExceedsLimit && (
                  <Alert color="red" icon={<IconInfoCircle size={16} />}>
                    {INITIAL_CAPACITY_LIMIT_MESSAGE}
                  </Alert>
                )}
                <ComponentBlock
                  title="TiKV"
                  description="The storage node that stores the online transactional processing (OLTP) data."
                >
                  <ComponentBlock.Item label="vCPU, RAM">
                    <Select
                      size="sm"
                      value={value.tikvNodeSpecKey}
                      onChange={(nodeSpecKey) => {
                        const spec = tikvSpecsQuery.data?.nodeSpecs?.find((item) => item.nodeSpecKey === nodeSpecKey)
                        if (!spec?.nodeSpecKey) return
                        update('tikvNodeSpecKey', spec.nodeSpecKey)
                        update('tikvVcpu', `${spec.cpu} vCPU`)
                        update('tikvMemory', `${spec.memoryGi} GiB`)
                        update('tikvStorage', `${getPrimaryStorage(spec)?.defaultSizeGi ?? 0} GiB`)
                      }}
                      data={(tikvSpecsQuery.data?.nodeSpecs ?? []).map((spec) => ({
                        label: spec.displayName ?? `${spec.cpu} vCPU, ${spec.memoryGi} GiB`,
                        value: spec.nodeSpecKey ?? ''
                      }))}
                      disabled={tikvSpecsQuery.isLoading}
                    />
                  </ComponentBlock.Item>
                  <ComponentBlock.Item label="Storage × Nodes">
                    <Group gap={8} align="flex-start" wrap="nowrap">
                      <NumberInput
                        size="sm"
                        value={Number.parseInt(value.tikvStorage, 10) || 0}
                        onChange={(next) => update('tikvStorage', `${Number(next) || 0} GiB`)}
                        min={Number(getPrimaryStorage(tikvSpec)?.minSizeGi ?? 1)}
                        max={toOptionalNumber(getPrimaryStorage(tikvSpec)?.maxSizeGi)}
                        suffix=" GiB"
                        sx={{ flexGrow: 1 }}
                      />
                      <Center h={32}>
                        <IconXClose size={16} />
                      </Center>
                      <NumberInput
                        size="sm"
                        min={Number(tikvSpecsQuery.data?.nodeCountOptions?.minNodeCount ?? 3)}
                        max={toOptionalNumber(tikvSpecsQuery.data?.nodeCountOptions?.maxNodeCount)}
                        step={Number(tikvSpecsQuery.data?.nodeCountOptions?.rule?.value ?? 1)}
                        value={value.tikvNodes}
                        onChange={(next) => update('tikvNodes', Number(next) || 3)}
                        suffix={getNodesSuffix(value.tikvNodes)}
                        sx={{ flexGrow: 1 }}
                      />
                    </Group>
                  </ComponentBlock.Item>
                </ComponentBlock>
                <ComponentBlock
                  title="TiFlash"
                  description="The analytical storage node that replicates data from TiKV in real-time and supports real-time analytical workloads."
                  rightSection={
                    <Switch
                      aria-label="Enable TiFlash"
                      ml={2}
                      checked={value.tiflashEnabled}
                      onChange={(event) => update('tiflashEnabled', event.currentTarget.checked)}
                    />
                  }
                >
                  {value.tiflashEnabled && (
                    <>
                      <ComponentBlock.Item label="vCPU, RAM">
                        <Select
                          size="sm"
                          value={value.tiflashNodeSpecKey}
                          onChange={(nodeSpecKey) => {
                            const spec = tiflashSpecsQuery.data?.nodeSpecs?.find(
                              (item) => item.nodeSpecKey === nodeSpecKey
                            )
                            if (!spec?.nodeSpecKey) return
                            update('tiflashNodeSpecKey', spec.nodeSpecKey)
                            update('tiflashVcpu', `${spec.cpu} vCPU`)
                            update('tiflashMemory', `${spec.memoryGi} GiB`)
                            update('tiflashStorage', `${getPrimaryStorage(spec)?.defaultSizeGi ?? 0} GiB`)
                          }}
                          data={(tiflashSpecsQuery.data?.nodeSpecs ?? []).map((spec) => ({
                            label: spec.displayName ?? `${spec.cpu} vCPU, ${spec.memoryGi} GiB`,
                            value: spec.nodeSpecKey ?? ''
                          }))}
                          disabled={tiflashSpecsQuery.isLoading}
                        />
                      </ComponentBlock.Item>
                      <ComponentBlock.Item label="Storage × Nodes">
                        <Group gap={8} align="flex-start" wrap="nowrap">
                          <NumberInput
                            size="sm"
                            value={Number.parseInt(value.tiflashStorage, 10) || 0}
                            onChange={(next) => update('tiflashStorage', `${Number(next) || 0} GiB`)}
                            min={Number(getPrimaryStorage(tiflashSpec)?.minSizeGi ?? 1)}
                            max={toOptionalNumber(getPrimaryStorage(tiflashSpec)?.maxSizeGi)}
                            suffix=" GiB"
                            sx={{ flexGrow: 1 }}
                          />
                          <Center h={32}>
                            <IconXClose size={16} />
                          </Center>
                          <NumberInput
                            size="sm"
                            value={value.tiflashNodes}
                            onChange={(next) => update('tiflashNodes', Number(next) || DEFAULT_TIFLASH_NODES)}
                            suffix={getNodesSuffix(value.tiflashNodes)}
                            sx={{ flexGrow: 1 }}
                          />
                        </Group>
                      </ComponentBlock.Item>
                    </>
                  )}
                </ComponentBlock>
              </Stack>
            </FormItem>

            <Divider />
            <Group justify="space-between" align="flex-start" p="xl" wrap="nowrap">
              <Stack gap={4} sx={{ flex: 1 }}>
                <Typography variant="title-lg">High Availability</Typography>
                <Typography variant="body-lg" c="carbon.7">
                  Choose <b>Zonal</b> to deploy in a single availability zone, or Regional to deploy across multiple
                  availability zones for higher availability.
                </Typography>
              </Stack>
              <Select
                value={value.highAvailability}
                onChange={(next) => {
                  if (next === 'Regional' || next === 'Zonal') {
                    update('highAvailability', next)
                  }
                }}
                onBlur={() => setHighAvailabilityTouched(true)}
                placeholder="Select high availability"
                data={['Regional', 'Zonal']}
                allowDeselect={false}
                required
                error={
                  highAvailabilityTouched && !value.highAvailability ? 'High availability is required.' : undefined
                }
                data-testid="resource-pool-create-high-availability"
                w={240}
              />
            </Group>

            <Divider />
            <FormItem
              label="Pool CIDR"
              description={
                <>
                  A CIDR to be used as the network address for peering with your application VPC. The VPC CIDR{' '}
                  <b>CANNOT</b> be modified after the resource pool is created.{' '}
                  <Typography component="span" variant="body-lg" c="peacock.7">
                    Learn more
                  </Typography>
                </>
              }
            >
              <Stack gap="md">
                <Box>
                  <TextInput
                    value={value.poolCidr}
                    onChange={(event) => {
                      setPoolCidrDirty(true)
                      update('poolCidr', event.currentTarget.value)
                    }}
                    placeholder="10.0.0.0/22"
                    error={cidrError || undefined}
                    rightSection={cidrLoading ? <Loader size="xs" /> : undefined}
                  />
                  {cidrPlanQuery.isError && (
                    <Alert color="red" mt="sm">
                      {CIDR_PLAN_ERROR_MESSAGE}
                    </Alert>
                  )}
                  {cidrPlanQuery.data?.recommendationStatus === 'EXHAUSTED' && (
                    <Alert color="yellow" mt="sm">
                      No available CIDR recommendation was found. Please enter another CIDR or change region.
                    </Alert>
                  )}
                  {highAvailabilityType && !cidrLoading && !cidrError && supportedNodeCount && (
                    <Typography variant="body-sm" c="carbon.7" mt={8}>
                      This CIDR setting can support up to <b>{supportedNodeCount}</b> nodes in the pool.
                    </Typography>
                  )}
                </Box>
                <Typography variant="body-lg">The following network addresses are supported:</Typography>
                <Group gap="xs">
                  {supportedPrivateCidrs.map((range) => (
                    <Tag key={range} label={toAddressRangeLabel(range)} px="sm" />
                  ))}
                </Group>
                {prefixRangeLabel && (
                  <Typography variant="body-lg">
                    We recommend configuring an IP range size <b>between {prefixRangeLabel}</b>.
                  </Typography>
                )}
                {exclusionCidrs.length > 0 && (
                  <Stack gap="xs">
                    <Typography variant="body-lg">
                      The CIDR you setup must not overlap with the shared O11Y Cluster CIDR or any existing Resource
                      Pool CIDR in this region. The <b>excluded CIDR</b> is :
                    </Typography>
                    <Group mt="xs">
                      {exclusionCidrs.map((cidr) => (
                        <Tag key={cidr} label={cidr} px="sm" />
                      ))}
                    </Group>
                  </Stack>
                )}
              </Stack>
            </FormItem>

            <Divider />
            <FormItem
              label={
                <>
                  AWS Resource Tags{' '}
                  <Typography component="span" fw={500} c="carbon.6">
                    (Optional)
                  </Typography>
                </>
              }
              description="Tags are used to label the AWS resources that support this resource pool. Tag keys must be unique."
            >
              <Card>
                <Card.Section bg="carbon.2">
                  <Group gap="sm" align="center" h={42} mx="md">
                    <Typography sx={{ flex: 1, border: '4px solid transparent' }} variant="body-md" c="carbon.7">
                      Key
                    </Typography>
                    <Group w={10} h={32} />
                    <Group sx={{ flex: 1 }} gap="4">
                      <Typography variant="body-md" c="carbon.7">
                        Value
                      </Typography>
                      <Typography variant="body-md" c="carbon.6" inline>
                        (Optional)
                      </Typography>
                    </Group>
                    <Box w={40}></Box>
                  </Group>
                </Card.Section>
                <Card.Section>
                  {value.tags.length > 0 && (
                    <Stack gap="xs" mt="xs">
                      {value.tags.map((tag, index) => (
                        <Fragment key={index}>
                          <Group gap="sm" align="flex-start" wrap="nowrap" px="md">
                            <TextInput
                              placeholder="Key"
                              sx={{ flex: 1 }}
                              value={tag.key}
                              size="sm"
                              error={
                                !tag.key.trim() && tag.value.trim()
                                  ? 'Tag key is required.'
                                  : duplicateTagKeys.has(tag.key.trim())
                                    ? 'Tag key must be unique.'
                                    : awsReservedTagKeys.has(tag.key.trim())
                                      ? 'Tag key cannot be aws or start with aws.'
                                      : invalidTagKeyLengths.has(tag.key.trim())
                                        ? 'Tag key cannot exceed 128 characters.'
                                        : undefined
                              }
                              onChange={(event) =>
                                update(
                                  'tags',
                                  value.tags.map((item, itemIndex) =>
                                    itemIndex === index ? { ...item, key: event.currentTarget.value } : item
                                  )
                                )
                              }
                            />
                            <Group w={10} justify="center" align="center" h={32}>
                              <Typography variant="title-lg" c="carbon.5">
                                :
                              </Typography>
                            </Group>
                            <TextInput
                              placeholder="Value"
                              sx={{ flex: 1 }}
                              value={tag.value}
                              size="sm"
                              error={
                                invalidTagValueIndexes.has(index)
                                  ? 'Tag value cannot exceed 256 characters.'
                                  : undefined
                              }
                              onChange={(event) =>
                                update(
                                  'tags',
                                  value.tags.map((item, itemIndex) =>
                                    itemIndex === index ? { ...item, value: event.currentTarget.value } : item
                                  )
                                )
                              }
                            />
                            <ActionIcon
                              variant="outline"
                              size={32}
                              bg="carbon.2"
                              aria-label={`Remove tag ${index + 1}`}
                              onClick={() =>
                                update(
                                  'tags',
                                  value.tags.filter((_, itemIndex) => itemIndex !== index)
                                )
                              }
                            >
                              <IconXClose size={18} />
                            </ActionIcon>
                          </Group>
                          <Divider />
                        </Fragment>
                      ))}
                    </Stack>
                  )}
                  <Group justify="space-between" px="md" py="sm">
                    <ActionIcon
                      variant="outline"
                      size={32}
                      aria-label="Add tag"
                      onClick={addTag}
                      bg="carbon.2"
                      disabled={value.tags.length >= 10}
                    >
                      <IconPlus size={18} />
                    </ActionIcon>
                    <Typography variant="body-sm" c="carbon.7">
                      Tags Limit: 10
                    </Typography>
                  </Group>
                </Card.Section>
              </Card>
            </FormItem>
          </Paper>
        </TwoPaneLayout>
      </PageShellBase.Root>
    </PageAccessGuard>
  )
}

export default ResourcePoolCreatePage
