import {
  Box,
  Button,
  Divider,
  Group,
  Loader,
  notifier,
  Paper,
  Stack,
  TextInput,
  Tooltip,
  Typography
} from '@tidbcloud/uikit'
import {
  DateTimePicker,
  FormErrorMessage,
  FormRadioGroup,
  FormSelect,
  FormSwitch,
  FormTextInput
} from '@tidbcloud/uikit/biz'
import { useMemoizedFn } from 'ahooks'
import { observer } from 'mobx-react'
import { useEffect, useId, useMemo, useState } from 'react'
import { Controller, FieldErrors, FormProvider, useForm } from 'react-hook-form'
import { useHistory, useLocation } from 'react-router-dom'

import { AccessGuard } from 'dbaas/contexts/access-control'
import { SharedCluster } from 'dbaas/features/ClusterManagement/cluster'
import { useOrgProjectClusterIds } from 'dbaas/features/Common'
import { CloudProviderLabelConst } from 'dbaas/features/Project'
import { HADescriptions } from 'dbaas/screens/ClustersV2/new/Serverless/FormContent/AdvancedSettings'
import { useAddCreditCard } from 'dbaas/screens/ClustersV2/new/Serverless/helpers'
import { EssentialCost } from 'dbaas/screens/ClusterV2/features/Capacity/SumCard'
import { filterEssentialRegionsByServicePlan } from 'dbaas/screens/NextGen/my-tidb-creation/plans/essential/essential-regions'
import { RestoreLayout } from 'dbaas/screens/Restore/RestoreLayout'
import { RestoreSummaryCard, RestoreSummaryItem, RestoreSummarySection } from 'dbaas/screens/Restore/RestoreSummary'
import { PaymentMethodModal } from 'dbaas/screens/Stripe'
import { ClusterPlan } from 'dbaas/screens/TiDBNextGen/components/ClusterPlan'
import { Encryption as EssentialEncryption } from 'dbaas/screens/TiDBNextGen/components/Encryption'
import { PlanDisplayNameMap } from 'dbaas/screens/TiDBNextGen/components/helper'
import { HighAvailability as EssentialHighAvailability } from 'dbaas/screens/TiDBNextGen/components/HighAvailability'
import { EssentialFormValue } from 'dbaas/screens/TiDBNextGen/essential'
import { Capacity as EssentialCapacity } from 'dbaas/screens/TiDBNextGen/essential/Capacity'
import { StarterFormValue } from 'dbaas/screens/TiDBNextGen/starter'
import { Capacity as StarterCapacity } from 'dbaas/screens/TiDBNextGen/starter/Capacity'
import { SpendLimitInfo as StarterSpendingLimitInfo } from 'dbaas/screens/TiDBNextGen/starter/SpendLimitInfo'
import { useGetExpWhiteList } from 'dbaas/services/api'
import {
  useBackupRestoreServiceListBackups,
  useBackupRestoreServiceListRecycledInstanceBackups,
  useBackupRestoreServiceRestore,
  useGetBackupRetention
} from 'dbaas/services/api-clients/legacy/serverless'
import {
  BackupRestoreServiceRestoreBody,
  RestoreTypeEnum as ServerlessRestoreTypeEnum
} from 'dbaas/services/api-clients/legacy/serverless/interface'
import { useRegionServiceListRegions } from 'dbaas/services/api-clients/v2023/cluster-management/v1beta2'
import { V1beta2ListRegionsRequestViewMode } from 'dbaas/services/api-clients/v2023/cluster-management/v1beta2/interface'
import {
  Commonv1beta1ServicePlan,
  Serverlessv1beta1ClusterHighAvailabilityType
} from 'dbaas/services/api-clients/v2023/serverless/v1beta1/interface'
import { getErrorMessage } from 'dbaas/services/errorCodes'
import { ExpFeatHashmap } from 'dbaas/stores/localStorage'
import useStores from 'dbaas/stores/useStores'
import { DEFAULT_TIME_FORMAT_WITH_TIMEZONE, formatTime, toCents, toDollars } from 'dbaas/utils/format'
import { clusterNameRules } from 'dbaas/utils/validationRules'

import { FormSection } from '../FormSection'

