import { zodResolver } from '@hookform/resolvers/zod'
import {
  Alert,
  Box,
  Button,
  Divider,
  Group,
  Input,
  notifier,
  Paper,
  SelectProps,
  Stack,
  Typography
} from '@tidbcloud/uikit'
import {
  DateTimePicker,
  FormErrorMessage,
  FormNumberInput,
  FormRadioGroup,
  FormSelect,
  FormTextInput,
  useDateTimePicker
} from '@tidbcloud/uikit/biz'
import { IconCheck, IconRefreshCw02 } from '@tidbcloud/uikit/icons'
import { useMemoizedFn } from 'ahooks'
import { observer } from 'mobx-react'
import { useEffect, useId, useMemo, useRef, useState } from 'react'
import { FormProvider, useForm } from 'react-hook-form'
import { useHistory } from 'react-router-dom'
import * as z from 'zod'

import { SharedCluster } from 'dbaas/features/ClusterManagement/cluster'
import { renderNodes, renderStorage } from 'dbaas/features/ClusterManagement/common'
import {
  TI_PROXY_AVALIABLE_NODE_COUNTS,
  useCheckArch2024ProjectExp,
  useTiproxyEnabled
} from 'dbaas/features/ClusterManagement/dedicated'
import { WaringForSpecInSummary } from 'dbaas/features/ClusterManagement/dedicated/node/NodeSizes/WarningForSpecInSummary'
import { useOrgProjectClusterIds } from 'dbaas/features/Common'
import { CloudProviderLabelConst } from 'dbaas/features/Project'
import { HeadLabel } from 'dbaas/screens/ClustersV2/new/components'
import { ClusterNodeSizes } from 'dbaas/screens/ClustersV2/new/Dedicated/FormContent/NodeSizes'
import { useFetchClusterCosts } from 'dbaas/screens/ClustersV2/new/Dedicated/helpers'
import {
  NewClusterPricingSummary,
  NodeSizeSkeleton,
  useNodeProfileTargets
} from 'dbaas/screens/ClustersV2/new/Dedicated/SumCard/SumCard'
import { CreationFormValueState, renderStorageType, useGetNodeProfiles } from 'dbaas/screens/ClustersV2/new/helpers'
import { CMEKActiveAlert } from 'dbaas/screens/ClustersV2/new/Wrapper/CreationForm'
import { useSetUpRegionalCidr } from 'dbaas/screens/NextGen/my-tidb-creation/plans/dedicated/hooks/use-setup-regional-cidr'
import { ProjectCidrSection } from 'dbaas/screens/NextGen/my-tidb-creation/plans/dedicated/sections/ProjectCidrSection'
import { RestoreLayout } from 'dbaas/screens/Restore/RestoreLayout'
import { RestoreSummaryCard, RestoreSummaryItem, RestoreSummarySection } from 'dbaas/screens/Restore/RestoreSummary'
import { useCustomerManagedEncryptionKeyServiceListCustomerManagedEncryptionKeys } from 'dbaas/services/api-clients/v2023/dedicated/v1beta1'
import { useListBackups, useListPiTRTimeRanges, useRestoreBackup } from 'dbaas/services/dataflow'
import {
  DataflowComponentType,
  type DataflowPiTRTimeRangeListResp,
  DataflowRestoreMode,
  DataflowSimpleClusterComponent,
  ListBackupsStatus
} from 'dbaas/services/dataflow/interface'
import { getErrorMessage } from 'dbaas/services/errorCodes'
import useStores from 'dbaas/stores/useStores'
import { formatTime } from 'dbaas/utils/format'
import { cidrRegEx } from 'dbaas/utils/regex'
import { clusterNameRules, tidbPortRules } from 'dbaas/utils/validationRules'

import { renderSpecVersion, useSpecVersionEnabled } from '../Cluster/OverviewV2/helpers'

import {
  getDedicatedRestoreInitialValues,
  getDedicatedRestorePitrRegionOptions,
  getDedicatedRestoreRegionOptions,
  getDedicatedRestoreTimeRange,
  getPreferredDedicatedRestoreRegion,
  getSourceTikvNodeCount,
  type RestoreRegionOption
} from './RestoreForDedicated.helpers'

interface RestoreFormData {
  restore_mode: DataflowRestoreMode
  /**
   * backup snapshot id or '0' for pitr
   */
  id: string
  timePoint: Date
  dedicated: CreationFormValueState['dedicated']
}

