import {
  Alert,
  Anchor,
  Box,
  Button,
  Center,
  Code,
  Grid,
  Group,
  Modal,
  NumberInput,
  Select,
  Stack,
  Switch,
  TextInput,
  Tooltip,
  Typography,
  notifier,
  openConfirmModal
} from '@tidbcloud/uikit'
import { IconAlertTriangle, IconInfoCircle, IconXClose } from '@tidbcloud/uikit/icons'
import { useEffect, useMemo, useState } from 'react'

import { ModalCodeTitle } from 'dbaas/components'
import { Tag } from 'dbaas/components/tag'
import { ComponentBlock } from 'dbaas/features/ClusterManagement/dedicated'
import { useRequestSupport } from 'dbaas/screens/HelpCenter/RequestSupport/helper'
import {
  useResourcePoolServiceDeleteResourcePool,
  useResourcePoolServiceGetResourcePoolCapacityConstraints,
  useResourcePoolServiceUpsertResourcePoolCloudResourceTags
} from 'dbaas/services/api-clients/v2025/tidb-management/v1beta2'
import type {
  Nextgenv1beta2ComponentCapacity,
  Nextgenv1beta2ResourcePoolCapacity,
  Nextgenv1beta2ResourcePoolCapacityConstraints,
  Nextgenv1beta2ResourcePoolServiceUpsertResourcePoolCloudResourceTagsBody,
  V1beta2VcpuLimit
} from 'dbaas/services/api-clients/v2025/tidb-management/v1beta2/interface'
import { getErrorMessage } from 'dbaas/services/errorCodes'
import useStores from 'dbaas/stores/useStores'

import { resizeResourcePoolCapacity, updateResourcePool } from './api'
import { ResourcePool } from './types'

export type ResourcePoolDialogAction = 'rename' | 'delete' | 'limit' | 'tags' | 'capacity'

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)
}

type ResourcePoolCloudResourceTagsBody = Nextgenv1beta2ResourcePoolServiceUpsertResourcePoolCloudResourceTagsBody & {
  removeCloudResourceTagKeys?: string[]
}

interface ResourcePoolDialogsProps {
  action?: ResourcePoolDialogAction
  opened: boolean
  pool: ResourcePool
  onClose: () => void
  onActionChange?: (action?: ResourcePoolDialogAction) => void
  onUpdated?: () => void
  onDeleted?: () => void
}

const NoteAlert = ({ children }: { children: React.ReactNode }) => (
  <Alert
    color="peacock"
    icon={<IconInfoCircle size={16} />}
    px={12}
    py={10}
    styles={(theme) => ({
      root: {
        backgroundColor: theme.colors.peacock[1],
        borderLeft: `2px solid ${theme.colors.peacock[7]}`
      },
      icon: {
        color: theme.colors.peacock[7]
      },
      message: {
        color: theme.colors.peacock[9],
        fontSize: 14,
        fontWeight: 400,
        lineHeight: '20px'
      }
    })}
  >
    {children}
  </Alert>
)

const DialogActions = ({
  primaryLabel,
  danger,
  disabled,
  error,
  onClose,
  onPrimary,
  loading
}: {
  primaryLabel: string
  danger?: boolean
  disabled?: boolean
  error?: string
  onClose: () => void
  onPrimary?: () => void | Promise<void>
  loading?: boolean
}) => (
  <>
    {error && (
      <Alert color="red" icon={<IconInfoCircle size={16} />} mt="xl">
        {error}
      </Alert>
    )}
    <Group
      justify="flex-end"
      mt="xl"
      mx={-24}
      mb={-24}
      px="xl"
      py="md"
      sx={(theme) => ({ borderTop: `1px solid ${theme.colors.carbon[4]}` })}
    >
      <Button variant="default" onClick={onClose} data-mp-event="BYOC Resource Pool Dialog Cancel Clicked">
        Cancel
      </Button>
      <Button
        color={danger ? 'red' : undefined}
        variant={danger ? 'light' : undefined}
        disabled={disabled}
        loading={loading}
        onClick={onPrimary ?? onClose}
        data-mp-event={`BYOC Resource Pool ${primaryLabel} Clicked`}
      >
        {primaryLabel}
      </Button>
    </Group>
  </>
)

const RenameDialog = ({
  pool,
  onClose,
  onSubmit,
  loading,
  error
}: {
  pool: ResourcePool
  onClose: () => void
  onSubmit: (name: string) => Promise<void>
  loading: boolean
  error?: string
}) => {
  const [name, setName] = useState(pool.name)
  useEffect(() => setName(pool.name), [pool.name])

  return (
    <>
      <TextInput
        label="New Resource Pool Name"
        value={name}
        onChange={(event) => setName(event.currentTarget.value)}
        placeholder="Enter the new name"
      />
      <DialogActions
        primaryLabel="Save"
        disabled={!name.trim() || name === pool.name}
        onClose={onClose}
        onPrimary={() => onSubmit(name.trim())}
        loading={loading}
        error={error}
      />
    </>
  )
}

