import {
  AnimatedNumber,
  Collapse,
  Group,
  notifier,
  Skeleton,
  Typography,
  Card,
  Button,
  Divider,
  Stack,
  Box,
  Flex,
  Tooltip,
  StackProps,
  Alert,
  CardProps
} from '@tidbcloud/uikit'
import { useDisclosure } from '@tidbcloud/uikit/hooks'
import { IconChevronDown, IconChevronUp } from '@tidbcloud/uikit/icons'
import { useMemoizedFn } from 'ahooks'
import { observer } from 'mobx-react'
import { useMemo } from 'react'
import { useFormContext } from 'react-hook-form'

import { AccessGuard } from 'dbaas/contexts/access-control'
import { renderNodes, renderStorage } from 'dbaas/features/ClusterManagement/common'
import { renderTiproxy } from 'dbaas/features/ClusterManagement/dedicated/node/NodeSizes/TiProxy'
import { WaringForSpecInSummary } from 'dbaas/features/ClusterManagement/dedicated/node/NodeSizes/WarningForSpecInSummary'
import { CloudProviderConst, getCloudProviderLabel } from 'dbaas/features/Project'
import { renderSpecVersion, useSpecVersionEnabled } from 'dbaas/screens/Cluster/OverviewV2/helpers'
import { PaymentMethodModal } from 'dbaas/screens/Stripe'
import { Dedicatedv1beta1ComponentType } from 'dbaas/services/api-clients/v2023/dedicated/v1beta1/interface'
import { getErrorMessage } from 'dbaas/services/errorCodes'

import { HeadLabel, SumInfoItem } from '../../components'
import { CreationFormValueState, renderStorageType } from '../../helpers'
import { useCreationStore } from '../../store'
import { NodeSpecByType } from '../../store/helpers'
import {
  useAddCreditCard,
  useCreateDedicatedCluster,
  useCreationFinished,
  useFetchClusterCosts,
  useSetUpRegionalCIDR
} from '../helpers'

import { CostDetail } from './CostPanel/CostDetail'

interface SumCardProps {
  onCancel: () => void
  cardProps?: CardProps
}

export const NodeSizeSkeleton = () => (
  <Stack justify="flex-start" gap={8}>
    <Skeleton width={140} height={16} />
    <Skeleton width={80} height={16} />
  </Stack>
)

export const useNodeProfileTargets = ({
  tidbProfileName,
  tikvProfileName,
  tiflashProfileName,
  nodeProfiles
}: {
  tidbProfileName?: string
  tikvProfileName?: string
  tiflashProfileName?: string
  nodeProfiles?: NodeSpecByType[]
}) => {
  return useMemo(() => {
    const [tidb, tikv, tiflash] = [
      {
        type: Dedicatedv1beta1ComponentType.TIDB,
        profileName: tidbProfileName
      },
      {
        type: Dedicatedv1beta1ComponentType.TIKV,
        profileName: tikvProfileName
      },
      {
        type: Dedicatedv1beta1ComponentType.TIFLASH,
        profileName: tiflashProfileName
      }
    ].map(({ type, profileName }) => {
      const profiles = nodeProfiles?.find((item) => item.componentType === type)?.nodeSpecs
      const profile = profiles?.find((item) => item.name === profileName)
      const label = profile?.displayName ?? ''
      const version = profile?.version ?? ''

      return {
        profile,
        label,
        version
      }
    })

    return {
      tidb,
      tikv,
      tiflash
    }
  }, [tidbProfileName, tiflashProfileName, tikvProfileName, nodeProfiles])
}