const createSchema = (data: RestoreFormData, sourceTikvNodeCount: number) => {
  return z
    .object({
      restore_mode: z.string(),

      ...(data.restore_mode === DataflowRestoreMode.RESTORE_MODE_SNAPSHOT && {
        id: z.string({ required_error: 'Required' }).min(1, 'Required')
      }),
      ...(data.restore_mode === DataflowRestoreMode.RESTORE_MODE_PITR && {
        timePoint: z.date({ required_error: 'Required' })
      }),

      dedicated: z.object({
        provider: z.string(),
        name: z
          .string({ required_error: clusterNameRules.required })
          .regex(clusterNameRules.pattern.value, clusterNameRules.pattern.message),
        region: z.string(),
        projectCIDR: z.string().trim().regex(cidrRegEx, 'Please enter a valid CIDR.').optional(),
        cmekId: z.string().optional(),
        port: z
          .number({ required_error: tidbPortRules.required })
          .min(tidbPortRules.min.value, tidbPortRules.min.message)
          .max(tidbPortRules.max.value, tidbPortRules.max.message)
          .refine((v) => Number(v) !== 10080, {
            message: 'Invalid port number, valid port number is from 1024 to 65535 except for port 10080'
          }),
        sizes: z.object({
          tidb: z.object({
            profileName: z.string({ required_error: 'Please choose a vCPU + RAM size.' }),
            replicas: z.number(),
            tiproxySetting: z
              .object({
                nodeSpecKey: z.string().optional(),
                nodeCount: z.number().refine((v) => TI_PROXY_AVALIABLE_NODE_COUNTS.includes(v), '')
              })
              .optional()
          }),
          tikv: z.object({
            profileName: z.string({ required_error: 'Please choose a vCPU + RAM size.' }),
            replicas: z
              .number()
              .refine((v) => v % 3 === 0, 'The number of nodes must be a multiple of 3.')
              .refine(
                (v) => v >= sourceTikvNodeCount,
                `TiKV node count must be at least ${sourceTikvNodeCount} to match the source cluster topology at the selected restore point.`
              ),
            storageGiB: z.number(),
            storageType: z.string().optional(),
            raftStoreIops: z.number().optional(),
            dataStoreIops: z.number().optional()
          }),
          tiflash: z.object({
            profileName: z.string().nullable().optional(),
            replicas: z.number().optional(),
            storageGiB: z.number().optional(),
            storageType: z.string().optional()
          })
        })
      })
    })
    .passthrough()
}

type SnapshotOption = { key: string; label: string; value: string; regionsLabel: string }

function formatRestoreRegionLabels(options: RestoreRegionOption[]) {
  return options.map((option) => option.label).join(', ')
}

const renderSnapshotOption: SelectProps['renderOption'] = ({ option, checked }) => {
  const snapshotOption = option as typeof option & { regionsLabel?: string }

  return (
    <Group wrap="nowrap" justify="space-between" sx={{ flex: 1 }}>
      <Stack gap={0}>
        <Typography variant="body-lg">{option.label}</Typography>
        <Typography variant="body-md" c="carbon.7">
          Available regions: {snapshotOption.regionsLabel}
        </Typography>
      </Stack>
      {checked && <IconCheck size={16} strokeWidth={3} style={{ marginInlineStart: 'auto' }} />}
    </Group>
  )
}

interface RestoreForDedicatedSummaryProps {
  formId: string
  cluster: SharedCluster
  backupId: string
  snapShotOptions: SnapshotOption[]
  restoreMode: DataflowRestoreMode
  restoreModeLabel?: string
  timePoint: Date
  timezone: number
  cmekId?: string
  provider: string
  region: string
  restoreRegionLabel: string
  name: string
  port: number
  projectCIDR?: string
  cidrConfigured: boolean
  sizes: RestoreFormData['dedicated']['sizes']
  isLoading: boolean
  nodeGroupEnabled: boolean
  profileTargets: ReturnType<typeof useNodeProfileTargets>
  specVersionEnabled?: boolean
  costs: ReturnType<typeof useFetchClusterCosts>['costs']
  totalCost?: number | null
  costError: ReturnType<typeof useFetchClusterCosts>['error']
  restore: ReturnType<typeof useRestoreBackup>
  onCancel: () => void
  disableSubmit: boolean
  submitLoading: boolean
  isCalculating: boolean
}

