import {
  Progress,
  Box,
  Stack,
  Card,
  Button,
  Skeleton,
  Alert,
  Typography,
  useMantineTheme,
  Flex,
  ActionIcon,
  HoverCard,
  Group,
  CardProps,
  useComputedColorScheme
} from '@tidbcloud/uikit'
import { Dot, DotProps, LabelTooltip } from '@tidbcloud/uikit/biz'
import { CSSObject } from '@tidbcloud/uikit/emotion'
import { IconAlertCircle, IconEdit05, IconHelpCircle, IconBreakdown02 } from '@tidbcloud/uikit/icons'
import { mergeSxList } from '@tidbcloud/uikit/utils'
import dayjs from 'dayjs'
import { observer } from 'mobx-react'
import React from 'react'
import { Link } from 'react-router-dom'

import config from 'config'
import { AccessDeniedHoverHint } from 'dbaas/contexts/access-control'
import { SharedCluster } from 'dbaas/features/ClusterManagement/cluster'
import { MoneyValue } from 'dbaas/screens/OrgSettings/features/Billing/components/MoneyValue'
import { useGetSpendLimitDetails, ServerlessGetSpendLimitDetailsResp, CentralClusterStatus } from 'dbaas/services'
import { getErrorMessage } from 'dbaas/services/errorCodes'
import useStores from 'dbaas/stores/useStores'
import { formatStorage, formatToMillions, toDollars } from 'dbaas/utils/format'
import { moneyFormatter } from 'dbaas/utils/numberFormat'

import { useSpendLimitSummary } from '../helpers'

import { SpendingEstimationChart } from './EstimationCard'
import { useGetForecastResult } from './EstimationCard/helpers'

const LeftRightSx: CSSObject = {
  display: 'flex',
  justifyContent: 'space-between',
  alignItems: 'center',
  marginTop: 30,
  height: 20,
  lineHeight: '20px'
}

const StorageMetricItem: React.FC<
  React.PropsWithChildren<{
    label: string
    value: number
    tooltip?: string
    dotProps?: DotProps
  }>
> = ({ label, value, tooltip, dotProps }) => {
  return (
    <Box>
      <Box sx={LeftRightSx}>
        <Flex align="center">
          <Dot color="blue" bg="peacock.7" {...dotProps} />
          <Typography variant="body-lg" ml={8}>
            {label}
            {tooltip && <LabelTooltip width={400} label={tooltip} />}
          </Typography>
        </Flex>
        <Typography variant="body-lg" c="carbon.8">
          {formatStorage(value)}
        </Typography>
      </Box>
    </Box>
  )
}

const RequestUnitItem: React.FC<
  React.PropsWithChildren<{
    label: string
    value: number
    tooltip?: string
    dotProps?: DotProps
  }>
> = ({ label, value, tooltip, dotProps }) => {
  return (
    <Box>
      <Box sx={LeftRightSx}>
        <Flex align="center">
          <Dot color="blue" bg="peacock.5" {...dotProps} />
          <Typography variant="body-lg" ml={8}>
            {label}
            {tooltip && <LabelTooltip width={400} label={tooltip} />}
          </Typography>
        </Flex>
        <Typography variant="body-lg" c="carbon.8">
          {formatToMillions(value)}
        </Typography>
      </Box>
    </Box>
  )
}

function LoadingSkeleton() {
  return (
    <Box h={216}>
      <Skeleton height={28} />
      <Skeleton height={28} mt={18} />
      <Skeleton height={28} mt={18} />
      <Skeleton height={28} mt={18} />
    </Box>
  )
}

const PaidSpendLimitMetric: React.FC<
  React.PropsWithChildren<{
    orgId: string
    projectId: string
    data: ServerlessGetSpendLimitDetailsResp
    cluster: SharedCluster
  }>