import { getPitrTimeRange } from './getPitrTimeRange'

interface FormData extends StarterFormValue, EssentialFormValue {
  mode: 'pitr' | 'snapshot'
  snapshot_id: string
  pitr_date: Date
}

const StarterRestoreAdvancedSettingsSection = () => {
  return (
    <Box sx={{ '& .mantine-FormItem-root': { padding: 0 } }} p="xl">
      <Stack gap="xl">
        <Group justify="space-between" wrap="nowrap" align="flex-start">
          <Stack gap={4}>
            <Typography variant="title-lg">Advanced Settings</Typography>
            <Typography variant="body-lg" c="carbon.7">
              Advanced settings can only be used for clusters with a monthly spending limit.
            </Typography>
          </Stack>
        </Group>

        <Group justify="space-between" wrap="nowrap" align="flex-start">
          <Box>
            <Typography variant="label-lg" fw={600}>
              Dual-Layer Data Encryption
            </Typography>
            <Typography variant="body-lg" c="carbon.7">
              Add a second layer of encryption on top of the default encryption at rest with an isolated service-managed
              key.
            </Typography>
          </Box>
          <Tooltip
            label="This option is inherited from the source cluster, and is not allowed to change."
            multiline
            withinPortal
          >
            <div>
              <FormSwitch name="encrypted" disabled />
            </div>
          </Tooltip>
        </Group>
      </Stack>
    </Box>
  )
}

interface ServerlessRestoreSummaryProps {
  formId: string
  sharedCluster: SharedCluster
  destinationMode: ServerlessRestoreTypeEnum
  provider: string
  providerDisplayName: string
  regionDisplayName: string
  mode: 'pitr' | 'snapshot'
  snapshotId: string
  snapshotOptions: Array<{ label: string; value: string }>
  pitrDate?: Date
  servicePlan: Commonv1beta1ServicePlan
  clusterName: string
  minRcu?: number
  maxRcu?: number
  monthlySpendingLimit: number
  highAvailabilityTypeVal?: Serverlessv1beta1ClusterHighAvailabilityType
  errors: FieldErrors<FormData>
  restore: ReturnType<typeof useBackupRestoreServiceRestore>
  onCancel: () => void
  disableSubmit: boolean
  isLoading: boolean
  creditAddVisible: boolean
  creditModalVisible: boolean
  creditModalHandlers: { open: () => void; close: () => void }
}