function RestoreForDedicatedSummary({
  formId,
  cluster,
  backupId,
  snapShotOptions,
  restoreMode,
  restoreModeLabel,
  timePoint,
  timezone,
  cmekId,
  provider,
  region,
  restoreRegionLabel,
  name,
  port,
  projectCIDR,
  cidrConfigured,
  sizes,
  isLoading,
  nodeGroupEnabled,
  profileTargets,
  specVersionEnabled,
  costs,
  totalCost,
  costError,
  restore,
  onCancel,
  disableSubmit,
  submitLoading,
  isCalculating
}: RestoreForDedicatedSummaryProps) {
  return (
    <RestoreSummaryCard
      description="Your new cluster will be:"
      footer={
        <>
          <NewClusterPricingSummary
            error={getErrorMessage(costError)}
            totalCost={totalCost ?? 0}
            costsDetail={costs}
            mt="sm"
          />

          <Stack gap="md" mt="md">
            <Group justify="flex-end">
              <Button variant="default" onClick={onCancel} data-mp-event="Dedicated Restore Cancel Clicked">
                Cancel
              </Button>

              <Button
                type="submit"
                form={formId}
                data-mp-event="Dedicated Restore Submit Clicked"
                disabled={disableSubmit}
                loading={submitLoading}
                sx={{ flex: 1 }}
              >
                {submitLoading ? 'Fetching...' : isCalculating ? 'Calculating...' : 'Restore'}
              </Button>
            </Group>

            <FormErrorMessage message={getErrorMessage(restore.error, true)} />
          </Stack>
        </>
      }
    >
      <RestoreSummarySection title="Select Backup">
        <RestoreSummaryItem label="Dedicated Cluster" value={cluster.displayName} labelWidth={100} />
        <RestoreSummaryItem label="Restore Mode" value={restoreModeLabel ?? '-'} labelWidth={100} />
        {restoreMode === DataflowRestoreMode.RESTORE_MODE_SNAPSHOT && (
          <RestoreSummaryItem
            label="Snapshot"
            value={snapShotOptions.find((item) => item.value === backupId)?.label ?? '-'}
            labelWidth={100}
          />
        )}
        {restoreMode === DataflowRestoreMode.RESTORE_MODE_PITR && (
          <RestoreSummaryItem
            label="Time Point"
            value={formatTime(timePoint, {
              offset: timezone,
              showSeconds: true,
              showTimezone: true
            })}
            labelWidth={100}
          />
        )}
      </RestoreSummarySection>

      <RestoreSummarySection title="Restore to a New Dedicated Cluster">
        <RestoreSummaryItem label="Provider" value={CloudProviderLabelConst[provider]} labelWidth={100} />
        <RestoreSummaryItem label="Region" value={restoreRegionLabel || region} labelWidth={100} />
        <RestoreSummaryItem label="Name" value={name || '-'} labelWidth={100} />
        <RestoreSummaryItem label="Port" value={port} labelWidth={100} />
        {!cidrConfigured && <RestoreSummaryItem label="CIDR" value={projectCIDR || '-'} labelWidth={100} />}
        {cmekId && <RestoreSummaryItem label="CMEK ID" value={cmekId} labelWidth={100} />}
      </RestoreSummarySection>

      <RestoreSummarySection title="Cluster Size">
        <RestoreSummaryItem
          label="TiDB"
          labelWidth={100}
          value={
            isLoading ? (
              <NodeSizeSkeleton />
            ) : (
              <Stack justify="flex-start" gap={8}>
                <Group gap={4}>
                  {profileTargets.tidb.label}
                  {renderSpecVersion(profileTargets.tidb.version, specVersionEnabled)}
                  <WaringForSpecInSummary
                    cpuNum={profileTargets.tidb.profile?.vCpu ?? 0}
                    memory={profileTargets.tidb.profile?.memorySizeGi ?? 0}
                  />
                </Group>
                <Box>
                  {nodeGroupEnabled ? 'Default Group: ' : ''}
                  {renderNodes(sizes?.tidb?.replicas)}
                </Box>
              </Stack>
            )
          }
        />

        <RestoreSummaryItem
          label="TiKV"
          labelWidth={100}
          value={
            isLoading ? (
              <NodeSizeSkeleton />
            ) : (
              <Stack justify="flex-start" gap={8}>
                <Group gap={4}>
                  {profileTargets.tikv.label}
                  {renderSpecVersion(profileTargets.tikv.version, specVersionEnabled)}
                  <WaringForSpecInSummary
                    cpuNum={profileTargets.tikv.profile?.vCpu ?? 0}
                    memory={profileTargets.tikv.profile?.memorySizeGi ?? 0}
                  />
                </Group>
                <Box>
                  {sizes?.tikv?.storageGiB || 0} GiB Storage × {renderNodes(sizes?.tikv?.replicas)}
                </Box>
                {sizes?.tikv?.storageType && <Box>{renderStorageType(sizes?.tikv?.storageType)} storage</Box>}
                {sizes?.tikv?.raftStoreIops && <Box>{sizes?.tikv?.raftStoreIops} Extra Raft IOPS</Box>}
                {sizes?.tikv?.storageType === 'Plus' && sizes.tikv.dataStoreIops && (
                  <Box>{sizes.tikv.dataStoreIops.toLocaleString()} Data Disk IOPS</Box>
                )}
              </Stack>
            )
          }
        />

        {!!sizes?.tiflash?.replicas && (
          <RestoreSummaryItem
            label="TiFlash"
            labelWidth={100}
            value={
              isLoading ? (
                <NodeSizeSkeleton />
              ) : (
                <Stack justify="flex-start" gap={8}>
                  <Group gap={4}>
                    {profileTargets.tiflash.label}
                    {renderSpecVersion(profileTargets.tiflash.version, specVersionEnabled)}
                    <WaringForSpecInSummary
                      cpuNum={profileTargets.tiflash.profile?.vCpu ?? 0}
                      memory={profileTargets.tiflash.profile?.memorySizeGi ?? 0}
                    />
                  </Group>
                  <Box>
                    {renderStorage(sizes?.tiflash?.storageGiB)} × {renderNodes(sizes?.tiflash?.replicas)}
                  </Box>
                  {sizes?.tiflash.storageType && <Box>{renderStorageType(sizes?.tiflash.storageType)} storage</Box>}
                </Stack>
              )
            }
          />
        )}
      </RestoreSummarySection>
    </RestoreSummaryCard>
  )
}

