import { Alert, Box, List, Loader, Stack, TextInput, Typography, notifier } from '@tidbcloud/uikit'
import { FormTextInput } from '@tidbcloud/uikit/biz'
import { useDebouncedValue } from '@tidbcloud/uikit/hooks'
import { observer } from 'mobx-react'
import { useCallback, useEffect, useMemo } from 'react'
import { useFormContext } from 'react-hook-form'

import { CloudProviderConst, useGetCIDRValidation } from 'dbaas/features/Project'
import { HeadLabel } from 'dbaas/screens/ClustersV2/new/components'
import { useCreationStore } from 'dbaas/screens/ClustersV2/new/store'
import { FormItem } from 'dbaas/screens/NextGen/my-tidb-creation/shared/FormItem'
import { useGetDefaultCidr, useListCidrs } from 'dbaas/services/api-clients/legacy/central'
import { ListCidrsProvider } from 'dbaas/services/api-clients/legacy/central/interface'
import { getErrorMessage, pickErrorResponse } from 'dbaas/services/errorCodes'
import useStores from 'dbaas/stores/useStores'
import { cidrRegEx } from 'dbaas/utils/regex'

import { DedicatedCreationFormValue } from '../types'

interface ProjectCidrSectionProps {
  orgId?: string
  projectId?: string
  provider: string
  region: string
  layout?: 'form-item' | 'stack'
  configuredAction?: 'create' | 'restore'
  testId?: string
  onConfiguredChange?: (configured: boolean | undefined) => void
}

