import {
  AnimatedNumber,
  Button,
  Card,
  Divider,
  Flex,
  Group,
  Stack,
  StackProps,
  Typography,
  TypographyProps,
  notifier
} from '@tidbcloud/uikit'
import { useDebouncedValue } from '@tidbcloud/uikit/hooks'
import { useMemoizedFn } from 'ahooks'
import { observer } from 'mobx-react'
import { useFormContext } from 'react-hook-form'
import { useHistory } from 'react-router-dom'

import { PropertyKV } from 'dbaas/components'
import { SharedCluster } from 'dbaas/features/ClusterManagement/cluster'
import { CloudProviderLabelConst } from 'dbaas/features/Project'
import { PlanDisplayNameMap } from 'dbaas/screens/TiDBNextGen/components/helper'
import { usePredictCost } from 'dbaas/services/api-clients/legacy/serverless'
import { useServerlessServicePartialUpdateCluster } from 'dbaas/services/api-clients/v2023/serverless/v1beta1'
import { getErrorMessage } from 'dbaas/services/errorCodes'
import useStores from 'dbaas/stores/useStores'
import { moneyFormatter } from 'dbaas/utils/numberFormat'

import { EditFormValueState, ESSENTIAL_RCU_MIN, useCancel } from './helpers'

interface SumInfoItemProps {
  label: string
  value?: React.ReactNode
  isError?: boolean
  errors?: React.ReactNode
  labelProps?: TypographyProps
}

export const SumInfoItem = ({ label, value = '', isError, errors, labelProps = {} }: SumInfoItemProps) => {
  return (
    <PropertyKV
      label={label}
      labelProps={{
        w: 112,
        ...labelProps
      }}
      valueProps={{ color: isError ? 'red' : 'carbon.8' }}
    >
      {isError ? (errors ?? `Invalid ${label}`) : value}
    </PropertyKV>
  )
}

export const SpendingLimitCost = ({
  value = 0,
  hourlyCost = 0,
  ...props
}: { value?: number; hourlyCost?: number } & StackProps) => {
  return (
    <Stack gap={4} {...props}>
      <Typography variant="title-lg">Starting from</Typography>
      <Group gap={8}>
        <Typography variant="headline-md" c="carbon.9">
          ~{moneyFormatter(value)}
        </Typography>
        <Typography variant="body-lg">/month</Typography>
      </Group>
      <Typography variant="body-lg" c="carbon.7">
        ~{moneyFormatter(hourlyCost)}/hr
      </Typography>
      <Typography variant="body-lg" c="carbon.7">
        + usage-based cost
      </Typography>
    </Stack>
  )
}

interface EssentialCostProps {
  cloudProvider: string
  regionName: string
  minRcu: number | undefined
  maxRcu: number | undefined
  wrapperProps?: StackProps
}

export const EssentialCost = ({ cloudProvider, regionName, minRcu, maxRcu, wrapperProps }: EssentialCostProps) => {
  const [debouncedMinRcu] = useDebouncedValue(minRcu, 500)
  const [debouncedMaxRcu] = useDebouncedValue(maxRcu, 500)
  const predictMinRcu = Math.max(debouncedMinRcu || ESSENTIAL_RCU_MIN, ESSENTIAL_RCU_MIN)
  const predictMaxRcu = Math.max(debouncedMaxRcu || ESSENTIAL_RCU_MIN, ESSENTIAL_RCU_MIN)

  const { data: minPredictCost } = usePredictCost(
    {
      cloud_provider: cloudProvider,
      region: regionName,
      rcu: predictMinRcu
    },
    {
      query: {
        enabled: !!(cloudProvider && regionName),
        select: (data) => {
          const monthly = Number(data?.rcu_cost_monthly || 0)

          return {
            monthly
          }
        },
        keepPreviousData: true,
        refetchOnWindowFocus: false
      }
    }
  )

  const { data: maxPredictCost } = usePredictCost(
    {
      cloud_provider: cloudProvider,
      region: regionName,
      rcu: predictMaxRcu
    },
    {
      query: {
        enabled: !!(cloudProvider && regionName),
        select: (data) => {
          const monthly = Number(data?.rcu_cost_monthly || 0)

          return {
            monthly
          }
        },
        keepPreviousData: true,
        refetchOnWindowFocus: false
      }
    }
  )

  return (
    <Stack gap={4} py="xl" {...wrapperProps}>
      <AnimatedNumber
        value={minPredictCost?.monthly ?? 0}
        format={{ maximumFractionDigits: 5, minimumFractionDigits: 2 }}
        prefix="From $"
        suffix="/month"
        c="carbon.9"
        fw={700}
        fz={20}
        sx={{
          '&::part(suffix)': {
            color: 'var(--mantine-color-carbon-8)',
            fontWeight: 400,
            fontSize: 14,
            marginLeft: 4
          }
        }}
      />
      <Typography variant="body-md" c="carbon.8" inline>
        Pay only for your actual usage. Monthly fees are limited by your maximum RCU setting, up to a maximum cost of{' '}
        <Typography variant="title-md" component="span">
          ${(maxPredictCost?.monthly ?? 0).toFixed(2)}
        </Typography>{' '}
        per month. Learn more{' '}
        <a target="_blank" rel="noreferrer" href="https://www.pingcap.com/tidb-cloud-essential-pricing-details/">
          TiDB Cloud Essential pricing
        </a>
      </Typography>
    </Stack>
  )
}