const ServerlessRestoreSummary = ({
  formId,
  sharedCluster,
  destinationMode,
  provider,
  providerDisplayName,
  regionDisplayName,
  mode,
  snapshotId,
  snapshotOptions,
  pitrDate,
  servicePlan,
  clusterName,
  minRcu,
  maxRcu,
  monthlySpendingLimit,
  highAvailabilityTypeVal,
  errors,
  restore,
  onCancel,
  disableSubmit,
  isLoading,
  creditAddVisible,
  creditModalVisible,
  creditModalHandlers
}: ServerlessRestoreSummaryProps) => {
  const {
    store: { tenantData }
  } = useStores()

  return (
    <RestoreSummaryCard
      description={
        destinationMode === ServerlessRestoreTypeEnum.NEW
          ? 'Your new instance will be:'
          : 'You are restoring to the current cluster:'
      }
      footer={
        <>
          {servicePlan === Commonv1beta1ServicePlan.Essential && (
            <EssentialCost
              cloudProvider={provider}
              regionName={sharedCluster.serverless?.region.name!}
              minRcu={minRcu}
              maxRcu={maxRcu}
            />
          )}

          {servicePlan === Commonv1beta1ServicePlan.Starter && (
            <StarterSpendingLimitInfo value={monthlySpendingLimit} />
          )}

          <Stack gap="md">
            <FormErrorMessage message={getErrorMessage(restore.error, true)} />

            <Group justify="flex-end">
              <Button variant="default" onClick={onCancel} data-mp-event="Serverless Restore Cancel Clicked">
                Cancel
              </Button>

              {creditAddVisible ? (
                <AccessGuard
                  id="BillingPaymentCreation"
                  fallback={
                    <Tooltip
                      withinPortal
                      multiline
                      sx={{ flexGrow: 1, maxWidth: 320 }}
                      label="Please contact the Owner or Billing Manager to add a credit card and your discount will be applied to your next billing cycle."
                    >
                      <Box>
                        <Button disabled data-mp-event="Serverless Restore Add Credit Card Clicked">
                          Add Credit Card
                        </Button>
                      </Box>
                    </Tooltip>
                  }
                >
                  <Button
                    disabled={isLoading}
                    onClick={creditModalHandlers.open}
                    sx={{ flexGrow: 1 }}
                    data-mp-event="Serverless Restore Add Credit Card Button Clicked"
                  >
                    Add Credit Card
                  </Button>
                </AccessGuard>
              ) : (
                <Button
                  type="submit"
                  form={formId}
                  disabled={disableSubmit}
                  loading={isLoading}
                  data-mp-event="Serverless Restore Submit Clicked"
                  sx={{ flex: 1 }}
                >
                  Restore
                </Button>
              )}

              {creditAddVisible && (
                <PaymentMethodModal
                  open={creditModalVisible}
                  onClose={creditModalHandlers.close}
                  onComplete={() => {
                    creditModalHandlers.close()
                    notifier.success('Your credit card was added successfully!')
                  }}
                />
              )}
            </Group>
          </Stack>
        </>
      }
    >
      <RestoreSummarySection title="Select Backup">
        <RestoreSummaryItem
          label={`${PlanDisplayNameMap[sharedCluster.servicePlan as Commonv1beta1ServicePlan]} Instance`}
          value={sharedCluster.displayName}
        />
        <RestoreSummaryItem
          label="Restore Mode"
          value={mode === 'snapshot' ? 'Snapshot Restore' : 'Point-in-Time Restore'}
        />
        {mode === 'snapshot' && (
          <RestoreSummaryItem
            label="Backup Snapshot"
            value={snapshotOptions.find((item) => item.value === snapshotId)?.label ?? '-'}
          />
        )}
        {mode === 'pitr' && (
          <RestoreSummaryItem
            label="Restore Time"
            value={
              pitrDate
                ? formatTime(pitrDate, {
                    offset: tenantData.timezone,
                    showSeconds: true,
                    showTimezone: true
                  })
                : '-'
            }
          />
        )}
      </RestoreSummarySection>

      <RestoreSummarySection title={`Restore to a New ${PlanDisplayNameMap[servicePlan]} Instance`}>
        <RestoreSummaryItem label="Name" value={clusterName || '-'} isError={!!errors?.name} />
        <RestoreSummaryItem label="Provider" value={providerDisplayName} />
        <RestoreSummaryItem label="Region" value={regionDisplayName} />

        {servicePlan === Commonv1beta1ServicePlan.Starter && monthlySpendingLimit > 0 && (
          <RestoreSummaryItem label="Spending Limit" value={`$${(monthlySpendingLimit || 0).toFixed(2)} / month`} />
        )}

        {highAvailabilityTypeVal && (
          <RestoreSummaryItem label="High Availability" value={HADescriptions[highAvailabilityTypeVal]} />
        )}
      </RestoreSummarySection>
    </RestoreSummaryCard>
  )
}