const DeleteDialog = ({
  pool,
  onClose,
  onSubmit,
  loading,
  error
}: {
  pool: ResourcePool
  onClose: () => void
  onSubmit: () => Promise<void>
  loading: boolean
  error?: string
}) => {
  const [confirmation, setConfirmation] = useState('')
  const {
    store: {
      tenantData: { name: orgName }
    }
  } = useStores()
  const confirmationLabel = `${orgName}/${pool.name}`.trim()
  useEffect(() => setConfirmation(''), [pool.id])

  return (
    <>
      <Typography variant="body-lg">
        Deleting this resource pool is permanent and cannot be undone. Are you sure you want to delete it?
      </Typography>
      <Typography variant="body-lg" mt="md">
        To confirm, type{' '}
        <Code fz={13} fw={600} bg="carbon.3" c="carbon.9">
          {confirmationLabel}
        </Code>{' '}
        in the box below.
      </Typography>
      {pool.instances > 0 && (
        <Alert color="yellow" icon={<IconInfoCircle size={16} />} mt="md">
          This resource pool still contains {pool.instances} instances. Delete all instances before deleting the
          resource pool.
        </Alert>
      )}
      <TextInput mt="md" value={confirmation} onChange={(event) => setConfirmation(event.currentTarget.value)} />
      <DialogActions
        primaryLabel="I understand, Delete it"
        danger
        disabled={confirmation !== confirmationLabel || pool.instances > 0}
        onClose={onClose}
        onPrimary={onSubmit}
        loading={loading}
        error={error}
      />
    </>
  )
}

const LimitDialog = ({
  pool,
  onClose,
  onSubmit,
  loading,
  error
}: {
  pool: ResourcePool
  onClose: () => void
  onSubmit: (limit: V1beta2VcpuLimit) => Promise<void>
  loading: boolean
  error?: string
}) => {
  const hasExistingLimit = pool.poolVcpuLimit !== undefined
  const [enabled, setEnabled] = useState(pool.poolVcpuLimit !== undefined)
  const [limit, setLimit] = useState<number | string>(pool.poolVcpuLimit ?? '')

  useEffect(() => {
    setEnabled(pool.poolVcpuLimit !== undefined)
    setLimit(pool.poolVcpuLimit ?? '')
  }, [pool.id, pool.poolVcpuLimit])

  const currentLimitLabel = pool.poolVcpuLimit === undefined ? 'Not set' : String(pool.poolVcpuLimit)
  const minimumLimit = Math.max(100, pool.currentVcpuProvision)
  const isLimitEnabled = hasExistingLimit ? enabled : true
  const limitNumber = Number(limit)
  const hasValidLimit = limit !== '' && Number.isFinite(limitNumber) && limitNumber >= minimumLimit
  const isLimitUnchanged = hasExistingLimit && isLimitEnabled && limitNumber === pool.poolVcpuLimit
  const limitError =
    isLimitEnabled && limit && Number(limit) < minimumLimit
      ? `The vCPU limit must be greater than or equal to ${minimumLimit} vCPU.`
      : undefined

  return (
    <>
      {hasExistingLimit ? (
        <>
          <Typography variant="body-lg" c="carbon.7">
            The new Pool vCPU Limit must be greater than or equal to the current provisioned vCPU. Otherwise, the update
            will be rejected. If you turn off the limit, no customer-defined vCPU limit will apply, and billing will
            remain based on actual usage.
          </Typography>
          <Grid mt="lg" gutter="lg">
            <Grid.Col span={6}>
              <Stack gap="sm">
                <Typography variant="label-md">Setup Limit</Typography>
                <Switch
                  aria-label="Setup Limit"
                  checked={enabled}
                  onChange={(event) => setEnabled(event.currentTarget.checked)}
                />
              </Stack>
            </Grid.Col>
            {enabled && (
              <>
                <Grid.Col span={6}>
                  <Stack gap="sm">
                    <Typography variant="label-md">Current vCPU Provision</Typography>
                    <Typography variant="title-md">{pool.currentVcpuProvision}</Typography>
                  </Stack>
                </Grid.Col>
                <Grid.Col span={6}>
                  <Stack gap="sm">
                    <Typography variant="label-md">Current Pool vCPU Limit</Typography>
                    <TextInput value={currentLimitLabel} disabled />
                  </Stack>
                </Grid.Col>
                <Grid.Col span={6}>
                  <Stack gap="sm">
                    <Typography variant="label-md">New Pool vCPU Limit</Typography>
                    <NumberInput value={limit} onChange={setLimit} error={limitError} hideControls />
                  </Stack>
                </Grid.Col>
              </>
            )}
          </Grid>
        </>
      ) : (
        <Stack>
          <Typography variant="body-lg" c="carbon.7">
            Setup your Pool vCPU Limit -- the maximum total provisioned vCPU allowed for this Resource Pool, based on
            your workload.
          </Typography>
          <Stack>
            <Stack gap={8}>
              <Typography variant="label-lg">Current vCPU Provision</Typography>
              <Typography variant="headline-sm" c="carbon.8">
                {pool.currentVcpuProvision}
              </Typography>
            </Stack>
            <Stack gap={8}>
              <NumberInput label=" Pool vCPU Limit" value={limit} onChange={setLimit} error={limitError} />
              <Typography variant="body-sm" c="carbon.7">
                The vCPU limit must be greater than or equal to the current provisioned vCPU.{' '}
              </Typography>
            </Stack>
          </Stack>
        </Stack>
      )}
      <DialogActions
        primaryLabel={hasExistingLimit ? 'Update' : 'Setup'}
        disabled={isLimitEnabled ? !hasValidLimit || isLimitUnchanged : false}
        onClose={onClose}
        onPrimary={() => onSubmit(isLimitEnabled ? { limitedVcpus: String(limit) } : { unlimited: true })}
        loading={loading}
        error={error}
      />
    </>
  )
}