> = ({ data, cluster, orgId, projectId }) => {
  const theme = useMantineTheme()
  const colorScheme = useComputedColorScheme()
  // progress calculation...
  const { hasCost, hasFreeQuota, hasSpendlimit, totalProgressNumber, cost, usedUp } = useSpendLimitSummary(data)

  const { data: forecastData } = useGetForecastResult(orgId, projectId, cluster, {
    query: {
      enabled: config.ENABLE_SERVERLESS_ESTIMATION && hasSpendlimit
    },
    request: {
      isHandleErr: true,
      _warningStatus: [404]
    }
  })
  const currentDate = dayjs()
  const currentUnixTime = dayjs(currentDate.format('YYYY-MM-DD')).utc(true).unix()

  // @TODO only calculate when has cost
  const showEstimation =
    !!forecastData?.data &&
    forecastData.data.predict_reach_spent_limit_date &&
    forecastData.data.predict_reach_spent_limit_date - currentUnixTime < 3 * 24 * 60 * 60 &&
    (currentUnixTime - dayjs(data.update_at).utc(true).unix() > 24 * 60 * 60 ||
      data.spend_limit / 100 <= forecastData.data.spend_limit_setting)

  const spent_free = data.spent_free || { request_unit: 0, column_storage: 0, row_storage: 0 }
  const spent_paid = data.spent_paid || { request_unit: 0, row_storage: 0, column_storage: 0 }

  const totalSpentFree = spent_free.request_unit + spent_free.column_storage + spent_free.row_storage

  const calcProgress = (num: number) => {
    return (num / totalProgressNumber) * 100
  }

  const isDark = colorScheme === 'dark'

  const onlyFreeQuota = !hasSpendlimit
  const freeQuotaWithSpendLimit = hasFreeQuota && hasSpendlimit
  const onlySpendLimit = !hasFreeQuota && hasSpendlimit

  // only free quota and only spend limit have same progress
  let sections: { value: number; color: string }[] = [
    // request units
    {
      value: calcProgress(spent_free.request_unit + spent_paid.request_unit),
      color: theme.colors.blue[isDark ? 9 : 5]
    },
    // Row-based Storage
    {
      value: calcProgress(spent_free.row_storage + spent_paid.row_storage),
      color: theme.colors.blue[7]
    },
    // Columnar Storage
    {
      value: calcProgress(spent_free.column_storage + spent_paid.column_storage),
      color: theme.colors.blue[isDark ? 5 : 9]
    }
  ]

  if (freeQuotaWithSpendLimit) {
    sections = [
      // request units
      {
        value: calcProgress(totalSpentFree),
        color: theme.colors.green[isDark ? 9 : 5]
      },
      // request units
      {
        value: calcProgress(spent_paid.request_unit),
        color: theme.colors.blue[isDark ? 9 : 5]
      },
      // Row-based Storage
      {
        value: calcProgress(spent_paid.row_storage),
        color: theme.colors.blue[7]
      },
      // Columnar Storage
      {
        value: calcProgress(spent_paid.column_storage),
        color: theme.colors.blue[isDark ? 5 : 9]
      }
    ]
  }

  const CurrentSpendElem = (
    <>
      <Typography variant="title-md" ml={8} c={usedUp ? 'red.7' : undefined}>
        <MoneyValue amount={toDollars(cost)} />
      </Typography>
      <HoverCard position="right" withArrow>
        <HoverCard.Target>
          <Flex px={8} sx={{ cursor: 'pointer' }}>
            <IconBreakdown02 size={16} />
          </Flex>
        </HoverCard.Target>
        <HoverCard.Dropdown miw={242}>
          <Group justify="space-between">
            <Typography variant="body-lg">Request Units</Typography>
            <Typography variant="body-lg">{moneyFormatter(toDollars(spent_paid.request_unit))}</Typography>
          </Group>
          <Group justify="space-between">
            <Typography variant="body-lg">Row-based Storage</Typography>
            <Typography variant="body-lg">{moneyFormatter(toDollars(spent_paid.row_storage))}</Typography>
          </Group>
          <Group justify="space-between">
            <Typography variant="body-lg">Columnar Storage</Typography>
            <Typography variant="body-lg">{moneyFormatter(toDollars(spent_paid.column_storage))}</Typography>
          </Group>
        </HoverCard.Dropdown>
      </HoverCard>
    </>
  )

  return (
    <Stack gap={0}>
      <Box sx={mergeSxList([LeftRightSx, { justifyContent: 'flex-start', marginTop: 16 }])}>
        {/* only free quota usage */}
        {onlyFreeQuota ? (
          <Flex align="center" gap={8}>
            <Typography variant="body-lg" c="carbon.9">
              Current Spend
            </Typography>
            <Typography variant="title-md" c="green.7">
              Free
            </Typography>
            <AccessDeniedHoverHint id="TidbxSpendLimitAction" scopeProps={{ tidbId: cluster.id }}>
              {({ accessible }) => (
                <Button
                  component={Link}
                  to={`/clusters/${cluster.id}/edit-spendlimit`}
                  size="sm"
                  px={8}
                  py={4}
                  variant="default"
                  data-mp-event="Upgrade to Scalable Cluster Button Clicked"
                  data-mp-event-alias="Set Starter Cluster Spending limit Button Clicked"
                  disabled={cluster.computed.creating || !accessible}
                  onClick={(e) => {
                    if (cluster.computed.creating || !accessible) {
                      e.preventDefault()
                      return
                    }
                  }}
                  sx={{ ':hover': { textDecoration: 'none' } }}
                >
                  Set Spending limit
                </Button>
              )}
            </AccessDeniedHoverHint>
          </Flex>
        ) : (
          <>
            <Box sx={{ display: 'flex', alignItems: 'center' }}>
              {/* free quota + spend limt */}
              {freeQuotaWithSpendLimit && (
                <>
                  <Typography variant="body-lg" c="carbon.9">
                    Current Spend
                  </Typography>
                  {!hasCost && (
                    <Typography variant="title-md" mx={8} c="green.7">
                      Free
                    </Typography>
                  )}
                  {hasCost && <>{CurrentSpendElem}</>}
                </>
              )}
              {/* only spendlimit*/}
              {onlySpendLimit && (
                <>
                  <Typography variant="body-lg" c="carbon.9">
                    Current Spend
                  </Typography>
                  {CurrentSpendElem}
                </>
              )}
            </Box>
            <Flex align="center" justify="space-between" sx={{ flex: 1 }}>
              <Flex align="center">
                <Typography variant="body-lg" c="carbon.9" mr={8}>
                  /
                </Typography>
                <Typography variant="body-lg" c="carbon.9" sx={{ display: 'flex', alignItems: 'center' }}>
                  {moneyFormatter(toDollars(data.spend_limit))}
                  <AccessDeniedHoverHint id="TidbxSpendLimitAction" scopeProps={{ tidbId: cluster.id }}>
                    {({ accessible }) => (
                      <Link to={`/clusters/${cluster.id}/edit-spendlimit`}>
                        <ActionIcon
                          color={accessible ? 'blue' : 'carbon.5'}
                          ml={8}
                          disabled={cluster.state === CentralClusterStatus.CREATING || !accessible}
                        >
                          <IconEdit05 size={16} />
                        </ActionIcon>
                      </Link>
                    )}
                  </AccessDeniedHoverHint>
                </Typography>
              </Flex>
              {showEstimation && (
                <HoverCard width={494} withArrow shadow="md" position="top-end">
                  <HoverCard.Target>
                    <Typography sx={{ display: 'flex', alignItems: 'center' }} variant="body-lg" c="yellow.8">
                      Exceed in 3 days
                      <IconHelpCircle size={16} ml={8} />
                    </Typography>
                  </HoverCard.Target>
                  <HoverCard.Dropdown p={16}>
                    <Typography variant="body-sm" fz={11} mb={12}>
                      Based on your previous usage, spending limit will be exceeded in 3 days. We suggest changing the
                      spending limit to {moneyFormatter(forecastData.data.predict_month_end_water_mark)} to avoid
                      possible restrictions.
                    </Typography>
                    <SpendingEstimationChart
                      data={forecastData.data}
                      cost={toDollars(cost)}
                      currentLimit={toDollars(data.spend_limit)}
                      size={{ h: 194 }}
                    />
                  </HoverCard.Dropdown>
                </HoverCard>
              )}
            </Flex>
          </>
        )}
      </Box>
      <Box pt={16} pb={16}>
        <Progress.Root
          mb={8}
          mt={2}
          radius="xs"
          size={6}
          styles={(theme) => ({ root: { background: theme.colors.carbon[isDark ? 5 : 3] } })}
        >
          {sections.map((section) => (
            <Progress.Section {...section} key={section.color} />
          ))}
        </Progress.Root>
        <SpendinglimitAlert data={data} />
      </Box>
    </Stack>
  )
}