interface SumCardProps {
  cluster: SharedCluster
}

export const SumCard = observer(({ cluster }: SumCardProps) => {
  const {
    store: {
      lastVisitLocation: { orgId, projectId }
    }
  } = useStores()
  const cancel = useCancel()
  const history = useHistory()
  const {
    handleSubmit,
    watch,
    formState: { errors }
  } = useFormContext<EditFormValueState>()
  const minRcu = watch('autoScaling.minRcu')
  const maxRcu = watch('autoScaling.maxRcu')
  const currentMaxRcu = Number(cluster.autoScaling?.maxRcu)
  const targetMaxRcu = Number(maxRcu)
  const isMaxRcuChanged =
    Number.isFinite(currentMaxRcu) && Number.isFinite(targetMaxRcu) && targetMaxRcu !== currentMaxRcu

  const updator = useServerlessServicePartialUpdateCluster({
    mutation: {
      onSuccess: () => {
        if (cluster.id) {
          notifier.success('Capacity updated successfully!')
          // wait backend sync data
          setTimeout(() => {
            history.push(`/clusters/${cluster.id}/overview`, { from: 'edit-capacity' })
          }, 1200)
        }
      },
      onError: (error) => {
        const errorMsg = getErrorMessage(error)

        notifier.error(<div dangerouslySetInnerHTML={{ __html: errorMsg }}></div>)
      }
    }
  })
  const onSubmit = useMemoizedFn(async (data: EditFormValueState) => {
    if (!orgId || !cluster?.id) {
      notifier.error(<div dangerouslySetInnerHTML={{ __html: 'Failed to update Capacity' }}></div>)

      return
    }

    return await updator.mutateAsync({
      clusterId: cluster.id,
      data: {
        cluster: {
          autoScaling: {
            minRcu: data.autoScaling.minRcu,
            maxRcu: data.autoScaling.maxRcu
          }
        },
        updateMask: 'autoScaling'
      },
      params: {
        orgId,
        projectId
      }
    })
  })
  const onConfirm = handleSubmit(onSubmit)

  return (
    <Card shadow="none" p="xl" miw={400}>
      <div>
        <Typography variant="title-lg">Summary</Typography>
        <Typography variant="body-lg" c="carbon.7" mt={4}>
          Your new instance will be
        </Typography>
      </div>
      <Stack justify="flex-start" gap={8} my="xl">
        <SumInfoItem label="Plan" value={PlanDisplayNameMap[cluster.servicePlan || '']} />
        <SumInfoItem label="Provider" value={CloudProviderLabelConst[cluster.provider]} />
        <SumInfoItem label="Region" value={cluster.regionDisplayName} />
        <SumInfoItem label="Name" value={cluster?.displayName} />
      </Stack>
      <Divider />
      <EssentialCost cloudProvider={cluster.provider} regionName={cluster.region} minRcu={minRcu} maxRcu={maxRcu} />
      <Flex align="center" gap="md">
        <Button variant="default" onClick={cancel} data-mp-event="Cancel Update Capacity Button Clicked">
          Cancel
        </Button>
        <Button
          disabled={!!errors.autoScaling?.maxRcu || !!errors.autoScaling?.minRcu || !isMaxRcuChanged}
          loading={updator.isLoading}
          onClick={onConfirm}
          sx={{ flexGrow: 1 }}
          data-mp-event=" Update Capacity Button Clicked"
          data-mp-ev-alias=" Update Capacity Button Clicked"
        >
          Update Capacity
        </Button>
      </Flex>
    </Card>
  )
})