const TagsDialog = ({
  pool,
  onClose,
  onSubmit,
  loading,
  error
}: {
  pool: ResourcePool
  onClose: () => void
  onSubmit: (tags: Record<string, string>) => Promise<void>
  loading: boolean
  error?: string
}) => {
  const [tags, setTags] = useState(pool.tags)
  const [tagKey, setTagKey] = useState<string>('')
  const [tagValue, setTagValue] = useState<string>('')
  const isEditTags = pool.tags.length > 0

  useEffect(() => {
    setTags(pool.tags)
  }, [pool.id, pool.tags])

  const addTag = (key: string, value: string) =>
    setTags((current) => (current.length >= 10 ? current : [...current, { key, value }]))
  const configuredTags = useMemo(() => tags.filter((tag) => tag.key?.trim() || tag.value?.trim()), [tags])
  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 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(
        tags
          .map((tag, index) => ({ index, value: tag.value?.trim() ?? '' }))
          .filter((tag) => tag.value.length > AWS_TAG_VALUE_MAX_LENGTH)
          .map((tag) => tag.index)
      ),
    [tags]
  )
  const pendingTagInvalid =
    isAwsReservedTagKey(tagKey) ||
    tagKey.trim().length > AWS_TAG_KEY_MAX_LENGTH ||
    tagValue.trim().length > AWS_TAG_VALUE_MAX_LENGTH
  const addTagDisabled = pendingTagInvalid || (!tagKey.trim() && !tagValue.trim()) || tags.length >= 10
  const hasInvalidTags =
    configuredTags.some((tag) => !tag.key?.trim()) ||
    duplicateTagKeys.size > 0 ||
    invalidTagKeyLengths.size > 0 ||
    awsReservedTagKeys.size > 0 ||
    invalidTagValueIndexes.size > 0 ||
    pendingTagInvalid

  return (
    <>
      <NoteAlert>
        <strong>Adding a tag:</strong> The new tag will be automatically applied to all AWS resources, including both
        current existing and future newly AWS resources.
        {isEditTags && (
          <>
            <br />
            <strong>Removing a tag:</strong> It will no longer be applied to newly AWS resources. However, this will not
            automatically clean up or delete the tag from previously marked AWS resources.
          </>
        )}
      </NoteAlert>
      <Typography variant="body-sm" c="carbon.7" mt="md">
        Tags are used to label the AWS resources that support this resource pool. Tag keys must be unique.
      </Typography>
      <Stack gap={0} mt="md">
        <Group gap="sm" align="flex-start" wrap="nowrap">
          <TextInput
            placeholder="Key"
            sx={{ flex: 1 }}
            value={tagKey}
            label="Tag Key"
            error={
              isAwsReservedTagKey(tagKey)
                ? 'Tag key cannot be aws or start with aws.'
                : tagKey.trim().length > AWS_TAG_KEY_MAX_LENGTH
                  ? 'Tag key cannot exceed 128 characters.'
                  : undefined
            }
            onChange={(event) => setTagKey(event.currentTarget?.value)}
          />
          <Group w={10} justify="center" align="center" h={32} mt={28}>
            <Typography variant="title-lg" c="carbon.5">
              :
            </Typography>
          </Group>
          <TextInput
            placeholder="Value"
            sx={{ flex: 1 }}
            value={tagValue}
            label={
              <Group sx={{ flex: 1 }} gap="4">
                <Typography variant="body-md" fw={500} c="carbon.7">
                  Value
                </Typography>
                <Typography variant="body-md" c="carbon.6" inline>
                  (Optional)
                </Typography>
              </Group>
            }
            error={
              tagValue.trim().length > AWS_TAG_VALUE_MAX_LENGTH ? 'Tag value cannot exceed 256 characters.' : undefined
            }
            onChange={(event) => setTagValue(event.currentTarget?.value)}
          />
          <Button
            variant="light"
            disabled={addTagDisabled}
            mt={26}
            onClick={() => {
              addTag(tagKey, tagValue)
              setTagKey('')
              setTagValue('')
            }}
          >
            Add to list
          </Button>
        </Group>
        <Typography variant="body-md" c="carbon.7" mt="xl" mb="sm">
          Tag List
        </Typography>
        {tags.length === 0 ? (
          <Typography variant="body-md" c="carbon.6">
            no any tag yet
          </Typography>
        ) : (
          <Group>
            {tags.map((tag, index) => (
              <Tag
                onRemove={() => setTags((current) => current.filter((_, i) => i !== index))}
                key={index}
                label={`${tag.key} : ${tag.value}`}
                withRemove
                color={
                  duplicateTagKeys.has(tag.key?.trim()) ||
                  invalidTagKeyLengths.has(tag.key?.trim()) ||
                  awsReservedTagKeys.has(tag.key?.trim()) ||
                  invalidTagValueIndexes.has(index)
                    ? 'red'
                    : undefined
                }
              />
            ))}
          </Group>
        )}
      </Stack>
      <DialogActions
        primaryLabel="Apply"
        disabled={hasInvalidTags}
        onClose={onClose}
        onPrimary={() => {
          onSubmit(
            Object.fromEntries(
              tags
                .filter((tag) => tag.key.trim() || tag.value?.trim())
                .map((tag) => [tag.key?.trim(), tag.value?.trim() ?? ''])
            )
          )
        }}
        loading={loading}
        error={error}
      />
    </>
  )
}