const SpendinglimitAlert: React.FC<React.PropsWithChildren<{ data: ServerlessGetSpendLimitDetailsResp }>> = ({
  data
}) => {
  const { hasCost, hasFreeQuota, hasSpendlimit, progress, cost, usedUp } = useSpendLimitSummary(data)

  let msg = <Box h={18}></Box>

  // reached free quota
  if (!hasSpendlimit && hasFreeQuota && data.spent_total >= data.free_credit) {
    // FIXME: Update the doc link
    msg = (
      <Flex align="center">
        <IconAlertCircle size={16} display="inline" c="red.7" mr={4} />
        Quota exhausted. Please set a monthly spending limit. Learn more about{' '}
        <Box
          component="a"
          href="https://www.pingcap.com/tidb-serverless-pricing-details"
          target="_blank"
          rel="noreferrer"
          ml={4}
        >
          pricing
        </Box>
        .
      </Flex>
    )
  } else if (!hasSpendlimit && hasFreeQuota && progress >= 90) {
    // reached 90% free quota
    // FIXME: Update the doc link
    msg = (
      <Flex align="center">
        <IconAlertCircle size={16} display="inline" c="yellow.7" mr={4} />
        Quota almost exhausted. Please set a monthly spending limit. Learn more about{' '}
        <Box
          component="a"
          href="https://www.pingcap.com/tidb-serverless-pricing-details"
          target="_blank"
          rel="noreferrer"
          ml={4}
        >
          pricing
        </Box>
        .
      </Flex>
    )
  } else if (hasSpendlimit && usedUp) {
    msg = (
      <Flex align="center">
        <IconAlertCircle size={16} display="inline" c="red.7" mr={4} />
        Quota exhausted. Try
        <IconEdit05 size={16} display="inline" c="carbon.7" mx={4} />
        increase spending limit to
        <Box
          component="a"
          href="https://www.pingcap.com/tidb-serverless-pricing-details/#cost-and-limitations"
          target="_blank"
          rel="noreferrer"
          ml={4}
        >
          gain full access
        </Box>
        .
      </Flex>
    )
  } else if (hasSpendlimit && progress >= 90) {
    // reached 90% usage
    msg = (
      <Flex align="center">
        <IconAlertCircle size={16} display="inline" c="yellow.7" mr={4} />
        Quota almost exhausted. Try
        <IconEdit05 size={16} display="inline" c="carbon.7" mx={4} />
        edit spending limit to avoid
        <Box
          component="a"
          href="https://www.pingcap.com/tidb-serverless-pricing-details/#cost-and-limitations"
          target="_blank"
          rel="noreferrer"
          ml={4}
        >
          quota exceed limitation
        </Box>
        .
      </Flex>
    )
  } else if (hasSpendlimit && hasFreeQuota && hasCost) {
    msg = (
      <>
        <a
          href="https://www.pingcap.com/tidb-serverless-pricing-details/#cost-and-limitations"
          target="_blank"
          rel="noreferrer"
        >
          Free Quota
        </a>{' '}
        used up. You are charged for actual usage and alerted near{' '}
        <a href="https://www.pingcap.com/tidb-serverless-pricing-details" target="_blank" rel="noreferrer">
          spending limit
        </a>
        .
      </>
    )
  } else if (hasSpendlimit && hasFreeQuota && !cost) {
    msg = (
      <>
        Charges will only be incurred if your usage surpasses the free quota. Learn more about{' '}
        <a
          href="https://www.pingcap.com/tidb-serverless-pricing-details/#cost-and-limitations"
          target="_blank"
          rel="noreferrer"
        >
          Free Quota
        </a>
        .
      </>
    )
  } else if (hasSpendlimit && !hasFreeQuota) {
    msg = (
      <>
        You'll be charged for actual usage and alerted near{' '}
        <a href="https://www.pingcap.com/tidb-serverless-pricing-details" target="_blank" rel="noreferrer">
          spending limit
        </a>
        .
      </>
    )
  }

  return <Typography variant="body-sm">{msg}</Typography>
}