export const RestoreForServerlessNextGen = observer(({ cluster: sharedCluster }: { cluster: SharedCluster }) => {
  const {
    store: { tenantData }
  } = useStores()
  const urlParams = new URLSearchParams(window.location.search)
  const history = useHistory()
  const location = useLocation()
  const formId = useId()
  // we now support only restore to a new cluster
  const destinationMode: ServerlessRestoreTypeEnum = ServerlessRestoreTypeEnum.NEW
  const provider = sharedCluster.serverless?.region.cloudProvider
  const providerDisplayName = CloudProviderLabelConst[provider!]
  const region = sharedCluster.serverless?.region.name
  const [regionDisplayName, setRegionDisplayName] = useState(sharedCluster.serverless?.region.displayName ?? '')
  const backupId = urlParams.get('backup_id')
  const backupPage = urlParams.get('backup_page')
  const backupProjectId = urlParams.get('backup_project_id') ?? ''
  const fromRecycleBin = !!backupId && !!backupPage && !!backupProjectId

  const methods = useForm<FormData>({
    mode: 'onChange',
    defaultValues: {
      mode: 'snapshot',
      snapshot_id: urlParams.get('backup_id') ?? '',
      pitr_date: undefined,
      servicePlan: sharedCluster?.servicePlan as Commonv1beta1ServicePlan,
      provider: provider,
      region: region,
      name: '',
      spendingLimit: toDollars(sharedCluster.serverless?.spendingLimit?.monthly ?? 0),
      encrypted: sharedCluster.serverless?.encryptionConfig?.enhancedEncryptionEnabled ?? false,
      autoScaling: sharedCluster?.autoScaling,
      highAvailabilityType: sharedCluster?.serverless?.highAvailabilityType
    }
  })

  const {
    setValue,
    watch,
    handleSubmit,
    formState: { errors }
  } = methods
  const mode = watch('mode')
  const pitrDate = watch('pitr_date')
  const snapshotId = watch('snapshot_id')
  const monthlySpendingLimit = watch('spendingLimit')
  const clusterName = watch('name')
  const servicePlan = watch('servicePlan')
  const minRcu = watch('autoScaling.minRcu')
  const maxRcu = watch('autoScaling.maxRcu')
  const highAvailabilityType = watch('highAvailabilityType')

  const { creditAddVisible, creditModalVisible, creditModalHandlers } = useAddCreditCard(monthlySpendingLimit)
  const { orgId, projectId, clusterId } = useOrgProjectClusterIds()

  const modeOptions = [
    { label: 'Snapshot Restore', value: 'snapshot' },
    {
      label: 'Point-in-Time Restore',
      value: 'pitr',
      disabled: sharedCluster?.computed.isStarter || fromRecycleBin
    }
  ]

  const highAvailabilityTypeVal = (() => {
    if (servicePlan === Commonv1beta1ServicePlan.Starter) {
      return monthlySpendingLimit > 0 ? sharedCluster?.serverless?.highAvailabilityType : undefined
    }

    return highAvailabilityType
  })()

  const { data: retentionData, isFetching: isFetchingRetention } = useGetBackupRetention(orgId, projectId, clusterId!, {
    query: {
      enabled: mode === 'pitr',
      refetchOnWindowFocus: false,
      onSuccess(data) {
        const [, endDate] = getPitrTimeRange(sharedCluster?.createTime, data.retention_hours ?? 0)
        if (endDate) {
          setValue('pitr_date', endDate)
        }
      }
    }
  })

  const clusterCreateTime = sharedCluster?.createTime!
  const retentionHours = retentionData?.retention_hours ?? 0

  const [startDate, endDate] = useMemo(() => {
    return getPitrTimeRange(clusterCreateTime, retentionHours)
  }, [clusterCreateTime, retentionHours])

  const restore = useBackupRestoreServiceRestore()
  const { data: backupListData, isLoading: isLoadingBackupListData } = useBackupRestoreServiceListBackups(
    orgId,
    projectId,
    clusterId!,
    {
      page_token: '',
      page_size: 100,
      order_by: 'backup_time desc'
    },
    {
      query: {
        refetchOnWindowFocus: false,
        enabled: mode === 'snapshot' && !fromRecycleBin
      }
    }
  )

  // backuplist from recycle bin
  const { data: recycleBinBackupListData, isLoading: isLoadingRecycleBinBackupListData } =
    useBackupRestoreServiceListRecycledInstanceBackups(
      orgId,
      clusterId!,
      {
        page_size: 100,
        page_token: '',
        order_by: 'backup_time desc'
      },
      {
        query: {
          refetchOnWindowFocus: false,
          enabled: mode === 'snapshot' && fromRecycleBin
        }
      }
    )

  const snapshotOptions = useMemo(
    () =>
      (fromRecycleBin ? recycleBinBackupListData?.backups : backupListData?.backups)?.map((i) => ({
        label: formatTime(i.backup_time!, {
          offset: tenantData.timezone,
          showSeconds: true,
          showTimezone: true
        }),
        value: i.backup_id!
      })) ?? [],
    [fromRecycleBin, backupListData, recycleBinBackupListData, tenantData.timezone]
  )

  const disableSubmit = useMemo(() => {
    if (mode === 'pitr') {
      return !pitrDate
    } else if (mode === 'snapshot') {
      return !snapshotId
    }
    return false
  }, [mode, pitrDate, snapshotId])

  const { data: essentialVisible } = useGetExpWhiteList(orgId, undefined, {
    query: {
      enabled: !!orgId,
      refetchOnMount: false,
      refetchOnWindowFocus: false,
      select: (data) => data?.exp_names?.includes(ExpFeatHashmap.AWSEssentialEnabled)
    }
  })

  const sourceRegionId = `${provider}-${region}`
  const { data: restoreRegions } = useRegionServiceListRegions(
    {
      orgId,
      viewMode: V1beta2ListRegionsRequestViewMode.FLAT
    },
    {
      query: {
        enabled: !!orgId && fromRecycleBin,
        refetchOnMount: false,
        refetchOnWindowFocus: false,
        select: (data) => filterEssentialRegionsByServicePlan(data.flatView?.regions, 'Essential', sourceRegionId)
      }
    }
  )

  useEffect(() => {
    if (!fromRecycleBin) return
    const curRegion = restoreRegions?.[0]
    if (curRegion?.displayName) {
      setRegionDisplayName(curRegion.displayName)
    }
  }, [fromRecycleBin, restoreRegions])

  const isLoading =
    restore.isLoading ||
    isLoadingBackupListData ||
    isLoadingRecycleBinBackupListData ||
    sharedCluster?.computed.loading ||
    isFetchingRetention

  const onCancel = useMemoizedFn(() => {
    if (fromRecycleBin) {
      history.push('/tidbs/recycle-bin')
    } else {
      history.push(`/clusters/${clusterId}/backup`)
    }
  })

  const onSubmit = useMemoizedFn(async (data: FormData) => {
    const payload: BackupRestoreServiceRestoreBody = {
      backup_id: mode === 'snapshot' ? data.snapshot_id : undefined,
      point_in_time:
        mode === 'pitr'
          ? {
              cluster_id: clusterId!,
              backup_time: data.pitr_date.toISOString()
            }
          : undefined,
      cluster: {
        high_availability_type: highAvailabilityTypeVal,
        display_name: data.name,
        auto_scaling: {
          min_rcu: data.autoScaling?.minRcu,
          max_rcu: data.autoScaling?.maxRcu
        },
        spending_limit:
          typeof data.spendingLimit === 'number'
            ? {
                monthly: toCents(data.spendingLimit)
              }
            : undefined
      },
      type: destinationMode
    }

    await restore.mutateAsync({ orgId, projectId: fromRecycleBin ? backupProjectId : projectId, data: payload })

    notifier.success('Cluster is restoring...')

    if (destinationMode === ServerlessRestoreTypeEnum.NEW) {
      if (location.pathname.startsWith('/tidbs')) {
        history.push('/tidbs')
      } else {
        history.push('/clusters')
      }
    } else {
      history.push(`/clusters/${clusterId}/overview`)
    }
  })

  return (
    <FormProvider {...methods}>
      <RestoreLayout
        title="Restore"
        rightSection={
          <ServerlessRestoreSummary
            formId={formId}
            sharedCluster={sharedCluster}
            destinationMode={destinationMode}
            provider={provider!}
            providerDisplayName={providerDisplayName}
            regionDisplayName={regionDisplayName}
            mode={mode}
            snapshotId={snapshotId}
            snapshotOptions={snapshotOptions}
            pitrDate={pitrDate}
            servicePlan={servicePlan}
            clusterName={clusterName}
            minRcu={minRcu}
            maxRcu={maxRcu}
            monthlySpendingLimit={monthlySpendingLimit}
            highAvailabilityTypeVal={highAvailabilityTypeVal}
            errors={errors}
            restore={restore}
            onCancel={onCancel}
            disableSubmit={disableSubmit}
            isLoading={isLoading}
            creditAddVisible={creditAddVisible}
            creditModalVisible={creditModalVisible}
            creditModalHandlers={creditModalHandlers}
          />
        }
      >
        <Paper
          component="form"
          id={formId}
          onSubmit={handleSubmit(onSubmit)}
          withBorder
          shadow="none"
          data-testid="restore-serverless"
        >
          <FormSection
            label="Select Backup"
            description="Confirm the backup data you want to restore. Point-in-Time restore is currently in beta and only available for Essential plan."
          >
            <Stack gap="md">
              <TextInput
                label={`Your ${PlanDisplayNameMap[sharedCluster.servicePlan as Commonv1beta1ServicePlan]} Instance`}
                value={sharedCluster.displayName || sharedCluster.name}
                disabled
              />

              <FormRadioGroup name="mode" label="Restore Mode" data={modeOptions} />

              {mode === 'snapshot' && (
                <FormSelect
                  data={snapshotOptions}
                  name="snapshot_id"
                  placeholder="Select backup snapshot"
                  rules={{ required: 'Required' }}
                  label="Backup Snapshot"
                  rightSection={isLoadingBackupListData ? <Loader size="xs" /> : null}
                  disabled={isLoadingBackupListData}
                />
              )}

              {mode === 'pitr' && (
                <Controller
                  name="pitr_date"
                  control={methods.control}
                  rules={{ required: 'Required' }}
                  render={({ field }) => (
                    <Stack gap={8}>
                      <Typography variant="label-lg" lh="20px">
                        Restore Time
                      </Typography>

                      <DateTimePicker
                        withinPortal
                        format={DEFAULT_TIME_FORMAT_WITH_TIMEZONE}
                        value={field.value}
                        onChange={field.onChange}
                        startDate={startDate}
                        endDate={endDate}
                        loading={isFetchingRetention}
                        disable={isFetchingRetention || (!startDate && !endDate)}
                      />
                    </Stack>
                  )}
                />
              )}
            </Stack>
          </FormSection>

          {destinationMode === ServerlessRestoreTypeEnum.NEW && (
            <>
              {essentialVisible && !fromRecycleBin && (
                <>
                  <Divider />
                  <Controller
                    name="servicePlan"
                    render={({ field }) => (
                      <ClusterPlan
                        value={field.value}
                        onChange={(val) => {
                          field.onChange(val)
                          if (val === Commonv1beta1ServicePlan.Starter) {
                            setValue('autoScaling.minRcu', 0)
                            setValue('autoScaling.maxRcu', 0)
                          } else if (val === Commonv1beta1ServicePlan.Essential) {
                            setValue('autoScaling.minRcu', 2000)
                            setValue('autoScaling.maxRcu', 4000)
                          }
                        }}
                        planData={{
                          starter: { disabled: sharedCluster?.computed.isEssential }
                        }}
                      />
                    )}
                  />
                </>
              )}

              <Divider />
              <FormSection
                label={`Restore to a New ${PlanDisplayNameMap[servicePlan]} Instance`}
                description="Set up your new instance for restoration. The cloud provider and region are inherited from the source backup."
              >
                <Stack gap="md">
                  <FormTextInput
                    name="name"
                    label="Instance Name"
                    rules={clusterNameRules}
                    placeholder="Enter cluster name"
                  />

                  <Group align="flex-start">
                    <FormSelect
                      name="provider"
                      label="Cloud Provider"
                      data={[{ label: providerDisplayName, value: provider ?? '' }]}
                      disabled
                    />
                    <FormSelect
                      name="region"
                      label="Region"
                      data={[{ label: regionDisplayName, value: region ?? '' }]}
                      disabled
                      sx={{ flexGrow: 1 }}
                    />
                  </Group>
                </Stack>
              </FormSection>

              {servicePlan === Commonv1beta1ServicePlan.Starter && (
                <>
                  <Divider />
                  <StarterCapacity />
                  <Divider />
                  <StarterRestoreAdvancedSettingsSection />
                </>
              )}

              {servicePlan === Commonv1beta1ServicePlan.Essential && (
                <>
                  <Divider />
                  <EssentialCapacity />
                  <Divider />
                  <EssentialEncryption
                    disabled
                    tooltip="This option is inherited from the source cluster, and is not allowed to change."
                  />
                  <Divider />
                  <EssentialHighAvailability region={region} />
                </>
              )}
            </>
          )}
        </Paper>
      </RestoreLayout>
    </FormProvider>
  )
})

export default RestoreForServerlessNextGen