const getPrimaryStorageIndex = (capacity?: Nextgenv1beta2ComponentCapacity) => {
  const dataIndex = capacity?.storages?.findIndex((storage) => storage.name === 'data') ?? -1
  return dataIndex >= 0 ? dataIndex : 0
}

const getPrimaryStorageSize = (capacity?: Nextgenv1beta2ComponentCapacity) =>
  Number(capacity?.storages?.[getPrimaryStorageIndex(capacity)]?.sizeGi ?? 0)

const toNumberInputValue = (value: string | number, fallback: number) => {
  const numberValue = Number(value)
  return Number.isFinite(numberValue) ? numberValue : fallback
}

const toFiniteNumber = (value?: string | number | null) => {
  const numberValue = Number(value)
  return Number.isFinite(numberValue) ? numberValue : undefined
}

const getPositiveStep = (value?: string | null) => {
  const step = Number(value)
  return Number.isFinite(step) && step > 0 ? step : 1
}

const getTiKVNodeStep = (value?: string | null) => Math.max(3, getPositiveStep(value))

const normalizeNodeCountByStep = (value: string | number, fallback: number, step: number) => {
  const nodeCount = toNumberInputValue(value, fallback)
  return nodeCount % step === 0 ? nodeCount : fallback
}

const getPositiveNodeCount = (value?: string | null) => {
  const nodeCount = Number(value)
  return Number.isFinite(nodeCount) && nodeCount > 0 ? nodeCount : 1
}

const withCapacityTarget = (
  capacity: Nextgenv1beta2ComponentCapacity,
  nodeCount: number,
  primaryStorageSize: number,
  nodeSpec = capacity.nodeSpec
): Nextgenv1beta2ComponentCapacity => ({
  nodeSpec,
  nodeCount: String(nodeCount),
  storages: capacity.storages?.map((storage, index) =>
    index === getPrimaryStorageIndex(capacity) ? { ...storage, sizeGi: String(primaryStorageSize) } : storage
  )
})

const buildDisableComponentTarget = (): Nextgenv1beta2ComponentCapacity => ({
  nodeCount: '0'
})

const getComponentVcpu = (capacity?: Nextgenv1beta2ComponentCapacity, nodeCount?: number) => {
  const cpu = toFiniteNumber(capacity?.nodeSpec?.cpu)
  const nodes = nodeCount ?? toFiniteNumber(capacity?.nodeCount)
  return cpu !== undefined && nodes !== undefined ? cpu * nodes : 0
}

const toNodeSpecValue = (nodeSpec?: { cpu?: string | null; memoryGi?: string | null }) =>
  nodeSpec?.cpu && nodeSpec.memoryGi ? `${nodeSpec.cpu}|${nodeSpec.memoryGi}` : ''

const toNodeSpecLabel = (nodeSpec?: { cpu?: string | null; memoryGi?: string | null; displayName?: string | null }) =>
  nodeSpec?.displayName ?? (nodeSpec?.cpu && nodeSpec.memoryGi ? `${nodeSpec.cpu} vCPU, ${nodeSpec.memoryGi} GiB` : '')