interface SpendLimitUsageProps extends Omit<CardProps, 'children'> {
  cluster: SharedCluster
}

export const SpendLimitUsage = observer(({ cluster, ...rootProps }: SpendLimitUsageProps) => {
  const {
    store: {
      lastVisitLocation: { orgId, projectId }
    }
  } = useStores()

  const colorScheme = useComputedColorScheme()
  const isDark = colorScheme === 'dark'

  const { data, error } = useGetSpendLimitDetails(orgId, cluster?.id, {
    query: {
      // 30s
      refetchInterval: cluster.state === CentralClusterStatus.CREATING ? 1 * 1000 : 30 * 1000
    }
  })

  return (
    <Card p="xl" {...rootProps}>
      <Flex justify="flex-start" align="center" mb={16}>
        <Typography variant="headline-sm">Capacity used this month</Typography>
      </Flex>
      {data ? (
        <>
          <PaidSpendLimitMetric orgId={orgId} projectId={projectId} data={data} cluster={cluster} />
          <Box className="metrics-items" mt={-18}>
            <RequestUnitItem
              label="Request Units"
              value={parseFloat(data.usages?.request_unit || '0')}
              tooltip={`The "request unit" is a unit of measurement used to track the resource consumption of a query or transaction. It is a metric that allows you to estimate the computational resources required to process a specific request in the database.  The request unit is also the billing unit for TiDB Cloud Starter service.`}
              dotProps={{ color: 'peacock', bg: isDark ? 'peacock.9' : 'peacock.5' }}
            />
            <StorageMetricItem
              label="Row-based Storage"
              value={data.usages?.row_storage || 0}
              tooltip={`The "row-based storage" refers to the storage space that stores data in a row-based format.`}
            />
            <StorageMetricItem
              label="Columnar Storage"
              value={data.usages?.column_storage || 0}
              tooltip={`The "columnar storage" refers to the storage space that stores data in a column-based format.`}
              dotProps={{ color: 'peacock', bg: isDark ? 'peacock.5' : 'peacock.9' }}
            />
            {error && (
              <Alert color="red" mt={16}>
                <div dangerouslySetInnerHTML={{ __html: getErrorMessage(error) }}></div>
              </Alert>
            )}
          </Box>
        </>
      ) : (
        <LoadingSkeleton />
      )}
    </Card>
  )
})
