import {
  ActionIcon,
  Alert,
  Box,
  Button,
  Card,
  Checkbox,
  Group,
  LoadingOverlay,
  notifier,
  Select,
  SimpleGrid,
  Stack,
  Tooltip,
  Typography
} from '@tidbcloud/uikit'
import { FormCheckbox, FormCheckboxGroup, FormNumberInput, FormSelect, FormSwitch } from '@tidbcloud/uikit/biz'
import { IconInfoCircle, IconPlus, IconTrash01 } from '@tidbcloud/uikit/icons'
import { rgba } from '@tidbcloud/uikit/utils'
import clsx from 'clsx'
import { capitalize, isEqual, omit, pick } from 'lodash-es'
import { observer } from 'mobx-react'
import { Fragment, ReactNode, useState } from 'react'
import { Controller, FormProvider, useForm } from 'react-hook-form'

import buildConfig from 'config'
import { FormErrorMessage } from 'dbaas/components/Form'
import { CloudProviderConst } from 'dbaas/features/Project'
import {
  useCreateBackupSchedule,
  useGetAutoBackupSetting,
  useGetBackupOptions,
  useGetBackupSetting,
  useModifyBackupSetting
} from 'dbaas/services/dataflow'
import {
  DataflowBackupCycle,
  DataflowBackupMode,
  DataflowBackupType,
  DataflowSnapshotBackupScheduleType,
  DataflowWeekDay,
  ModifyBackupSettingBody
} from 'dbaas/services/dataflow/interface'
import { getErrorMessage } from 'dbaas/services/errorCodes'
import useStores from 'dbaas/stores/useStores'
import { formatTime, getUTCString } from 'dbaas/utils/format'
import { difference } from 'dbaas/utils/object'

import { SettingFormValuesV2 } from './backupSettingUtils'
import { times, toLocalBackupTime, toLocalWeekdays, toUTCBackupTime, toUTCWeekdays } from './utils'

const defaultValues = {
  backup_mode: DataflowBackupMode.FULL,
  backup_time: '00:00',
  retention_period: 7,
  cluster_region: '',
  backup_region: '',
  pitr: false,
  volume_backup_enable: false,
  switch_immediately: false,
  auto_backup_enable: true,
  backup_cycle: DataflowBackupCycle.DAILY as DataflowBackupCycle,
  weekdays: Object.values(DataflowWeekDay) as DataflowWeekDay[]
}

export type SettingFormValues = typeof defaultValues

type PartialSetting = Partial<SettingFormValues>

const Settings: Array<{
  key: keyof (SettingFormValues & SettingFormValuesV2) | Array<keyof (SettingFormValues & SettingFormValuesV2)>
  title: string
  render?: (c: Partial<SettingFormValues & SettingFormValuesV2>) => ReactNode
}> = [
  {
    key: 'auto_backup_enable',
    title: 'Auto Backup',
    render(c) {
      return c?.auto_backup_enable ? 'On' : 'Off'
    }
  },
  {
    key: 'backup_time',
    title: 'Backup Time'
  },
  {
    key: 'retention_period',
    title: 'Backup Retention'
  },
  {
    key: 'pitr',
    title: 'Point-in-time Restore',
    render(c) {
      return c?.pitr ? 'On' : 'Off'
    }
  },
  {
    key: 'volume_backup_enable',
    title: 'EBS Snapshot',
    render(c) {
      return c?.volume_backup_enable ? 'On' : 'Off'
    }
  },
  {
    key: ['backup_region', 'cluster_region'],
    title: 'Backup Storage Region',
    render(c) {
      return [c?.cluster_region, c?.backup_region].filter(Boolean).join(', ')
    }
  },
  {
    key: ['backup_cycle', 'weekdays'],
    title: 'Backup Cycle',
    render(c) {
      let content = capitalize(c.backup_cycle)
      if (c.backup_cycle === DataflowBackupCycle.WEEKLY) {
        content += ` (${c.weekdays?.map(capitalize).join(', ')})`
      }
      return content
    }
  },
  {
    key: ['schedule_type', 'weekdays'],
    title: 'Schedule Type',
    render(c) {
      let content = capitalize(c.schedule_type)
      if (c.schedule_type === DataflowSnapshotBackupScheduleType.Weekly && c.weekdays?.length) {
        content += ` (${c.weekdays?.map(capitalize).join(', ')})`
      }
      return content
    }
  },
  {
    key: ['dual_region', 'selected_dual_region'],
    title: 'Dual Region',
    render(c) {
      if (!c.dual_region) {
        return 'Off'
      }
      return `On, ${c.selected_dual_region}`
    }
  }
]