const getNodesSuffix = (value: string | number) => (Number(value) === 1 ? ' node' : ' nodes')

const EvaluationItem = ({
  label,
  value,
  tooltipLabel,
  warning
}: {
  label: string
  value: string | number
  tooltipLabel?: React.ReactNode
  warning?: boolean
}) => (
  <Stack gap={4}>
    <Group gap={6} align="center">
      <Typography variant="body-md" c="carbon.8">
        {label}
      </Typography>
      {tooltipLabel && (
        <Tooltip label={tooltipLabel} withArrow multiline maw={360}>
          <Box>
            <IconInfoCircle size={14} color="var(--mantine-color-carbon-6)" />
          </Box>
        </Tooltip>
      )}
    </Group>
    <Typography variant="body-lg" c={warning ? 'red.7' : 'carbon.9'} fz={22}>
      {value}
    </Typography>
  </Stack>
)

const toCandidateCapacity = (
  constraints?: Nextgenv1beta2ResourcePoolCapacityConstraints
): Nextgenv1beta2ComponentCapacity | undefined => {
  const candidate = constraints?.tiflash?.nodeSpec?.candidates?.find((item) => item.available)?.nodeSpec
  if (!candidate?.cpu || !candidate.memoryGi) {
    return undefined
  }

  return {
    nodeSpec: { cpu: candidate.cpu, memoryGi: candidate.memoryGi },
    nodeCount: String(getPositiveNodeCount(constraints?.tiflash?.nodeCount?.min)),
    storages: candidate.storages
      ?.filter((storage) => storage.name && storage.defaultSizeGi)
      .map((storage) => ({ name: storage.name!, sizeGi: storage.defaultSizeGi! }))
  }
}

