import { zodResolver } from '@hookform/resolvers/zod'
import { Box, Flex } from '@tidbcloud/uikit'
import { useDisclosure } from '@tidbcloud/uikit/hooks'
import { observer } from 'mobx-react'
import { useEffect } from 'react'
import { FormProvider, useForm } from 'react-hook-form'
import * as z from 'zod'

import { TI_PROXY_AVALIABLE_NODE_COUNTS } from 'dbaas/features/ClusterManagement/dedicated'
import { CloudProviderConst } from 'dbaas/features/Project'
import { ClusterPageShell } from 'dbaas/screens/ClusterV2/components'
import { SwitchGenModal } from 'dbaas/screens/TiDBNextGen/components/SwitchGenModal'
import { Serverlessv1beta1ClusterHighAvailabilityType } from 'dbaas/services/api-clients/v2023/serverless/v1beta1/interface'
import { cidrRegEx } from 'dbaas/utils/regex'
import { clusterNameRules, tidbPortRules } from 'dbaas/utils/validationRules'

import { ESSENTIAL_RCU_MAX, ESSENTIAL_RCU_MIN, ESSENTIAL_RCU_STEP } from '../Essential/shared/capacity'
import { CreationFormValueState } from '../helpers'
import { CreationStoreProvider } from '../store'

import { CreationForm } from './CreationForm'
import { useDefaultValueFromURL } from './helpers'
import { SumCard } from './SumCard'

const createSchema = (data: CreationFormValueState) => {
  const starterSchema = z.object({
    provider: z.string({ required_error: 'Please select a cloud provider.' }),
    region: z.string({ required_error: 'Please select a region.' }),
    name: z
      .string({ required_error: clusterNameRules.required })
      .regex(clusterNameRules.pattern.value, clusterNameRules.pattern.message),
    plan: z.enum(['free', 'scalable']),
    monthlySpendingLimit:
      data.serverless.plan === 'free'
        ? z.coerce.number().optional()
        : z.coerce
            .number({
              required_error: 'The free quota of your organization is exhausted. Please set the spending limit.',
              invalid_type_error: 'Please enter an integer or a number with two decimal places.'
            })
            .min(0.01, 'The spending limit must be greater than or equal to $0.01'),
    encrypted: z.boolean(),
    highAvailabilityType: z
      .enum([Serverlessv1beta1ClusterHighAvailabilityType.REGIONAL, Serverlessv1beta1ClusterHighAvailabilityType.ZONAL])
      .optional()
  })

  const essentialSchema = z
    .object({
      provider: z.string({ required_error: 'Please select a cloud provider.' }),
      region: z.string({ required_error: 'Please select a region.' }),
      name: z
        .string({ required_error: clusterNameRules.required })
        .regex(clusterNameRules.pattern.value, clusterNameRules.pattern.message),
      minRcu: z
        .number()
        .int()
        .multipleOf(ESSENTIAL_RCU_STEP)
        .min(ESSENTIAL_RCU_MIN, 'RCUs should be greater than 2,000')
        .max(ESSENTIAL_RCU_MAX, 'RCUs should be less than 100,000'),
      maxRcu: z
        .number()
        .int()
        .multipleOf(ESSENTIAL_RCU_STEP)
        .min(ESSENTIAL_RCU_MIN, 'RCUs should be greater than 2,000')
        .max(ESSENTIAL_RCU_MAX, 'RCUs should be less than 100,000'),
      encrypted: z.boolean(),
      highAvailabilityType: z.enum([
        Serverlessv1beta1ClusterHighAvailabilityType.REGIONAL,
        Serverlessv1beta1ClusterHighAvailabilityType.ZONAL
      ])
    })
    .refine(
      (data) => {
        return data.minRcu <= data.maxRcu
      },
      {
        message: 'Min RCUs should be less than or equal to Max RCUs',
        path: ['minRcu']
      }
    )
    .refine(
      (data) => {
        return data.minRcu <= data.maxRcu
      },
      {
        message: 'Max RCUs should be greater than or equal to Min RCUs',
        path: ['maxRcu']
      }
    )

  const dedicatedSchema = z.object({
    provider: z.string({ required_error: 'Please select a cloud provider.' }),
    region: z.string({ required_error: 'Please select a region.' }),
    name: z
      .string({ required_error: clusterNameRules.required })
      .regex(clusterNameRules.pattern.value, clusterNameRules.pattern.message),
    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.'),
        storageGiB: z.number(),
        storageType: z.string().optional(),
        raftStoreIops: z.number().min(2000).max(30000).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()
      })
    }),
    projectCIDR: z.string().regex(cidrRegEx, 'Please enter a valid CIDR.').optional()
  })

  return z
    .object({
      tier: z.enum(['serverless', 'essential', 'dedicated']),
      ...(data.tier === 'serverless' ? { serverless: starterSchema } : {}),
      ...(data.tier === 'essential' ? { essential: essentialSchema } : {}),
      ...(data.tier === 'dedicated' ? { dedicated: dedicatedSchema } : {})
    })
    .passthrough()
}

