import { Box, Button, Divider, Group, Typography } from '@tidbcloud/uikit'
import { useURLQueryState } from '@tidbcloud/uikit/hooks'
import { IconArrowTab, IconEqual, IconMinus } from '@tidbcloud/uikit/icons'
import dayjs from 'dayjs'
import { observer } from 'mobx-react'
import React, { useEffect, useMemo, useState } from 'react'
import { FormProvider, useForm } from 'react-hook-form'
import { useQuery } from 'react-query'

import { getCostExplorerTrends, useGetTenantCostExplorerArgs } from 'dbaas/services/billing'
import { FormsBillingConfig, FormsCostExplorerTrendsBreakdownKey } from 'dbaas/services/billing/interface'
import useStores from 'dbaas/stores/useStores'
import { eventTracking } from 'dbaas/utils/tracking'

import { CostsLabel } from '../components/CostsItem'
import { toTimeRangeValue } from '../components/DatePicker/helpers'
import { filterByocCostExplorerOutput, getEffectiveCostExplorerServices } from '../helpers/byoc-bill'

import { ExplorerChart } from './ExplorerChart'
import { ExplorerTable } from './ExplorerTable'
import {
  BREAKDOWN_KEY_CHARGE_TYPE,
  DEFAULT_FILTER_VALUE,
  DEFAULT_GROUP_BY,
  DEFAULT_TIME_RANGE,
  Filter,
  FILTER_STATE_KEY,
  FilterFormFields,
  FilterValue
} from './Filter'
import { MoneyValue } from './MoneyValue'