export const NewClusterPricingSummary = ({
  error,
  totalCost,
  costsDetail,
  ...props
}: {
  error?: string
  totalCost: number
  costsDetail: ReturnType<typeof useFetchClusterCosts>['costs']
} & StackProps) => {
  const [costDetailVisible, costDetailHandlers] = useDisclosure(false)

  return (
    <Stack gap={4} {...props}>
      <Group onClick={costDetailHandlers.toggle} sx={{ '&:hover': { cursor: error ? 'auto' : 'pointer' } }}>
        {error ? (
          <Group gap="xs">
            <Typography variant="headline-md">-</Typography>
            <Typography variant="body-lg">/hour</Typography>
          </Group>
        ) : (
          <AnimatedNumber
            value={error ? 0 : totalCost}
            format={{ maximumFractionDigits: 5, minimumFractionDigits: 2 }}
            prefix="$"
            suffix="/hour"
            c="carbon.9"
            fw={700}
            fz={20}
            sx={{
              '&::part(suffix)': {
                color: 'var(--mantine-color-carbon-8)',
                fontWeight: 400,
                fontSize: 14
              }
            }}
          />
        )}
        {!error && <Box component={costDetailVisible ? IconChevronUp : IconChevronDown} size={14} />}
      </Group>
      {/* Discount info removed for Dedicated create/modify flows */}
      <Collapse in={!error && costDetailVisible}>
        <CostDetail costs={costsDetail} />
      </Collapse>

      <Typography variant="body-lg" c="carbon.7">
        You will be billed hourly and can terminate your cluster anytime. Learn more in{' '}
        <a href="https://www.pingcap.com/tidb-cloud-pricing-details/#node-cost" target="_blank" rel="noreferrer">
          Pricing Details
        </a>
        .
      </Typography>

      {error && <Alert color="red">{error}</Alert>}
    </Stack>
  )
}