export const CreationWrapper = observer(({ onGenChange }: { onGenChange?: () => void }) => {
  const { tier: defaultTier, dedicated: defaultDedicated } = useDefaultValueFromURL()

  const formProps = useForm<CreationFormValueState>({
    mode: 'onChange',
    defaultValues: {
      initializing: {
        serverless: 'false',
        essential: 'false',
        dedicated: 'false'
      },
      tier: defaultTier,
      serverless: {
        provider: '',
        region: '',
        name: '',
        plan: 'scalable',
        monthlySpendingLimit: 10,
        encrypted: false
      },
      essential: {
        provider: '',
        region: '',
        name: '',
        minRcu: ESSENTIAL_RCU_MIN,
        maxRcu: 10000,
        encrypted: true,
        highAvailabilityType: Serverlessv1beta1ClusterHighAvailabilityType.REGIONAL
      },
      dedicated: {
        provider: defaultDedicated.provider ?? '',
        region: defaultDedicated.region ?? '',
        name: '',
        port: 4000,
        specVersion: 'V2',
        sizes: {
          tidb: {
            replicas: 2,
            profileName: ''
          },
          tikv: {
            replicas: 3,
            profileName: '',
            storageGiB: 0
          },
          tiflash: {
            replicas: 0,
            profileName: '',
            storageGiB: 0
          }
        },
        projectCIDR: undefined
      }
    },
    resolver: async (data, context, options) => {
      const schema = createSchema(data)

      // 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 serverlessProvider = formProps.watch('serverless.provider')
  const essentialProvider = formProps.watch('essential.provider')

  const [modalOpened, modalHandlers] = useDisclosure(false)

  useEffect(() => {
    if (serverlessProvider === CloudProviderConst.ALICLOUD || essentialProvider === CloudProviderConst.ALICLOUD) {
      modalHandlers.open()
    }
  }, [modalHandlers, serverlessProvider, essentialProvider])

  return (
    <ClusterPageShell title="Create Cluster" headerProps={{ withBack: true }} bodyProps={{ pb: 96 }}>
      <FormProvider {...formProps}>
        <CreationStoreProvider>
          <Flex
            sx={{
              height: '100%',
              gap: 24,
              justifyContent: 'center'
            }}
          >
            <Box w={720} miw={360}>
              <CreationForm />
            </Box>
            <Box w={408} miw={320}>
              <SumCard control={formProps.control} />
            </Box>
          </Flex>
        </CreationStoreProvider>
      </FormProvider>
      <SwitchGenModal
        opened={modalOpened}
        switchingProvider={CloudProviderConst.ALICLOUD}
        onConfirm={() => {
          onGenChange?.()
          modalHandlers.close()
        }}
        onCancel={() => {
          formProps.setValue('serverless.provider', CloudProviderConst.AWS)
          formProps.setValue('essential.provider', CloudProviderConst.AWS)
          modalHandlers.close()
        }}
      />
    </ClusterPageShell>
  )
})