const _RestoreForDedicated = ({ cluster }: { cluster: SharedCluster }) => {
  const {
    store: {
      tenantData,
      activeProject: { aws_cmek_enabled: awsEnableCmek }
    }
  } = useStores()
  const history = useHistory()
  const formId = useId()
  const sourceTikvNodeCount = getSourceTikvNodeCount(cluster)
  const methods = useForm<RestoreFormData>({
    defaultValues: {
      restore_mode: DataflowRestoreMode.RESTORE_MODE_SNAPSHOT,
      timePoint: new Date(),
      id: new URLSearchParams(window.location.search).get('backup_id') ?? '',
      dedicated: {
        provider: cluster.provider,
        region: cluster.region,
        projectCIDR: undefined,
        name: '',
        port: 4000,
        specVersion: 'V2'
      }
    },
    resolver: async (data, context, options) => {
      const schema = createSchema(data, sourceTikvNodeCount)

      // you can debug your validation schema here
      // console.log('formData', data, context, options)
      // console.log('validation result', await zodResolver(schema)(data, context, options))
      return zodResolver(schema)(data, context, options)
    }
  })
  const { handleSubmit, watch, setValue, setError, setFocus } = methods
  const backupId = watch('id')
  const timePoint = watch('timePoint')
  const restoreMode = watch('restore_mode')
  const provider = watch('dedicated.provider')
  const region = watch('dedicated.region')
  const name = watch('dedicated.name')
  const port = watch('dedicated.port')
  const projectCIDR = watch('dedicated.projectCIDR')
  const sizes = watch('dedicated.sizes')
  const cmekId = watch('dedicated.cmekId')

  const { orgId, projectId } = useOrgProjectClusterIds()
  const [projectCidrConfiguredStatus, setProjectCidrConfiguredStatus] = useState<boolean>()
  const projectCidrConfigured = projectCidrConfiguredStatus === true
  const cidrStatusLoading = !!provider && !!region && typeof projectCidrConfiguredStatus === 'undefined'
  const { setUpRegionalCidr, isLoading: isSettingProjectCidr } = useSetUpRegionalCidr({
    orgId,
    projectId,
    configured: projectCidrConfigured
  })

  useCustomerManagedEncryptionKeyServiceListCustomerManagedEncryptionKeys(
    {
      orgId,
      projectId
    },
    {
      query: {
        refetchOnMount: false,
        refetchOnWindowFocus: false,
        enabled: !!orgId && !!projectId && awsEnableCmek,
        onSuccess(data) {
          if (awsEnableCmek) {
            const cmekData = data?.cmeks?.find((item) => item.regionId === `${provider}-${region}`)
            setValue(
              'dedicated.cmekId',
              cmekData?.awsKms ? cmekData.awsKms.kmsKeyArn : cmekData?.azureKeyVault?.keyVaultKey
            )
          }
        }
      }
    }
  )

  const { data: backupListData, isLoading: isLoadingBackups } = useListBackups(
    orgId,
    projectId,
    cluster.id,
    {
      page: 1,
      per_page: 100,
      status: ListBackupsStatus.BACKUP_STATUS_SUCCESS
    },
    { query: { enabled: !!cluster.id } }
  )

  const setLatestRestorePoint = useMemoizedFn(
    (ranges: DataflowPiTRTimeRangeListResp | undefined, restoreRegion: string) => {
      const latestRange = getDedicatedRestoreTimeRange(ranges, restoreRegion)

      if (latestRange?.end_ts) {
        setValue('timePoint', new Date(latestRange.end_ts))
      }
    }
  )
  const getPreferredPitrRegion = useMemoizedFn(
    (ranges: DataflowPiTRTimeRangeListResp | undefined, currentRegion: string) =>
      getPreferredDedicatedRestoreRegion({
        options: getDedicatedRestorePitrRegionOptions({ timeRanges: ranges }),
        currentRegion,
        fallbackRegion: cluster.region
      })
  )
  const syncPitrRegion = useMemoizedFn((ranges: DataflowPiTRTimeRangeListResp | undefined, currentRegion: string) => {
    const restoreRegion = getPreferredPitrRegion(ranges, currentRegion)

    setValue('dedicated.region', restoreRegion)
    setLatestRestorePoint(ranges, restoreRegion)
  })

  const {
    data: timeRanges,
    isFetching: isLoadingTimeRange,
    refetch: refetchTimeRanges
  } = useListPiTRTimeRanges(orgId, projectId, cluster.id, {
    query: {
      enabled: !!cluster.id,
      refetchOnWindowFocus: false,
      onSuccess: (ranges) => {
        if (restoreMode === DataflowRestoreMode.RESTORE_MODE_PITR) {
          syncPitrRegion(ranges, region)
        } else {
          setLatestRestorePoint(ranges, region)
        }
      }
    }
  })
  const selectedRegionTimeRange = useMemo(() => getDedicatedRestoreTimeRange(timeRanges, region), [timeRanges, region])

  const noTimeRange = !selectedRegionTimeRange

  const tidbVersion = useMemo(() => {
    if (restoreMode === DataflowRestoreMode.RESTORE_MODE_SNAPSHOT) {
      return backupId ? backupListData?.items.find((i) => i.id === backupId)?.cluster.cluster_version : undefined
    } else if (restoreMode === DataflowRestoreMode.RESTORE_MODE_PITR) {
      return cluster.version
    }
  }, [restoreMode, backupId, backupListData, cluster.version])

  const { expEnabled: nodeGroupEnabled, isLoading: nodeGroupLoading } = useCheckArch2024ProjectExp('node-group')
  const { tiproxyEnabled, isLoading: tiproxyEnabledLoading } = useTiproxyEnabled()

  const { nodeProfiles, isLoading: nodeProfilesLoading } = useGetNodeProfiles({
    tier: 'dedicated',
    provider,
    region,
    tidbVersion
  })

  const { specVersionEnabled, isLoading: specVersionEnabledLoading } = useSpecVersionEnabled(provider)
  const hasSourceTiproxy = useMemo(
    () =>
      cluster.dedicated?.tidbNodeSetting?.tidbNodeGroups?.some((group) => (group.tiproxySetting?.nodeCount ?? 0) > 0) ??
      false,
    [cluster.dedicated?.tidbNodeSetting?.tidbNodeGroups]
  )
  const waitForTiproxyFlag = hasSourceTiproxy && tiproxyEnabledLoading
  const restoreInitialTiproxyEnabled = hasSourceTiproxy ? tiproxyEnabled : false
  const restoreSizeInitializedRef = useRef(false)

  useEffect(() => {
    if (restoreSizeInitializedRef.current) {
      return
    }

    if (nodeProfilesLoading || specVersionEnabledLoading || waitForTiproxyFlag || !nodeProfiles.length) {
      return
    }

    const { sizes: initialSizes, specVersion } = getDedicatedRestoreInitialValues({
      cluster,
      nodeProfiles,
      specVersionEnabled: !!specVersionEnabled,
      tiproxyEnabled: restoreInitialTiproxyEnabled
    })

    setValue('dedicated.specVersion', specVersion)
    setValue('dedicated.sizes', initialSizes)
    restoreSizeInitializedRef.current = true
  }, [
    cluster,
    nodeProfiles,
    nodeProfilesLoading,
    restoreInitialTiproxyEnabled,
    setValue,
    specVersionEnabled,
    specVersionEnabledLoading,
    waitForTiproxyFlag
  ])

  const profileTargets = useNodeProfileTargets({
    tidbProfileName: sizes?.tidb?.profileName,
    tikvProfileName: sizes?.tikv?.profileName,
    tiflashProfileName: sizes?.tiflash?.profileName,
    nodeProfiles: nodeProfiles!
  })

  const restoreModeOptions = useMemo(() => {
    return [
      { label: 'Snapshot Restore', value: DataflowRestoreMode.RESTORE_MODE_SNAPSHOT },
      {
        label: 'Point-in-Time Restore',
        value: DataflowRestoreMode.RESTORE_MODE_PITR,
        tooltip:
          timeRanges?.msg || noTimeRange ? (
            <Typography maw={400}>
              There is no available time point for this region currently, please make sure you have enabled
              Point-in-Time Restore and try it again later.
            </Typography>
          ) : (
            ''
          ),
        disabled: !!timeRanges?.msg || noTimeRange
      }
    ]
  }, [timeRanges, noTimeRange])

  const [pickerStartDate, pickerEndDate] = useMemo(() => {
    return [
      selectedRegionTimeRange?.start_ts ? new Date(selectedRegionTimeRange.start_ts) : undefined,
      selectedRegionTimeRange?.end_ts ? new Date(selectedRegionTimeRange.end_ts) : undefined
    ]
  }, [selectedRegionTimeRange])

  const snapShotOptions = useMemo(
    () =>
      backupListData?.items.map((i) => ({
        key: i.id,
        label: `${i.name} (${formatTime(i.backup_ts ?? i.created_at, {
          offset: tenantData.timezone,
          showSeconds: true,
          showTimezone: true
        })})`,
        value: i.id,
        regionsLabel:
          formatRestoreRegionLabels(getDedicatedRestoreRegionOptions({ regions: i.regions })) ||
          cluster.regionDisplayName ||
          cluster.region
      })) ?? [],
    [backupListData, tenantData.timezone, cluster.region, cluster.regionDisplayName]
  )

  const selectedBackup = useMemo(
    () => backupListData?.items.find((item) => item.id === backupId),
    [backupListData, backupId]
  )

  const {
    costs,
    totalCost,
    isLoading: isCostLoading,
    error: costError
  } = useFetchClusterCosts(
    {
      isAllValid: true,
      provider,
      region,
      sizes
    },
    false
  )

  // new cluster's region
  const regionOptions = useMemo(
    () =>
      (() => {
        if (restoreMode === DataflowRestoreMode.RESTORE_MODE_SNAPSHOT) {
          const snapshotRegionOptions = getDedicatedRestoreRegionOptions({ regions: selectedBackup?.regions })

          if (snapshotRegionOptions.length) {
            return snapshotRegionOptions
          }
        }

        if (restoreMode === DataflowRestoreMode.RESTORE_MODE_PITR) {
          const pitrRegionOptions = getDedicatedRestorePitrRegionOptions({ timeRanges })

          if (pitrRegionOptions.length) {
            return pitrRegionOptions
          }
        }

        return [{ key: cluster.region, label: cluster.regionDisplayName || cluster.region, value: cluster.region }]
      })(),
    [restoreMode, selectedBackup?.regions, timeRanges, cluster.region, cluster.regionDisplayName]
  )

  useEffect(() => {
    const restoreRegion = getPreferredDedicatedRestoreRegion({
      options: regionOptions,
      currentRegion: region,
      fallbackRegion: cluster.region
    })

    if (region !== restoreRegion) {
      setValue('dedicated.region', restoreRegion)
    }
  }, [cluster.region, region, regionOptions, setValue])

  const restore = useRestoreBackup()
  const restoreModeLabel = restoreModeOptions.find((item) => item.value === restoreMode)?.label
  const restoreRegionLabel = regionOptions.find((item) => item.value === region)?.label ?? region

  const isLoading =
    isLoadingBackups ||
    isLoadingTimeRange ||
    nodeGroupLoading ||
    nodeProfilesLoading ||
    specVersionEnabledLoading ||
    waitForTiproxyFlag
  const disablePitrSelect = noTimeRange || isLoading
  const dateTimePickerProps = useDateTimePicker({
    formatter: (val) => {
      return formatTime(val, {
        offset: tenantData.timezone,
        showSeconds: true,
        showTimezone: true
      })
    },
    startDate: pickerStartDate,
    endDate: pickerEndDate,
    value: timePoint,
    onChange: (val) => setValue('timePoint', val)
  })
  const handleRefreshLatestRestorePoint = useMemoizedFn(async () => {
    const result = await refetchTimeRanges()
    setLatestRestorePoint(result.data, region)
  })
  const handleRestoreModeChange = useMemoizedFn((nextMode: string) => {
    if (nextMode === DataflowRestoreMode.RESTORE_MODE_PITR) {
      syncPitrRegion(timeRanges, region)
    }
  })
  const handleRestoreRegionChange = useMemoizedFn((nextRegion: string | null) => {
    if (!nextRegion) {
      return
    }

    if (restoreMode === DataflowRestoreMode.RESTORE_MODE_PITR) {
      setLatestRestorePoint(timeRanges, nextRegion)
    }
  })
  const isCalculating = isCostLoading
  const disableSubmit = (() => {
    if (cidrStatusLoading) {
      return true
    }

    if (restoreMode === DataflowRestoreMode.RESTORE_MODE_PITR) {
      return noTimeRange || !!timeRanges?.msg
    }

    return false
  })()

  const onSubmit = useMemoizedFn(async (data: RestoreFormData) => {
    const isPitr = restoreMode === DataflowRestoreMode.RESTORE_MODE_PITR
    if (typeof projectCidrConfiguredStatus === 'undefined') {
      notifier.error('Project CIDR is still loading. Please try again in a moment.')
      return
    }

    const normalizedProjectCIDR = data.dedicated.projectCIDR?.trim() ?? ''
    if (!projectCidrConfigured && !normalizedProjectCIDR) {
      setError('dedicated.projectCIDR', { type: 'manual', message: 'Please enter a valid CIDR.' })
      setFocus('dedicated.projectCIDR', { shouldSelect: true })
      return
    }

    const cidrErrMsg = await setUpRegionalCidr({
      provider: data.dedicated.provider,
      region: data.dedicated.region,
      cidr: normalizedProjectCIDR
    })
    if (cidrErrMsg) {
      notifier.error(<div dangerouslySetInnerHTML={{ __html: cidrErrMsg }} />)
      return
    }

    const components: DataflowSimpleClusterComponent[] = [
      {
        component_type: DataflowComponentType.COMPONENT_TYPE_TIDB,
        node_profile_name: sizes?.tidb?.profileName,
        replicas: sizes?.tidb?.replicas,
        storage_size_gi: sizes?.tidb?.storageGiB
      },
      {
        component_type: DataflowComponentType.COMPONENT_TYPE_TIKV,
        node_profile_name: sizes?.tikv?.profileName,
        replicas: sizes?.tikv?.replicas,
        storage_size_gi: sizes?.tikv?.storageGiB,
        storage_type: sizes?.tikv?.storageType,
        raft_iops: sizes?.tikv?.raftStoreIops,
        data_store_iops: sizes?.tikv?.storageType === 'Plus' ? sizes.tikv.dataStoreIops : undefined
      }
    ]
    if (sizes?.tiflash?.replicas) {
      components.push({
        component_type: DataflowComponentType.COMPONENT_TYPE_TIFLASH,
        node_profile_name: sizes?.tiflash?.profileName,
        replicas: sizes?.tiflash?.replicas,
        storage_size_gi: sizes?.tiflash?.storageGiB,
        storage_type: sizes?.tiflash?.storageType
      })
    }
    if (sizes?.tidb?.tiproxySetting?.nodeCount) {
      components.push({
        component_type: DataflowComponentType.COMPONENT_TYPE_TIPROXY,
        node_profile_name: sizes?.tidb?.tiproxySetting?.nodeSpecKey!,
        replicas: sizes?.tidb?.tiproxySetting?.nodeCount,
        storage_size_gi: 0
      })
    }

    await restore.mutateAsync({
      orgId,
      projectId,
      clusterId: cluster.id,
      id: isPitr ? '0' : data.id,
      data: {
        restore_mode: data.restore_mode,
        restored_ts: isPitr ? Math.floor(data.timePoint.valueOf() / 1000) + '' : undefined,
        cluster_name: data.dedicated.name,
        port: data.dedicated.port,
        region: data.dedicated.region,
        components
      }
    })

    notifier.success(`Successfully initiated restore to cluster ${data.dedicated.name}`)

    // back to My TiDB list
    history.push(`/tidbs`)
  })

  const onCancel = useMemoizedFn(() => {
    history.push(`/console/clusters/${cluster.id}/backup`)
  })

  return (
    <FormProvider {...methods}>
      <RestoreLayout
        title="Restore"
        rightSection={
          <RestoreForDedicatedSummary
            formId={formId}
            cluster={cluster}
            backupId={backupId}
            snapShotOptions={snapShotOptions}
            restoreMode={restoreMode}
            restoreModeLabel={restoreModeLabel}
            timePoint={timePoint}
            timezone={tenantData.timezone}
            cmekId={cmekId}
            provider={provider}
            region={region}
            restoreRegionLabel={restoreRegionLabel}
            name={name}
            port={port}
            projectCIDR={projectCIDR}
            cidrConfigured={projectCidrConfigured}
            sizes={sizes}
            isLoading={isLoading}
            nodeGroupEnabled={nodeGroupEnabled}
            profileTargets={profileTargets}
            specVersionEnabled={specVersionEnabled}
            costs={costs}
            totalCost={totalCost}
            costError={costError}
            restore={restore}
            onCancel={onCancel}
            disableSubmit={disableSubmit}
            submitLoading={isLoading || methods.formState.isSubmitting || isCalculating || isSettingProjectCidr}
            isCalculating={isCalculating}
          />
        }
      >
        <Paper
          component="form"
          id={formId}
          onSubmit={handleSubmit(onSubmit)}
          withBorder
          shadow="none"
          p="xl"
          data-testid="restore-dedicated"
        >
          <Stack>
            <HeadLabel label="Restore Method" description="Choose a backup source and restore mode." />

            <Stack>
              <Group>
                <Typography variant="label-lg" w={90}>
                  From Cluster
                </Typography>
                <Typography variant="body-lg">{cluster.displayName}</Typography>
              </Group>

              <FormRadioGroup
                label="Restore Mode"
                name="restore_mode"
                data={restoreModeOptions}
                onChange={handleRestoreModeChange}
              />

              {restoreMode === DataflowRestoreMode.RESTORE_MODE_SNAPSHOT && (
                <FormSelect
                  name="id"
                  label="From Snapshot"
                  data={snapShotOptions}
                  searchable
                  placeholder="Select a snapshot backup"
                  rules={{ required: 'Required' }}
                  renderOption={renderSnapshotOption}
                />
              )}

              {restoreMode === DataflowRestoreMode.RESTORE_MODE_PITR && (
                <>
                  <div>
                    <Input.Label mb={5}>From Time Point</Input.Label>
                    <Group align="flex-start">
                      <Box sx={{ flexGrow: 1 }}>
                        <DateTimePicker
                          {...dateTimePickerProps}
                          disable={disablePitrSelect}
                          loading={isLoadingTimeRange}
                        />
                      </Box>
                      <Button
                        variant="default"
                        leftSection={<IconRefreshCw02 size={14} />}
                        disabled={disablePitrSelect}
                        loading={isLoadingTimeRange}
                        onClick={handleRefreshLatestRestorePoint}
                        type="button"
                      >
                        Refresh latest restore point
                      </Button>
                    </Group>
                  </div>
                </>
              )}

              {awsEnableCmek && <CMEKActiveAlert />}
            </Stack>
            <Divider mx={-24} />

            <HeadLabel label="Cloud Provider & Region" description="Select the region of the new cluster." />
            <Group>
              <FormSelect
                name="dedicated.provider"
                label="Cloud Provider"
                disabled
                data={[{ label: CloudProviderLabelConst[cluster.provider], value: cluster.provider }]}
              />
              <FormSelect
                name="dedicated.region"
                label="Region"
                data={regionOptions}
                sx={{ flexGrow: 1 }}
                disabled={awsEnableCmek || regionOptions.length <= 1}
                onChange={handleRestoreRegionChange}
              />
            </Group>

            {awsEnableCmek && (
              <Alert color="yellow" mt="sm">
                For projects with Customer-Managed Encryption Key (CMEK) enabled, the restored cluster must be in the
                same region as the source cluster.
              </Alert>
            )}
            <Divider mx={-24} />

            <HeadLabel label="Cluster Name" description="Enter the name of the new cluster." />
            <FormTextInput name="dedicated.name" placeholder="Enter your new cluster's name" />
            <Divider mx={-24} />

            <HeadLabel
              label="Cluster Size"
              description="The restore cluster size defaults to your source cluster's current configuration. You can adjust the node counts and instance sizes as needed."
            />
            <ClusterNodeSizes
              nodeProfiles={nodeProfiles}
              nodeGroupEnabled={nodeGroupEnabled}
              provider={cluster.provider}
              ignoreDefaultProfiles
              restoreTips
            />
            <Divider mx={-24} />

            <HeadLabel label="Custom Port Number" description="Adjust the TiDB port for connection. Default is 4000." />
            <FormNumberInput name="dedicated.port" min={tidbPortRules.min.value} max={tidbPortRules.max.value} />
            <Divider mx={-24} />

            <ProjectCidrSection
              orgId={orgId}
              projectId={projectId}
              provider={provider}
              region={region}
              layout="stack"
              configuredAction="restore"
              testId="restore-project-cidr-section"
              onConfiguredChange={setProjectCidrConfiguredStatus}
            />
          </Stack>
        </Paper>
      </RestoreLayout>
    </FormProvider>
  )
}

export const RestoreForDedicated = observer(_RestoreForDedicated)