export const SumCardForDedicated = observer(({ onCancel, cardProps }: SumCardProps) => {
  const { dedicated: store } = useCreationStore()
  const cidrConfigured = !!store?.cidrConfigured
  const {
    watch,
    handleSubmit,
    formState: { dirtyFields, errors }
  } = useFormContext<CreationFormValueState>()
  const formErrors = errors.dedicated
  const formDirtyFields = dirtyFields.dedicated

  const { dedicated: initializing } = watch('initializing')
  const provider = watch('dedicated.provider')
  const region = watch('dedicated.region')
  const name = watch('dedicated.name')
  const port = watch('dedicated.port')
  const sizes = watch('dedicated.sizes')
  const projectCIDR = watch('dedicated.projectCIDR')

  const { regionName, crossAccountDeploymentId } = useMemo(() => {
    const targetRegion = store?.providers.find((p) => p.name === provider)?.regions.find((r) => r.name === region)

    return {
      regionName: targetRegion?.display_name ?? '',
      crossAccountDeploymentId: targetRegion?.cross_account_deployment_id || void 0
    }
  }, [provider, region, store?.providers])

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

  // Regional CIDR
  const { setUpRegionalCIDR, isLoading: isSettingCIDR } = useSetUpRegionalCIDR(
    { configured: cidrConfigured },
    {
      onError: (errMsg) => {
        if (errMsg) {
          notifier.error(<div dangerouslySetInnerHTML={{ __html: errMsg }} />)
        }
      }
    }
  )

  const { createCluster, isCreating } = useCreateDedicatedCluster()
  const finishCreation = useCreationFinished()

  const isError = !!formErrors?.provider || !!formErrors?.region || !!formErrors?.sizes

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

  const onSubmit = useMemoizedFn(async (data: CreationFormValueState) => {
    const { provider, region, sizes, name, port, projectCIDR } = data.dedicated || {}
    const [, cidrErrMsg] = await setUpRegionalCIDR({ provider, region, cidr: projectCIDR })

    if (cidrErrMsg) {
      // error message is handled and showed in `useSetUpRegionalCIDR`'s `onError` callback
      return
    }

    const { clusterId, errorMsg } = await createCluster({
      provider,
      region,
      sizes,
      name,
      port,
      crossAccountDeploymentId
    })

    if (clusterId) {
      finishCreation(clusterId)
    } else if (errorMsg) {
      notifier.error(<div dangerouslySetInnerHTML={{ __html: errorMsg }}></div>)
    }
  })
  const onConfirm = handleSubmit(onSubmit)

  const { creditAddVisible, modalVisible, modalHandlers } = useAddCreditCard()

  // FIXME: move `initializing` to simple context store
  const loading = JSON.parse(initializing) as boolean

  const { specVersionEnabled } = useSpecVersionEnabled(provider)

  return (
    <Card shadow="none" p="xl" {...cardProps}>
      <HeadLabel label="Summary" description="Your new cluster will be:" />
      <Stack justify="flex-start" gap={8} py="xl" sx={{ position: 'relative' }}>
        <SumInfoItem label="Plan" value="Dedicated" />
        <SumInfoItem
          label="Provider"
          value={loading ? <Skeleton width={80} height={16} /> : getCloudProviderLabel(provider)}
        />
        <SumInfoItem label="Region" value={loading ? <Skeleton width={150} height={16} /> : regionName} />
        <SumInfoItem
          label="Name"
          value={loading ? <Skeleton width={100} height={16} /> : name}
          isError={!!formErrors?.name}
        />
        <SumInfoItem label="Port" value={port} isError={!!formErrors?.port} />
      </Stack>
      <Divider color="carbon.4" />
      <Stack justify="flex-start" gap={8} py="xl" sx={{ position: 'relative' }}>
        <SumInfoItem
          label="TiDB"
          value={
            loading ? (
              <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>Default Group x {renderNodes(sizes?.tidb?.replicas)}</Box>
                {renderTiproxy(sizes?.tidb.tiproxySetting)}
              </Stack>
            )
          }
          isError={!!formErrors?.sizes?.tidb}
          errors="Invalid Size"
        />
        <SumInfoItem
          label="TiKV"
          value={
            loading ? (
              <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>
            )
          }
          isError={!!formErrors?.sizes?.tikv}
          errors="Invalid Size"
        />
        {!!sizes?.tiflash?.replicas && (
          <SumInfoItem
            label="TiFlash"
            value={
              loading ? (
                <NodeSizeSkeleton />
              ) : (
                <Stack justify="flex-start" gap={8}>
                  <Group>
                    {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>
                  {provider === CloudProviderConst.AWS && (
                    <Box>{renderStorageType(sizes?.tiflash.storageType)} Storage</Box>
                  )}
                </Stack>
              )
            }
            isError={!!formErrors?.sizes?.tiflash}
            errors="Invalid Size"
          />
        )}
      </Stack>
      <Divider color="carbon.4" />
      {!cidrConfigured && (
        <>
          <Stack justify="flex-start" gap={8} py="xl" sx={{ position: 'relative' }}>
            <SumInfoItem label="CIDR" value={projectCIDR} isError={!!formErrors?.projectCIDR} />
          </Stack>
          <Divider color="carbon.4" />
        </>
      )}

      <NewClusterPricingSummary
        error={getErrorMessage(costError)}
        totalCost={totalCost ?? 0}
        costsDetail={costs}
        mt="sm"
      />

      <Flex align="center" gap="md" mt="md">
        <Button
          variant="default"
          onClick={onCancel}
          data-mp-event="Cancel Created A Cluster"
          data-mp-service-plan="Dedicated"
          data-mp-source="Clusters - Creation v2"
          data-mp-filled-fields={['provider', 'region', 'name'].filter((key) => formDirtyFields?.[key]).join(', ')}
        >
          Cancel
        </Button>
        {creditAddVisible && (
          <AccessGuard
            id="BillingPaymentCreation"
            fallback={
              <Tooltip
                sx={{ flexGrow: 1 }}
                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 loading={isCostLoading || loading} data-mp-event="mp-addcreditcard-1691461017800">
                    {isCostLoading ? 'Calculating...' : loading ? 'Fetching...' : `Add Credit Card`}
                  </Button>
                </Box>
              </Tooltip>
            }
          >
            <Button
              loading={isSettingCIDR || isCreating || isCostLoading || loading}
              onClick={modalHandlers.open}
              sx={{ flexGrow: 1 }}
              data-mp-event="Add Credit Card Button Clicked"
              data-mp-service-plan="Dedicated"
              data-mp-source="Clusters - Creation v2"
            >
              {isCostLoading ? 'Calculating...' : loading ? 'Fetching...' : `Add Credit Card`}
            </Button>
          </AccessGuard>
        )}
        {!creditAddVisible && (
          <Button
            loading={isSettingCIDR || isCreating || isCostLoading || loading}
            onClick={onConfirm}
            sx={{ flexGrow: 1 }}
            data-mp-event="Cluster Creation Button Clicked"
            data-mp-service-plan="Dedicated"
            data-mp-source="Clusters - Creation v2"
            data-testid="dedicated-cluster-creation-btn"
          >
            {isCostLoading ? 'Calculating...' : loading ? 'Fetching...' : 'Create'}
          </Button>
        )}
      </Flex>
      {creditAddVisible && (
        <PaymentMethodModal
          open={modalVisible}
          onClose={modalHandlers.close}
          onComplete={() => {
            notifier.success('Your credit card has been saved successfully.')
            modalHandlers.close()
          }}
        />
      )}
    </Card>
  )
})