export const ConfigurationCard = ({
  config,
  title,
  isNew
}: {
  title?: string
  config: Partial<typeof defaultValues>
  isNew: boolean
}) => {
  if (!config) return null
  return (
    <Card p={0} withBorder={false}>
      <Box
        component="header"
        p={16}
        sx={(theme) => ({ backgroundColor: rgba(theme.colors[isNew ? 'peacock' : 'gray'][6], 0.2) })}
      >
        <Typography fw={700}>{title}</Typography>
      </Box>
      <SimpleGrid
        cols={2}
        p={16}
        sx={(theme) => ({ backgroundColor: rgba(theme.colors[isNew ? 'peacock' : 'gray'][4], 0.2) })}
      >
        {Settings.map((i) => {
          if (Array.isArray(i.key) && i.key.every((k) => k in config)) {
            return (
              <Fragment key={i.key.join()}>
                <div>{i.title}</div>
                <b>{i.render?.(config)}</b>
              </Fragment>
            )
          }

          if (typeof i.key === 'string' && i.key in config) {
            return (
              <Fragment key={i.key}>
                <div>{i.title}</div>
                <b>{i.render ? i.render?.(config) : config[i.key]}</b>
              </Fragment>
            )
          }

          return null
        })}
      </SimpleGrid>
    </Card>
  )
}