const ProjectCidrSectionBase = ({
  orgId: orgIdProp,
  projectId: projectIdProp,
  provider,
  region,
  layout = 'form-item',
  configuredAction = 'create',
  testId,
  onConfiguredChange
}: ProjectCidrSectionProps) => {
  const {
    store: {
      lastVisitLocation: { orgId: currentOrgId, projectId: currentProjectId }
    }
  } = useStores()

  const { dedicated: { setCidrConfigured } = {} } = useCreationStore()
  const { watch, setValue, setError, clearErrors, formState } = useFormContext<DedicatedCreationFormValue>()
  const { errors } = formState

  const upperProvider = provider.toUpperCase() as ListCidrsProvider
  const orgId = orgIdProp ?? currentOrgId
  const projectId = projectIdProp ?? currentProjectId
  const updateConfigured = useCallback(
    (configuredStatus: boolean | undefined) => {
      setCidrConfigured?.(configuredStatus)
      onConfiguredChange?.(configuredStatus)
    },
    [onConfiguredChange, setCidrConfigured]
  )

  const latestCIDRQuery = useListCidrs(
    orgId,
    projectId,
    { provider: upperProvider },
    {
      query: {
        enabled: !!(orgId && projectId && provider && region),
        refetchOnWindowFocus: false,
        onError: (err) => {
          updateConfigured(false)
          const errMsg = getErrorMessage(err, true)
          notifier.error(<div dangerouslySetInnerHTML={{ __html: errMsg }} />)
        }
      }
    }
  )

  const latestCIDRVal = useMemo(
    () => latestCIDRQuery.data?.items?.find((item) => item.region?.name === region)?.cidr,
    [latestCIDRQuery.data?.items, region]
  )
  const configured = !!latestCIDRVal

  useEffect(() => {
    updateConfigured(undefined)
  }, [provider, region, updateConfigured])

  useEffect(() => {
    if (latestCIDRQuery.isSuccess) {
      updateConfigured(!!latestCIDRVal)
    }
  }, [latestCIDRQuery.isSuccess, latestCIDRVal, provider, region, updateConfigured])

  useEffect(() => {
    if (configured) {
      setValue('dedicated.projectCIDR', (latestCIDRVal ?? '').trim())
      clearErrors('dedicated.projectCIDR')
    }
  }, [clearErrors, configured, latestCIDRVal, setValue])

  const defaultCidrQuery = useGetDefaultCidr(
    orgId,
    projectId,
    { provider: upperProvider, region },
    {
      query: {
        enabled: !configured && !!(orgId && projectId && provider && region),
        refetchOnWindowFocus: false,
        onSuccess: (data) => {
          const isDirty = !!formState.dirtyFields?.dedicated?.projectCIDR
          if (!isDirty || !!errors?.dedicated?.projectCIDR) {
            setValue('dedicated.projectCIDR', data?.cidr)
            clearErrors('dedicated.projectCIDR')
          }
        },
        onError: (err) => {
          const errMsg = getErrorMessage(err, true)
          notifier.error(<div dangerouslySetInnerHTML={{ __html: errMsg }} />)
        }
      }
    }
  )

  const { projectCIDR: cidrVal } = watch('dedicated')
  const [debouncedCIDR] = useDebouncedValue(cidrVal, 300)

  const {
    query: { data: { max_node_ip: maxIPNum, overlaps } = {} }
  } = useGetCIDRValidation(
    {
      orgId,
      projectId,
      provider: upperProvider,
      region,
      cidr: debouncedCIDR,
      disabled: configured || !cidrRegEx.test(debouncedCIDR)
    },
    {
      query: {
        refetchOnWindowFocus: false,
        retry: false,
        onSuccess: () => clearErrors('dedicated.projectCIDR'),
        onError: (err) => {
          const { message = 'Please enter a valid CIDR.' } = pickErrorResponse(err)
          if (!configured) {
            setError('dedicated.projectCIDR', { message })
          }
        }
      }
    }
  )

  const overlapped = !!overlaps?.length
  const loading = latestCIDRQuery.isLoading || defaultCidrQuery.isLoading

  const content = (
    <>
      {configured ? (
        <Box>
          <TextInput disabled value={latestCIDRVal} />
          <Alert mt="md">
            CIDR has been created for this region and it can not be updated after the first cluster is created in this
            region. To use a different CIDR, you have the option to {configuredAction} the cluster within alternative
            projects.
          </Alert>
        </Box>
      ) : (
        <Box>
          <Box>
            <FormTextInput name="dedicated.projectCIDR" rightSection={loading ? <Loader size="xs" /> : undefined} />
            {maxIPNum && (
              <Typography variant="body-sm" color="carbon.7" mt={8}>
                This CIDR setting can support up to {maxIPNum} nodes in the region.
              </Typography>
            )}
          </Box>

          {overlapped && (
            <Alert color="yellow" mt="md">
              <Typography variant="body-lg" color="inherit">
                The CIDR overlaps with the existing CIDR settings.
                <List c="inherit" fz="inherit" my={8}>
                  {overlaps
                    .flatMap((o) => o.regions?.map((r) => ({ ...r, project_id: o.project_id })))
                    .map((el, i) => (
                      <List.Item key={i}>
                        <Typography span variant="label-lg" color="inherit">
                          {el?.display_name || el?.name}
                        </Typography>{' '}
                        region in{' '}
                        {el?.project_id === projectId ? (
                          'current project'
                        ) : (
                          <>
                            project ID{' '}
                            <Typography span variant="label-lg" color="inherit">
                              {el?.project_id}
                            </Typography>
                          </>
                        )}
                      </List.Item>
                    ))}
                </List>
                This overlap will block you from establishing VPC peering between these regions.
              </Typography>
            </Alert>
          )}

          <Typography variant="body-lg" mt="md">
            {provider === CloudProviderConst.AWS && 'We recommend configuring an IP range size between /16 and /23.'}
            {provider === CloudProviderConst.GCP && 'We recommend configuring an IP range size between /16 and /20.'}
            {provider === CloudProviderConst.AZURE &&
              'We recommend configuring an IP range size between /16 and /20.'}{' '}
          </Typography>

          <Typography variant="body-lg" mt="sm">
            The following network addresses are supported:
          </Typography>

          <List
            size="sm"
            c="carbon.8"
            bg="carbon.2"
            fz={13}
            p={8}
            mt="sm"
            sx={(theme) => ({
              border: `1px solid ${theme.colors.carbon[4]}`,
              borderRadius: theme.defaultRadius
            })}
          >
            {provider === CloudProviderConst.AWS && (
              <>
                <List.Item>10.250.0.0 - 10.251.255.255</List.Item>
                <List.Item>172.16.0.0 - 172.31.255.255</List.Item>
                <List.Item>192.168.0.0 - 192.168.255.255</List.Item>
              </>
            )}
            {provider === CloudProviderConst.GCP && (
              <>
                <List.Item>10.250.0.0 - 10.251.255.255</List.Item>
                <List.Item>172.16.0.0 - 172.17.255.255</List.Item>
                <List.Item>172.30.0.0 - 172.31.255.255</List.Item>
              </>
            )}
            {provider === CloudProviderConst.AZURE && (
              <>
                <List.Item>10.250.0.0 - 10.251.255.255</List.Item>
                <List.Item>172.16.0.0 - 172.20.255.255</List.Item>
              </>
            )}
          </List>

          <Alert mt="md">
            The project CIDR can not be modified after the cluster is created. Please ensure it does not overlap with
            your application VPC CIDR.{' '}
            <a
              href="https://docs.pingcap.com/tidbcloud/set-up-vpc-peering-connections"
              target="_blank"
              rel="noreferrer"
            >
              Learn more
            </a>
          </Alert>
        </Box>
      )}
    </>
  )

  if (layout === 'stack') {
    return (
      <Stack data-testid={testId}>
        <HeadLabel
          label="Project CIDR"
          description="A CIDR to be used as the network address for peering with your application VPC."
        />
        {content}
      </Stack>
    )
  }

  return (
    <FormItem
      label="Project CIDR"
      description="A CIDR to be used as the network address for peering with your application VPC."
    >
      {content}
    </FormItem>
  )
}

export const ProjectCidrSection = observer(ProjectCidrSectionBase)