export const CostExplorer: React.FC<React.PropsWithChildren<{ billingConfig: FormsBillingConfig }>> = observer(
  ({ billingConfig }) => {
    const [showFilter, setShowFilter] = useState(true)
    const [filterValue, setFilterValue] = useURLQueryState(FILTER_STATE_KEY, DEFAULT_FILTER_VALUE)
    const currentFilterValue = (filterValue || DEFAULT_FILTER_VALUE) as FilterValue
    const {
      store: {
        tenantData,
        config: { enableByocBill }
      }
    } = useStores()
    const shouldHideByocBill = !!enableByocBill
    const { groupBy: groupByValue, withDiscountAndCredit, trendBy: _trendBy, project, cluster } = currentFilterValue
    // empty string means trend by total, `undefined` use the default value
    const trendBy =
      _trendBy === undefined
        ? shouldHideByocBill || !withDiscountAndCredit
          ? DEFAULT_FILTER_VALUE.trendBy
          : ''
        : shouldHideByocBill && _trendBy === ''
          ? DEFAULT_FILTER_VALUE.trendBy
          : _trendBy
    const effectiveWithDiscountAndCredit = shouldHideByocBill ? false : withDiscountAndCredit
    const methods = useForm<FilterFormFields>({
      defaultValues: {
        groupBy: (groupByValue || DEFAULT_GROUP_BY) as FormsCostExplorerTrendsBreakdownKey,
        timeRange: DEFAULT_TIME_RANGE,
        projects: project,
        clusters: cluster
      }
    })

    const { watch } = methods
    const groupBy = watch('groupBy')
    const timeRange = watch('timeRange')
    const timeRangeValue = useMemo(() => {
      const trv = toTimeRangeValue(timeRange)
      const startTime = dayjs.unix(trv[0]).startOf('day')
      const endTime = dayjs.unix(trv[1]).endOf('day')
      return [startTime, endTime].map((t) => t.format('YYYY-MM-DDTHH:mm:ss[Z]'))
    }, [timeRange])
    const providers = watch('providers')
    const projects = watch('projects')
    const services = watch('services')
    const clusters = watch('clusters')
    const { data: costExplorerArgs, isLoading: isArgsLoading } = useGetTenantCostExplorerArgs(tenantData.id, {
      query: { refetchOnWindowFocus: false },
      request: { isHandleErr: true }
    })
    const effectiveServices = useMemo(
      () => getEffectiveCostExplorerServices(services, costExplorerArgs?.services || [], shouldHideByocBill),
      [costExplorerArgs?.services, shouldHideByocBill, services]
    )

    useEffect(() => {
      if (!shouldHideByocBill) {
        return
      }

      if (_trendBy === '' || withDiscountAndCredit) {
        setFilterValue({
          ...currentFilterValue,
          trendBy: DEFAULT_FILTER_VALUE.trendBy,
          withDiscountAndCredit: false
        })
      }
    }, [shouldHideByocBill, currentFilterValue, setFilterValue, _trendBy, withDiscountAndCredit])

    useEffect(() => {
      if (groupBy === BREAKDOWN_KEY_CHARGE_TYPE && currentFilterValue.withDiscountAndCredit) {
        setFilterValue({ ...currentFilterValue, withDiscountAndCredit: false })
      }
    }, [currentFilterValue, groupBy, setFilterValue])

    const { data, isLoading: isTrendsLoading } = useQuery(
      ['getCostExplorerTrends', trendBy, groupBy, timeRangeValue, providers, projects, effectiveServices, clusters],
      () =>
        getCostExplorerTrends(
          tenantData.id,
          String(billingConfig.billing_config_id),
          {
            start_date: timeRangeValue[0],
            end_date: timeRangeValue[1],
            breakdown_by: groupBy !== BREAKDOWN_KEY_CHARGE_TYPE ? [groupBy] : [],
            time_group: trendBy === '' ? undefined : trendBy,
            providers,
            projects,
            services: effectiveServices,
            clusters
          },
          { isHandleErr: true }
        ),
      {
        enabled: !!(tenantData.id && billingConfig.billing_config_id) && (!shouldHideByocBill || !isArgsLoading),
        refetchOnWindowFocus: false
      }
    )
    const displayData = useMemo(
      () => (shouldHideByocBill ? filterByocCostExplorerOutput(data) : data),
      [data, shouldHideByocBill]
    )
    const isLoading = isTrendsLoading || (shouldHideByocBill && isArgsLoading)

    const Brief = () => (
      <Group justify="space-between" px="xl" py="md" gap="lg">
        <CostsLabel
          label="Select Time"
          value={toTimeRangeValue(timeRange)
            .map((t) => dayjs.unix(t).format('MMM D, YYYY'))
            .join(' - ')}
        />

        <Group justify="space-between" gap={118}>
          {!shouldHideByocBill && (
            <Group>
              <CostsLabel
                label="Running Total"
                value={<MoneyValue amount={displayData?.raw_price!} />}
                loading={isLoading}
              />
              <IconMinus />
              <CostsLabel
                label="Credits"
                value={<MoneyValue amount={displayData?.credits_usage!} />}
                loading={isLoading}
              />{' '}
              <IconMinus />
              <CostsLabel
                label="Discounts"
                value={<MoneyValue amount={displayData?.discount!} />}
                loading={isLoading}
              />
              <IconEqual />
              <CostsLabel
                label={
                  <>
                    Total Cost{' '}
                    <Typography span variant="label-sm" c="carbon.6">
                      (without taxes)
                    </Typography>
                  </>
                }
                value={<MoneyValue amount={displayData?.cost_price!} />}
                loading={isLoading}
              />
            </Group>
          )}

          {!showFilter && (
            <Button
              data-mp-event="Billing Cost Explorer Filter Unfold Clicked"
              variant="default"
              leftSection={<IconArrowTab size={18} style={{ transform: 'rotate(180deg)' }} />}
              onClick={() => setShowFilter((v) => !v)}
            >
              Filter
            </Button>
          )}
        </Group>
      </Group>
    )

    return (
      <FormProvider {...methods}>
        <Box h="calc(100% + 48px)" m={-24} pt={8}>
          <Group h="100%" gap={0} sx={(t) => ({ borderTop: `1px solid ${t.colors.carbon[4]}` })}>
            <Box w="100%" h="100%" sx={{ display: 'flex' }}>
              <Box mah="100%" pb="xl" sx={{ flex: '1 1', overflowY: 'auto' }}>
                <Brief />
                <Divider c="carbon.4" />

                <ExplorerChart
                  loading={isLoading}
                  data={displayData}
                  trendBy={trendBy}
                  onTrendByChange={(v) => {
                    eventTracking('Billing Cost Explorer Trend By Clicked', { trendBy: v })
                    setFilterValue({
                      ...currentFilterValue,
                      trendBy: v,
                      withDiscountAndCredit: v ? false : currentFilterValue.withDiscountAndCredit
                    })
                  }}
                  withDiscountAndCredit={effectiveWithDiscountAndCredit}
                  onDiscountAndCreditChange={(val) => {
                    setFilterValue({
                      ...currentFilterValue,
                      withDiscountAndCredit: val,
                      trendBy: shouldHideByocBill ? DEFAULT_FILTER_VALUE.trendBy : ''
                    })
                  }}
                  hideTotalTrend={shouldHideByocBill}
                />
                <Divider c="carbon.4" />
                <ExplorerTable
                  loading={isLoading}
                  data={displayData}
                  trendBy={trendBy}
                  billingConfig={billingConfig}
                  withDiscountAndCredit={effectiveWithDiscountAndCredit}
                  effectiveServices={effectiveServices}
                  hideTotalRow={shouldHideByocBill}
                />
              </Box>
              <Filter
                showFilter={showFilter}
                setShowFilter={setShowFilter}
                isLoading={isLoading}
                argsData={costExplorerArgs}
                argsLoading={isArgsLoading}
                hideByocBill={shouldHideByocBill}
              />
            </Box>
          </Group>
        </Box>
      </FormProvider>
    )
  }
)