const BackupSettingModal = ({ closeModal }: { closeModal: () => void }) => {
  const {
    store: { tenantData, activeProjectId, currentCluster }
  } = useStores()

  const methods = useForm({
    defaultValues: {
      ...defaultValues,
      cluster_region: currentCluster?.region
    }
  })
  const { control, watch, setValue, handleSubmit } = methods
  const pitr = watch('pitr')
  const volumeBackupEnable = watch('volume_backup_enable')
  const autoBackupEnable = watch('auto_backup_enable')
  const backupCycle = watch('backup_cycle')
  const createSchedule = useCreateBackupSchedule()

  // dual region 的一些选项和状态
  const { data: backupOptions, isFetching: isLoadingOptions } = useGetBackupOptions(
    tenantData.id,
    activeProjectId,
    currentCluster?.id!,
    {
      query: {
        enabled: currentCluster?.provider === CloudProviderConst.GCP, // aws not supported for now
        refetchOnWindowFocus: false,
        retry: false,
        onSuccess(data) {
          setValue('cluster_region', data.dual_region_options.cluster_region?.name)
        }
      }
    }
  )

  const availableRegions = backupOptions?.dual_region_options?.region_options?.map((i) => i.region!) ?? []
  const regionOptions = availableRegions.map((i) => ({ label: i.display_name!, value: i.name! }))
  const canUseBackupRegion = Boolean(regionOptions.length) && !volumeBackupEnable

  const { data: autoBackupSetting, isFetching: isLoadingAutoBackupSetting } = useGetAutoBackupSetting(
    tenantData.id,
    activeProjectId,
    currentCluster?.id!,
    {
      query: {
        refetchOnWindowFocus: false,
        enabled: !buildConfig.featureFlags.enableWeeklySchedule,
        onSuccess(data) {
          const { backup_time, backup_mode, retention_period } = data
          setValue('backup_mode', backup_mode)
          setValue('backup_time', toLocalBackupTime(backup_time, tenantData.timezone))
          setValue('retention_period', retention_period!)
        }
      }
    }
  )

  // dual region 和 pitr 的设置在新接口
  const { data: backupSetting, isFetching: isLoadingSetting } = useGetBackupSetting(
    tenantData.id,
    activeProjectId,
    currentCluster?.id!,
    {
      query: {
        refetchOnWindowFocus: false,
        onSuccess(data) {
          setValue('pitr', data.pitr?.enable!)
          setValue('volume_backup_enable', data.volume_backup?.enable!)
          setValue('auto_backup_enable', !data.backup_schedule?.disable)
          setValue('backup_cycle', data.backup_schedule?.backup_cycle!)
          if (data.backup_schedule?.backup_time) {
            setValue('backup_time', toLocalBackupTime(data.backup_schedule?.backup_time, tenantData.timezone))
          }
          if (!!data.backup_schedule?.weekdays?.length && data.backup_schedule?.backup_time) {
            setValue(
              'weekdays',
              toLocalWeekdays(data.backup_schedule?.weekdays, data.backup_schedule?.backup_time, tenantData.timezone)
            )
          }
          if (data.backup_schedule?.retention_period) {
            setValue('retention_period', Number(data.backup_schedule?.retention_period))
          }

          // already have dual region running
          if (data.regions.length >= 2) {
            setShowAnotherRegionPicker(true)
            const backupRegionName = data.regions.find((i) => i.name !== currentCluster?.region)?.name
            if (backupRegionName) {
              setValue('backup_region', backupRegionName)
            }
          }
        }
      }
    }
  )
  const alreadyHaveBackupRegion = (backupSetting?.regions.length ?? 0) >= 2
  const backupRegion = alreadyHaveBackupRegion ? backupSetting?.regions?.[1] : undefined
  const showEbsSwitch = backupSetting?.volume_backup?.hidden === false

  const isLoading = isLoadingOptions || isLoadingSetting || isLoadingAutoBackupSetting
  const modifySetting = useModifyBackupSetting()
  const isLoadingSubmit = modifySetting.isLoading
  const submitError = modifySetting.error

  const [showAnotherRegionPicker, setShowAnotherRegionPicker] = useState(false)
  const [regionCheck, setRegionCheck] = useState({ error: false, checked: false })
  const [showDiff, setShowDiff] = useState(false)
  const [settingDiff, setSettingDiff] = useState<[PartialSetting, PartialSetting] | null>(null)

  const onSubmit = async (data: typeof defaultValues) => {
    modifySetting.reset()

    if (!data.auto_backup_enable) {
      // turn off backup
      await modifySetting.mutateAsync({
        orgId: tenantData.id,
        projectId: activeProjectId,
        clusterId: currentCluster?.id!,
        data: {
          ...backupSetting,
          description: '',
          regions: backupSetting?.regions.map((i) => i.name!)!,
          backup_schedule: {
            ...backupSetting?.backup_schedule,
            disable: true
          }
        }
      })
      notifier.success('Backup is turned off.')
      closeModal()
      return
    }

    const clusterRegion = currentCluster?.region!
    const oldSetting: Omit<typeof defaultValues, 'switch_immediately'> = omit(
      {
        ...defaultValues,
        cluster_region: clusterRegion,
        retention_period: Number(autoBackupSetting?.max_backups ?? backupSetting?.backup_schedule?.retention_period!),
        backup_time: toLocalBackupTime(
          autoBackupSetting?.backup_time ?? backupSetting?.backup_schedule?.backup_time!,
          tenantData.timezone
        ),
        backup_region: backupSetting?.regions.find((i) => i.name !== clusterRegion)?.name ?? '',
        pitr: backupSetting?.pitr?.enable!,
        volume_backup_enable: backupSetting?.volume_backup?.enable!,
        auto_backup_enable: !backupSetting?.backup_schedule?.disable,
        backup_cycle: backupSetting?.backup_schedule?.backup_cycle!,
        weekdays: backupSetting?.backup_schedule?.weekdays!
      },
      ['switch_immediately']
    )

    const changed = !isEqual(data, oldSetting)
    if (!changed) {
      closeModal()
      return
    }
    if (!showDiff) {
      const changedKeys = Object.keys(difference(data, oldSetting))
      const newValues = pick(data, changedKeys)
      const oldValues = pick(oldSetting, changedKeys)

      if (changedKeys.includes('backup_region')) {
        newValues.cluster_region = clusterRegion
        oldValues.cluster_region = clusterRegion
      } else if (changedKeys.includes('backup_cycle')) {
        newValues.weekdays = data.weekdays
        oldValues.weekdays = oldSetting.weekdays
      } else if (changedKeys.includes('weekdays')) {
        newValues.backup_cycle = data.backup_cycle
        oldValues.backup_cycle = oldSetting.backup_cycle
      } else if (changedKeys.includes('backup_time')) {
        const timezone = getUTCString(tenantData.timezone)
        newValues.backup_time = newValues.backup_time + ' ' + timezone
        oldValues.backup_time = oldValues.backup_time + ' ' + timezone
      }

      if (changedKeys.length > 0) {
        setSettingDiff([newValues, oldValues])
        setShowDiff(true)
        return
      }
    }

    const body: ModifyBackupSettingBody = {
      regions: [data.cluster_region, data.backup_region].filter(Boolean),
      description: '',
      pitr: {
        enable: data.pitr
      },
      volume_backup: {
        enable: data.volume_backup_enable
      },
      backup_schedule: {
        disable: !data.auto_backup_enable,
        weekdays: toUTCWeekdays(data.weekdays, data.backup_time, tenantData.timezone),
        backup_cycle: data.backup_cycle,
        backup_time: toUTCBackupTime(data.backup_time, tenantData.timezone),
        retention_period: data.retention_period
      },
      switch_immediately: data.switch_immediately
    }
    await modifySetting.mutateAsync({
      orgId: tenantData.id,
      projectId: activeProjectId,
      clusterId: currentCluster?.id!,
      data: body
    })

    if (!buildConfig.featureFlags.enableWeeklySchedule) {
      createSchedule.mutateAsync({
        orgId: tenantData.id,
        projectId: activeProjectId,
        clusterId: currentCluster?.id!,
        data: {
          description: '',
          name: '',
          backup_time: toUTCBackupTime(data.backup_time, tenantData.timezone),
          backup_mode: DataflowBackupMode.FULL,
          // @ts-expect-error
          max_backups: data.retention_period,
          retention_period: data.retention_period,
          backup_type: DataflowBackupType.AUTO
        }
      })
    }

    notifier.success('Backup Settings changed.')
    closeModal()
  }

  return (
    <FormProvider {...methods}>
      <form onSubmit={handleSubmit(onSubmit)}>
        <LoadingOverlay visible={isLoading} overlayProps={{ blur: 4 }} />
        <Controller control={control} name="backup_mode" render={({ field }) => <input {...field} hidden />} />
        <Controller control={control} name="cluster_region" render={({ field }) => <input {...field} hidden />} />

        <Stack className={clsx(showDiff && 'wd-hidden')}>
          <Group h={30}>
            <Typography variant="label-lg" miw={200}>
              Auto Backup
            </Typography>
            <FormSwitch name="auto_backup_enable" />
          </Group>

          <Stack sx={{ display: autoBackupEnable ? 'flex' : 'none' }}>
            <Group wrap="nowrap" align={backupCycle === DataflowBackupCycle.WEEKLY ? 'flex-start' : undefined}>
              <Typography variant="label-lg" miw={200}>
                Backup Cycle
              </Typography>
              {buildConfig.featureFlags.enableWeeklySchedule ? (
                <Stack gap={0}>
                  <Group>
                    <FormSelect
                      name="backup_cycle"
                      size="xs"
                      data={[
                        { label: 'Daily', value: DataflowBackupCycle.DAILY },
                        { label: 'Weekly', value: DataflowBackupCycle.WEEKLY }
                      ]}
                      onChange={(val) => {
                        if (val === DataflowBackupCycle.WEEKLY) {
                          setValue('pitr', true)
                          setValue(
                            'weekdays',
                            backupSetting?.backup_schedule?.weekdays ?? Object.values(DataflowWeekDay)
                          )
                        }
                      }}
                      w={100}
                    />

                    {backupCycle === DataflowBackupCycle.WEEKLY && (
                      <Typography variant="body-sm">
                        Point-in-Time Restore will be enabled if opt-in weekly backup schedule
                      </Typography>
                    )}
                  </Group>

                  <FormCheckboxGroup
                    name="weekdays"
                    hidden={backupCycle !== DataflowBackupCycle.WEEKLY}
                    data={Object.values(DataflowWeekDay).map((i) => ({ label: capitalize(i), value: i as string }))}
                    size="xs"
                    rules={{
                      validate: (val) => {
                        return val.length === 0 ? 'Select at least one' : undefined
                      }
                    }}
                  />
                </Stack>
              ) : (
                <Typography variant="body-lg">Everyday</Typography>
              )}
            </Group>

            <Group>
              <Typography variant="label-lg" miw={200}>
                Backup Time
              </Typography>
              <FormSelect name="backup_time" data={times} size="xs" disabled={!autoBackupEnable} w={100} />
              <Typography variant="body-md">{getUTCString(tenantData.timezone)}</Typography>
            </Group>

            <Group>
              <Typography variant="label-lg" miw={200}>
                Backup Retention
              </Typography>
              <Group>
                <FormNumberInput
                  name="retention_period"
                  step={1}
                  min={1}
                  max={90}
                  placeholder="Backup Retention"
                  rules={{ required: 'Required' }}
                  size="xs"
                  disabled={!autoBackupEnable}
                  w={100}
                />
                <Typography variant="body-lg">Days</Typography>
              </Group>
            </Group>

            <Group sx={{ display: showEbsSwitch ? 'flex' : 'none' }}>
              <Group miw={200} gap={8}>
                <Typography variant="label-lg">EBS Snapshot</Typography>
                <Tooltip label="Point-in-time Restore and EBS Snapshot are mutually exclusive" withArrow multiline>
                  <IconInfoCircle size={14} />
                </Tooltip>
              </Group>

              <FormSwitch name="volume_backup_enable" disabled={pitr || alreadyHaveBackupRegion} />
            </Group>

            <Group>
              <Typography variant="label-lg" miw={200}>
                Point-in-time Restore
              </Typography>
              <FormSwitch name="pitr" disabled={volumeBackupEnable || backupCycle === DataflowBackupCycle.WEEKLY} />
            </Group>

            <Group align="flex-start">
              <Group gap={5} miw={200}>
                <Typography variant="label-lg">Backup Storage Region</Typography>
                <Tooltip
                  label="This is the cluster's current region. TiDB Cloud supports storing backup data in two regions, which facilitates data safety and faster recovery. When you restore data, you can choose either region for the new cluster"
                  withArrow
                  multiline
                  maw={400}
                >
                  <IconInfoCircle size={14} />
                </Tooltip>
              </Group>

              <Controller
                name="backup_region"
                control={control}
                render={({ field }) => (
                  <Stack>
                    <Typography variant="body-lg">{`${
                      backupOptions?.dual_region_options.cluster_region?.display_name ?? currentCluster?.region
                    }`}</Typography>

                    {showAnotherRegionPicker ? (
                      <Group>
                        {alreadyHaveBackupRegion ? (
                          <Typography variant="body-lg" mt={4}>
                            {backupRegion?.display_name}
                          </Typography>
                        ) : (
                          <Select
                            placeholder="Select another region"
                            data={regionOptions}
                            value={field.value}
                            size="xs"
                            onChange={(val) => field.onChange(val)}
                          />
                        )}

                        {!alreadyHaveBackupRegion && (
                          <Box
                            component={IconTrash01}
                            size={14}
                            color="grey"
                            onClick={() => {
                              setShowAnotherRegionPicker(false)
                            }}
                            sx={{ cursor: 'pointer' }}
                          />
                        )}
                      </Group>
                    ) : canUseBackupRegion ? (
                      <ActionIcon variant="default" onClick={() => setShowAnotherRegionPicker(true)}>
                        <Box c="carbon.7" component={IconPlus} size={14} />
                      </ActionIcon>
                    ) : null}
                  </Stack>
                )}
              />
            </Group>

            <Stack>
              {backupRegion && !alreadyHaveBackupRegion && (
                <Checkbox
                  label={`Data backup will copy data to ${backupRegion?.display_name}, please confirm that you understand the compliance of cross region, and it would incur extra data replication fees.`}
                  error={regionCheck.error}
                  onChange={(e) => {
                    setRegionCheck({
                      checked: e.target.checked!,
                      error: !e.target.checked!
                    })
                  }}
                />
              )}

              <Typography variant="body-md">
                We will charge you according to your average monthly storage and data replication usage. See{' '}
                <a
                  href="https://en.pingcap.com/tidb-cloud-pricing-details/#data-backup-cost"
                  target="_blank"
                  rel="noopener noreferrer"
                  data-mp-event="Data Backup Cost Link Clicked"
                >
                  data backup cost
                </a>{' '}
                for more information.
              </Typography>

              {backupSetting?.backup_schedule?.next_backup_time && (
                <Alert color="peacock">{`Next backup will start at ${formatTime(
                  backupSetting?.backup_schedule?.next_backup_time,
                  {
                    showSeconds: true,
                    showTimezone: true,
                    offset: tenantData.timezone
                  }
                )}`}</Alert>
              )}

              {modifySetting.error && <FormErrorMessage message={getErrorMessage(modifySetting.error)} />}
            </Stack>
          </Stack>
        </Stack>

        {!autoBackupEnable && (
          <Alert color="yellow" mt={16}>
            We highly recommend enable auto backup to protect your data. Once backed up, you can easily recover from any
            data loss.
          </Alert>
        )}

        {settingDiff && showDiff && (
          <Stack>
            <ConfigurationCard title="Old Configuration" config={settingDiff[1]} isNew={false} />
            <ConfigurationCard title="New Configuration" config={settingDiff[0]} isNew />
            {pitr && !backupSetting?.pitr?.enable && (
              <Box mt={16}>
                <FormCheckbox
                  name="switch_immediately"
                  label="Perform a backup immediately and use it as recovery starting point in PITR."
                />
              </Box>
            )}

            {submitError && <FormErrorMessage message={getErrorMessage(submitError)} />}
          </Stack>
        )}

        <Group justify="flex-end" mt={16}>
          <Button
            variant="default"
            type="button"
            data-mp-event="Dedicated Backup Setting Modal Cancel Button Clicked"
            onClick={closeModal}
          >
            Cancel
          </Button>
          <Button
            type="submit"
            data-mp-event="Dedicated Backup Setting Modal Confirm Button Clicked"
            loading={isLoadingSubmit}
          >
            Confirm
          </Button>
        </Group>
      </form>
    </FormProvider>
  )
}

export default observer(BackupSettingModal)