const CapacityDialog = ({
  pool,
  constraints,
  onClose,
  onSubmit,
  onResetLimit,
  loading,
  error,
  submitError
}: {
  pool: ResourcePool
  constraints?: Nextgenv1beta2ResourcePoolCapacityConstraints
  onClose: () => void
  onSubmit: (capacity: Nextgenv1beta2ResourcePoolCapacity) => Promise<void>
  onResetLimit: () => void
  loading: boolean
  error: boolean
  submitError?: string
}) => {
  const tikv = pool.apiCapacity?.tikv
  const currentTiflash = pool.apiCapacity?.tiflash
  const candidateTiflash = useMemo(() => toCandidateCapacity(constraints), [constraints])
  const tiflash = currentTiflash ?? candidateTiflash
  const tikvNodeStep = getTiKVNodeStep(constraints?.tikv?.nodeCount?.rule?.value)
  const tiflashNodeStep = getPositiveStep(constraints?.tiflash?.nodeCount?.rule?.value)
  const tiflashMinNodes = Number(constraints?.tiflash?.nodeCount?.min ?? pool.capacity.tiflash?.nodes ?? 0)
  const tiflashSpecOptions = useMemo(() => {
    const options = [
      ...(currentTiflash
        ? [
            {
              value: toNodeSpecValue(currentTiflash.nodeSpec),
              label: toNodeSpecLabel(currentTiflash.nodeSpec),
              capacity: currentTiflash
            }
          ]
        : []),
      ...(constraints?.tiflash?.nodeSpec?.candidates
        ?.filter((candidate) => candidate.available && candidate.nodeSpec?.cpu && candidate.nodeSpec.memoryGi)
        .map((candidate) => ({
          value: toNodeSpecValue(candidate.nodeSpec),
          label: toNodeSpecLabel(candidate.nodeSpec),
          capacity: {
            nodeSpec: {
              cpu: candidate.nodeSpec!.cpu!,
              memoryGi: candidate.nodeSpec!.memoryGi!
            },
            nodeCount: String(getPositiveNodeCount(constraints?.tiflash?.nodeCount?.min)),
            storages: candidate
              .nodeSpec!.storages?.filter((storage) => storage.name && storage.defaultSizeGi)
              .map((storage) => ({ name: storage.name!, sizeGi: storage.defaultSizeGi! }))
          }
        })) ?? [])
    ]

    return options.filter(
      (option, index, array) => option.value && array.findIndex((item) => item.value === option.value) === index
    )
  }, [constraints?.tiflash?.nodeCount?.min, constraints?.tiflash?.nodeSpec?.candidates, currentTiflash])
  const [tiflashEnabled, setTiflashEnabled] = useState(!!currentTiflash)
  const [tikvNodes, setTikvNodes] = useState<string | number>(Number(tikv?.nodeCount ?? 0))
  const [tikvStorage, setTikvStorage] = useState(getPrimaryStorageSize(tikv))
  const [tiflashSpecValue, setTiflashSpecValue] = useState(toNodeSpecValue(tiflash?.nodeSpec))
  const [tiflashNodes, setTiflashNodes] = useState<string | number>(Number(tiflash?.nodeCount ?? 1))
  const [tiflashStorage, setTiflashStorage] = useState(getPrimaryStorageSize(tiflash))
  const selectedTiflash = tiflashSpecOptions.find((option) => option.value === tiflashSpecValue)?.capacity ?? tiflash
  const fallbackTikvNodes = Number(tikv?.nodeCount ?? 0)
  const nextTikvNodes = normalizeNodeCountByStep(tikvNodes, fallbackTikvNodes, tikvNodeStep)
  const nextTiflashNodes = getPositiveNodeCount(String(tiflashNodes))
  const currentSharedVcpu = getComponentVcpu(tikv) + getComponentVcpu(currentTiflash)
  const targetSharedVcpu =
    getComponentVcpu(tikv, nextTikvNodes) + (tiflashEnabled ? getComponentVcpu(selectedTiflash, nextTiflashNodes) : 0)
  const projectedVcpuProvision = pool.currentVcpuProvision + targetSharedVcpu - currentSharedVcpu
  const hasVcpuLimit = pool.poolVcpuLimit !== undefined
  const isOverVcpuLimit = hasVcpuLimit && targetSharedVcpu > 0 && projectedVcpuProvision > pool.poolVcpuLimit!

  useEffect(() => {
    setTiflashEnabled(!!currentTiflash)
    setTikvNodes(Number(tikv?.nodeCount ?? 0))
    setTikvStorage(getPrimaryStorageSize(tikv))
    setTiflashSpecValue(toNodeSpecValue(tiflash?.nodeSpec))
    setTiflashNodes(getPositiveNodeCount(tiflash?.nodeCount))
    setTiflashStorage(getPrimaryStorageSize(tiflash))
  }, [currentTiflash, pool.id, tiflash, tikv])

  const submit = () => {
    if (!tikv || isOverVcpuLimit) {
      return Promise.resolve()
    }

    return onSubmit({
      tikv: withCapacityTarget(tikv, nextTikvNodes, tikvStorage),
      ...(tiflashEnabled && selectedTiflash
        ? {
            tiflash: withCapacityTarget(selectedTiflash, nextTiflashNodes, tiflashStorage, selectedTiflash.nodeSpec)
          }
        : currentTiflash
          ? {
              tiflash: buildDisableComponentTarget()
            }
          : {})
    })
  }

  const { open: openSupport } = useRequestSupport()

  return (
    <>
      <NoteAlert>
        Only scale-out operations are supported here. If you have additional nodes and size requirements, or Scale-in
        requirement, please contacting the{' '}
        <Anchor component="button" type="button" onClick={() => openSupport()}>
          TiDB Cloud Support
        </Anchor>
        .
      </NoteAlert>
      <Stack mt="md" gap="md">
        <ComponentBlock
          title="TiKV"
          description="The storage node that stores the online transactional processing (OLTP) data."
        >
          <ComponentBlock.Item label="vCPU, RAM">
            <Select
              size="sm"
              value={`${pool.capacity.tikv.vcpu}, ${pool.capacity.tikv.memory}`}
              data={[`${pool.capacity.tikv.vcpu}, ${pool.capacity.tikv.memory}`]}
              disabled
            />
          </ComponentBlock.Item>
          <ComponentBlock.Item label="Storage × Nodes">
            <Group gap={8} align="flex-start" wrap="nowrap">
              <NumberInput
                size="sm"
                min={getPrimaryStorageSize(tikv)}
                value={tikvStorage}
                onChange={(value) => setTikvStorage(Number(value) || getPrimaryStorageSize(tikv))}
                suffix=" GiB"
                sx={{ flexGrow: 1 }}
              />
              <Center h={32}>
                <IconXClose size={16} />
              </Center>
              <NumberInput
                size="sm"
                min={pool.capacity.tikv.nodes}
                max={Number(constraints?.tikv?.nodeCount?.max ?? undefined) || undefined}
                step={tikvNodeStep}
                value={tikvNodes}
                onChange={(value) => setTikvNodes(normalizeNodeCountByStep(value, fallbackTikvNodes, tikvNodeStep))}
                onBlur={() => setTikvNodes(nextTikvNodes)}
                suffix={getNodesSuffix(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 capacity"
              checked={tiflashEnabled}
              onChange={(event) => {
                const checked = event.currentTarget.checked
                if (!checked && currentTiflash) {
                  openConfirmModal({
                    title: 'Disable TiFlash?',
                    centered: true,
                    children: (
                      <Stack gap="sm">
                        <Typography>Disabling TiFlash will affect all BYOC instances in this Resource Pool.</Typography>
                        <Typography>
                          TiFlash resources will be removed, and analytical workloads that depend on TiFlash may become
                          unavailable or experience performance degradation.
                        </Typography>
                      </Stack>
                    ),
                    labels: {
                      cancel: 'Cancel',
                      confirm: 'Disable TiFlash'
                    },
                    onConfirm: () => setTiflashEnabled(false)
                  })
                  return
                }
                setTiflashEnabled(checked)
              }}
              disabled={!tiflash}
            />
          }
        >
          {tiflashEnabled && (
            <>
              <ComponentBlock.Item label="vCPU, RAM">
                <Select
                  size="sm"
                  value={tiflashSpecValue}
                  data={tiflashSpecOptions}
                  onChange={(value) => {
                    const next = tiflashSpecOptions.find((option) => option.value === value)
                    if (!next) {
                      return
                    }
                    setTiflashSpecValue(next.value)
                    setTiflashStorage(getPrimaryStorageSize(next.capacity))
                  }}
                  disabled={tiflashSpecOptions.length <= 1}
                />
              </ComponentBlock.Item>
              <ComponentBlock.Item label="Storage × Nodes">
                <Group gap={8} align="flex-start" wrap="nowrap">
                  <NumberInput
                    size="sm"
                    min={getPrimaryStorageSize(selectedTiflash)}
                    value={tiflashStorage}
                    onChange={(value) => setTiflashStorage(Number(value) || getPrimaryStorageSize(selectedTiflash))}
                    suffix=" GiB"
                    sx={{ flexGrow: 1 }}
                  />
                  <Center h={32}>
                    <IconXClose size={16} />
                  </Center>
                  <NumberInput
                    size="sm"
                    min={Number.isFinite(tiflashMinNodes) ? tiflashMinNodes : 0}
                    max={Number(constraints?.tiflash?.nodeCount?.max ?? undefined) || undefined}
                    step={tiflashNodeStep}
                    value={tiflashNodes}
                    onChange={setTiflashNodes}
                    onBlur={() => setTiflashNodes(getPositiveNodeCount(String(tiflashNodes)))}
                    suffix={getNodesSuffix(tiflashNodes)}
                    sx={{ flexGrow: 1 }}
                  />
                </Group>
              </ComponentBlock.Item>
            </>
          )}
        </ComponentBlock>
      </Stack>
      {hasVcpuLimit && (
        <Group
          justify="space-between"
          p="lg"
          mt="md"
          sx={(theme) => ({
            border: `1px solid ${theme.colors.carbon[4]}`,
            borderRadius: theme.radius.md
          })}
        >
          <EvaluationItem label="Current vCPU Usage" value={pool.currentVcpuProvision} />
          <EvaluationItem
            label="Scaling vCPU Preview"
            value={projectedVcpuProvision}
            tooltipLabel="Review the estimated vCPU usage before applying changes."
          />
          <EvaluationItem label="Max vCPU Limit" value={pool.poolVcpuLimit!} />
          <EvaluationItem
            label="Scale Evaluation"
            value={isOverVcpuLimit ? 'Exceeds limit' : 'Allowed'}
            tooltipLabel="Indicates whether the scaling plan stays within the max vCPU limit."
            warning={isOverVcpuLimit}
          />
        </Group>
      )}
      {isOverVcpuLimit && (
        <Alert color="yellow" icon={<IconAlertTriangle size={16} />} mt="md">
          This scaling plan exceeds the maximum vCPU limit. Please adjust the scaling nodes value above or go to{' '}
          <Anchor component="button" type="button" onClick={onResetLimit}>
            reset the max limit
          </Anchor>
          .
        </Alert>
      )}
      <DialogActions
        primaryLabel="Save"
        disabled={!tikv || error || isOverVcpuLimit}
        onClose={onClose}
        onPrimary={submit}
        loading={loading}
        error={submitError}
      />
    </>
  )
}

const TITLES: Record<ResourcePoolDialogAction, (pool: ResourcePool) => React.ReactNode> = {
  rename: (pool) => (
    <>
      Rename <ModalCodeTitle>{pool.name}</ModalCodeTitle>
    </>
  ),
  delete: (pool) => (
    <>
      Delete <ModalCodeTitle>{pool.name}</ModalCodeTitle>
    </>
  ),
  limit: (pool) => (
    <>
      <ModalCodeTitle>{pool.name}</ModalCodeTitle> vCPU Limit
    </>
  ),
  tags: (pool) => (
    <>
      {pool.tags.length ? 'Edit' : 'Add'} {pool.cloud} Resource Tags for <ModalCodeTitle>{pool.name}</ModalCodeTitle>
    </>
  ),
  capacity: (pool) => (
    <>
      Update <ModalCodeTitle>{pool.name}</ModalCodeTitle> Capacity
    </>
  )
}

const SILENT_BYOC_QUERY_REQUEST = { isShowToast: false, isHandleErr: false }

export const ResourcePoolDialogs = ({
  action,
  opened,
  pool,
  onClose,
  onActionChange,
  onUpdated,
  onDeleted
}: ResourcePoolDialogsProps) => {
  const {
    store: {
      lastVisitLocation: { orgId, projectId }
    }
  } = useStores()
  const params = { orgId, projectId }
  const deleteMutation = useResourcePoolServiceDeleteResourcePool()
  const tagsMutation = useResourcePoolServiceUpsertResourcePoolCloudResourceTags({
    request: { isShowToast: false, isHandleErr: false }
  })
  const [isUpdating, setIsUpdating] = useState(false)
  const [isResizing, setIsResizing] = useState(false)
  const [dialogError, setDialogError] = useState('')
  const constraintsQuery = useResourcePoolServiceGetResourcePoolCapacityConstraints(pool.id, params, {
    query: { enabled: opened && action === 'capacity' && !!pool.id && !!orgId && !!projectId, retry: 1 },
    request: SILENT_BYOC_QUERY_REQUEST
  })

  useEffect(() => {
    setDialogError('')
  }, [action, opened, pool.id])

  const finish = (message: string) => {
    notifier.success(message)
    onUpdated?.()
    onClose()
  }
  const reportError = (error: unknown) => {
    const message = getErrorMessage(error, true) || 'The resource pool request failed.'
    setDialogError(message)
    notifier.error(message)
  }

  const rename = async (displayName: string) => {
    setIsUpdating(true)
    try {
      await updateResourcePool(pool.id, { displayName }, { ...params, updateMask: 'display_name' })
      finish('Resource pool renamed.')
    } catch (error) {
      reportError(error)
    } finally {
      setIsUpdating(false)
    }
  }
  const remove = async () => {
    try {
      await deleteMutation.mutateAsync({ resourcePoolId: pool.id, params })
      notifier.success('Resource pool deleted.')
      onDeleted?.()
      onUpdated?.()
      onClose()
    } catch (error) {
      reportError(error)
    }
  }
  const updateLimit = async (vcpuLimit: V1beta2VcpuLimit) => {
    setIsUpdating(true)
    try {
      await updateResourcePool(pool.id, { vcpuLimit }, { ...params, updateMask: 'vcpu_limit' })
      finish('Pool vCPU limit updated.')
    } catch (error) {
      reportError(error)
    } finally {
      setIsUpdating(false)
    }
  }
  const updateTags = async (cloudResourceTags: Record<string, string>) => {
    const nextTagKeys = new Set(Object.keys(cloudResourceTags))
    const removeCloudResourceTagKeys = pool.tags
      .map((tag) => tag.key.trim())
      .filter((key) => key && !nextTagKeys.has(key))
    const data: ResourcePoolCloudResourceTagsBody = {
      cloudResourceTags,
      ...(removeCloudResourceTagKeys.length ? { removeCloudResourceTagKeys } : {})
    }

    try {
      await tagsMutation.mutateAsync({
        resourcePoolId: pool.id,
        data,
        params
      })
      finish('Resource tags updated.')
    } catch (error) {
      reportError(error)
    }
  }
  const resize = async (targetCapacity: Nextgenv1beta2ResourcePoolCapacity) => {
    setIsResizing(true)
    try {
      await resizeResourcePoolCapacity(pool.id, targetCapacity, { ...params, validateOnly: true })
      await resizeResourcePoolCapacity(pool.id, targetCapacity, params)
      finish('Resource pool capacity update submitted.')
    } catch (error) {
      reportError(error)
    } finally {
      setIsResizing(false)
    }
  }

  return (
    <Modal
      opened={opened}
      onClose={onClose}
      centered
      size={action === 'capacity' || action === 'tags' ? 720 : 640}
      title={action ? TITLES[action](pool) : ''}
      data-testid="resource-pool-dialog"
    >
      {action === 'rename' && (
        <RenameDialog pool={pool} onClose={onClose} onSubmit={rename} loading={isUpdating} error={dialogError} />
      )}
      {action === 'delete' && (
        <DeleteDialog
          pool={pool}
          onClose={onClose}
          onSubmit={remove}
          loading={deleteMutation.isLoading}
          error={dialogError}
        />
      )}
      {action === 'limit' && (
        <LimitDialog pool={pool} onClose={onClose} onSubmit={updateLimit} loading={isUpdating} error={dialogError} />
      )}
      {action === 'tags' && (
        <TagsDialog
          pool={pool}
          onClose={onClose}
          onSubmit={updateTags}
          loading={tagsMutation.isLoading}
          error={dialogError}
        />
      )}
      {action === 'capacity' && (
        <CapacityDialog
          pool={pool}
          constraints={constraintsQuery.data}
          onClose={onClose}
          onSubmit={resize}
          onResetLimit={() => onActionChange?.('limit')}
          loading={constraintsQuery.isLoading || isResizing}
          error={constraintsQuery.isError}
          submitError={dialogError}
        />
      )}
    </Modal>
  )
}
