import {
  ActionIcon,
  Anchor,
  Badge,
  Box,
  Button,
  Flex,
  Group,
  HoverCard,
  Input,
  Menu,
  Modal,
  Stack,
  Typography
} from '@tidbcloud/uikit'
import { Form, MRT_ColumnDef, ProTable } from '@tidbcloud/uikit/biz'
import { IconDotsHorizontal, IconInfoCircle, IconSearchLg } from '@tidbcloud/uikit/icons'
import { capitalize } from 'lodash-es'
import { useMemo, useState } from 'react'
import { Link, useHistory } from 'react-router-dom'

import { ModalCodeTitle } from 'dbaas/components'
import { AccessDeniedHoverHint, useAccessCheck } from 'dbaas/contexts/access-control'
import { useDeleteBudget, useListAllBudgets } from 'dbaas/services/billing'
import { FormsBudget } from 'dbaas/services/billing/interface'
import { getErrorMessage } from 'dbaas/services/errorCodes'
import useStores from 'dbaas/stores/useStores'
import { moneyFormatter } from 'dbaas/utils/numberFormat'

import { MoneyValue } from '../components/MoneyValue'

const MAX_CUSTOM_BUDGET_SIZE = 5
const EMPTY_ARR: FormsBudget[] = []
const BIZ_TYPE_MAP: { [k: string]: string } = {
  serverless_cluster: 'Starter Spending Limit',
  budget_alert: 'Custom'
}
const STATUS_MAP: { [k: string]: string } = { fit: 'OK', warning: 'Warning', excessive: 'Exceeded' }
const SCOPE_MAP: { [k: string]: string } = { tenant: 'Organization', cluster: 'Cluster', project: 'Project' }

export const BudgetAlert = () => {
  const history = useHistory()
  const {
    store: { lastVisitLocation }
  } = useStores()
  const {
    data,
    isLoading: isLoadingList,
    isRefetching,
    error
  } = useListAllBudgets(lastVisitLocation.orgId, { query: { refetchOnWindowFocus: false } })
  const [search, setSearch] = useState('')
  const searchedData = useMemo(() => {
    const _data = data || []
    const _search = search.trim()
    if (!_search) {
      return _data
    }

    return _data.filter((d) => d.name?.toLowerCase().includes(_search.toLowerCase()))
  }, [search, data])
  const isLoading = isLoadingList || isRefetching
  const customBudgets = useMemo(() => data?.filter((d) => d.biz_type !== DEFAULT_BUDGET_TYPE), [data])

  return (
    <Stack>
      <Group justify="space-between">
        <Input
          placeholder="Search by name"
          rightSection={
            <Flex w={36} h={36} justify="center" align="center" sx={{ cursor: 'pointer' }}>
              <IconSearchLg />
            </Flex>
          }
          w={298}
          value={search}
          onChange={(e: React.ChangeEvent<HTMLInputElement>) => setSearch(e.currentTarget.value)}
        />
        <AccessDeniedHoverHint id="BillingBudgetCreation">
          {({ accessible }) => (
            <Button
              size="sm"
              disabled={!accessible || isLoading || (customBudgets?.length || 0) >= MAX_CUSTOM_BUDGET_SIZE}
              onClick={() => {
                history.push('/org-settings/billing/budget-alert/budget')
              }}
              data-mp-event="Create Budget Button Clicked"
            >
              Create Custom Budget {isLoading ? '' : `(${customBudgets?.length || 0}/${MAX_CUSTOM_BUDGET_SIZE})`}
            </Button>
          )}
        </AccessDeniedHoverHint>
      </Group>
      <BudgetAlertTable data={searchedData} isLoading={isLoading} errorMessage={getErrorMessage(error)} />
    </Stack>
  )
}

const BudgetAlertTable: React.FC<{ data?: FormsBudget[]; isLoading: boolean; errorMessage: string }> = ({
  data,
  isLoading,
  errorMessage
}) => {
  const {
    store: { lastVisitLocation }
  } = useStores()
  const [deleteModalData, setDeleteModalData] = useState<FormsBudget>()

  const clusterColumns = useMemo<MRT_ColumnDef<FormsBudget>[]>(
    () => [
      {
        accessorKey: 'name',
        header: 'Name',
        minSize: 160,
        accessorFn: (row) => {
          if (row.biz_type === 'serverless_cluster' && row.meta?.cluster_name) {
            return (
              <Typography>
                Budget for{' '}
                <Link
                  to=""
                  onClick={(e) => {
                    e.preventDefault()
                    window.location.href = `/clusters/${row.meta!.cluster_id}/overview?orgId=${
                      lastVisitLocation.orgId
                    }&projectId=${row.meta!.project_id}`
                    return false
                  }}
                >
                  {row.meta.cluster_name}
                </Link>
              </Typography>
            )
          }
          return row.name
        }
      },
      {
        header: 'Type',
        Header: () => {
          return (
            <Group gap="xs">
              <Typography>Type</Typography>
              <HoverCard position="top" shadow="md" withArrow>
                <HoverCard.Target>
                  <Group sx={{ cursor: 'pointer' }}>
                    <IconInfoCircle />
                  </Group>
                </HoverCard.Target>
                <HoverCard.Dropdown>
                  The spending limit budget is automatically created according to the{' '}
                  <Anchor href="https://docs.pingcap.com/tidbcloud/manage-serverless-spend-limit" target="_blank">
                    spending limit
                  </Anchor>{' '}
                  configured for your TiDB Cloud Starter instance.
                </HoverCard.Dropdown>
              </HoverCard>
            </Group>
          )
        },
        accessorFn: (row) => (row.biz_type ? BIZ_TYPE_MAP[row.biz_type] : 'Unknown'),
        size: 200
      },
      {
        header: 'Status',
        accessorFn: (row) => <StateTag data={row} />,
        size: 210
      },
      {
        header: 'Amount Used',
        accessorFn: (row) => {
          const bizData = JSON.parse(row.biz_data || '{}')
          const withDiscountAndCredit = (!bizData.apply_credits || !bizData.apply_discounts) as boolean
          let filterParams: { [k: string]: any } = {}

          if (withDiscountAndCredit) {
            filterParams.withDiscountAndCredit = true
          }
          const isServerless = row.biz_type === 'serverless_cluster'
          const isCustomAndProjectRange = row.biz_type === 'budget_alert' && row.range_type === 'project'
          if (isServerless) {
            filterParams.cluster = [row.range_id_str]
          }
          if (isCustomAndProjectRange) {
            filterParams.project = [row.range_id_str]
          }

          const filterParamsStr = JSON.stringify(filterParams)

          return (
            <HoverCard position="top" shadow="md" withArrow>
              <HoverCard.Target>
                <Link to={`/org-settings/billing/cost-explorer?&__ce_filter__=${filterParamsStr}`} aria-label="go to">
                  <MoneyValue amount={row.costs!} monospaced />
                </Link>
              </HoverCard.Target>
              <HoverCard.Dropdown>Analyze your costs with Cost Explorer.</HoverCard.Dropdown>
            </HoverCard>
          )
        },
        size: 160
      },
      {
        header: 'Budget Amount',
        accessorFn: (row) => <MoneyValue amount={row.limit!} monospaced />,
        size: 160
      },
      {
        accessorKey: 'interval',
        header: 'Period',
        size: 100,
        accessorFn: (row) => capitalize(row.interval)
      },
      {
        accessorKey: 'range_type',
        header: 'Scope',
        size: 110,
        accessorFn: (row) => SCOPE_MAP[row.range_type!]
      },
      {
        header: 'Action',
        accessorFn: (row) => {
          return <BudgetOperations data={row} openDeleteModal={() => setDeleteModalData(row)} />
        },
        size: 60
      }
    ],
    [lastVisitLocation.orgId]
  )

  return (
    <>
      <Box mx="-xl">
        <ProTable<FormsBudget>
          loading={isLoading}
          errorMessage={errorMessage}
          enableColumnPinning
          initialState={{ columnPinning: { left: ['name'] } }}
          mantineTableProps={{
            verticalSpacing: 8,
            horizontalSpacing: 'sm',
            sx: {
              'thead tr th:first-of-type': {
                paddingLeft: 24
              }
            }
          }}
          mantinePaperProps={{
            mx: 24
          }}
          columns={clusterColumns}
          data={data || EMPTY_ARR}
          emptyMessage="No Data"
        />
      </Box>
      <DeleteBudgetModal
        visible={!!deleteModalData}
        onClose={() => setDeleteModalData(undefined)}
        budget={deleteModalData}
      />
    </>
  )
}

const statusColorMap: {
  [index: string]: string
} = {
  fit: 'green',
  excessive: 'red'
}
const StateTag: React.FC<{ data: FormsBudget }> = ({ data }) => {
  const proportion = (Number.parseFloat(data.costs!) / Number.parseFloat(data.limit!)) * 100
  const proportionStr = proportion.toFixed(2)
  const status = data.status

  let statusLabel = STATUS_MAP[status!] || status
  let statusColor = statusColorMap[status || '']

  const minThreshold =
    data.thresholds?.reduce((prev, cur) => {
      const curProportion = Number.parseFloat(cur.proportion!) * 100
      if (!prev) {
        return curProportion
      }

      if (curProportion < prev) {
        return curProportion
      }

      return prev
    }, 0) || 0

  if (status === 'fit' && proportion >= minThreshold) {
    statusLabel = STATUS_MAP.warning
    statusColor = 'yellow'
  }

  return (
    <Group gap="xs">
      <Badge color={statusColor} variant="dot" size="lg" sx={{ padding: 0 }}>
        {statusLabel} ({proportionStr}%)
      </Badge>
      <HoverCard position="right" shadow="md" width={280} withArrow>
        <HoverCard.Target>
          <Stack align="center" sx={{ cursor: 'pointer' }}>
            <IconInfoCircle />
          </Stack>
        </HoverCard.Target>
        <HoverCard.Dropdown>
          <Stack gap="xs">
            {data.thresholds?.map((t, idx) => {
              const tProportion = Number.parseFloat(t.proportion!)
              return (
                <Typography variant="body-sm" key={idx}>
                  Threshold {idx + 1}: {'>='} {(tProportion * 100).toFixed(2)}% (
                  {moneyFormatter(Number.parseFloat(data.limit!) * tProportion)}){' '}
                  {proportion >= tProportion * 100 ? 'exceeded' : ''}
                </Typography>
              )
            })}
          </Stack>
        </HoverCard.Dropdown>
      </HoverCard>
    </Group>
  )
}

const DEFAULT_BUDGET_TYPE = 'serverless_cluster'

const BudgetOperations = ({ data, openDeleteModal }: { data: FormsBudget; openDeleteModal: () => void }) => {
  const history = useHistory()
  const actionsAccessible = useAccessCheck('BillingBudgetAction')

  return (
    <Menu position="bottom-end" width={200}>
      <Menu.Target>
        <ActionIcon variant="subtle" data-mp-event="Budget Detail Button Clicked" w={32} h={32}>
          <IconDotsHorizontal size={16} />
        </ActionIcon>
      </Menu.Target>
      <Menu.Dropdown>
        <HoverCard position="left" shadow="md" width={220} withArrow disabled={data.biz_type !== DEFAULT_BUDGET_TYPE}>
          <HoverCard.Target>
            <Menu.Item
              disabled={!actionsAccessible}
              onClick={() =>
                data.biz_type !== DEFAULT_BUDGET_TYPE &&
                history.push(`/org-settings/billing/budget-alert/budget/${data.spendlimit_setting_id}`)
              }
              {...(data.biz_type === DEFAULT_BUDGET_TYPE ? { 'data-disabled': true } : {})}
              data-mp-event="Edit Budget Button Clicked"
            >
              Edit
            </Menu.Item>
          </HoverCard.Target>
          <HoverCard.Dropdown>You can edit spending limit on the Starter instance overview page.</HoverCard.Dropdown>
        </HoverCard>
        <HoverCard position="left" shadow="md" width={220} withArrow disabled={data.biz_type !== DEFAULT_BUDGET_TYPE}>
          <HoverCard.Target>
            <Menu.Item
              disabled={!actionsAccessible}
              onClick={() => data.biz_type !== DEFAULT_BUDGET_TYPE && openDeleteModal()}
              {...(data.biz_type === DEFAULT_BUDGET_TYPE ? { 'data-disabled': true } : {})}
              data-mp-event="Delete Budget Button Clicked"
            >
              Delete
            </Menu.Item>
          </HoverCard.Target>
          <HoverCard.Dropdown>You cannot delete the spending limit for a Starter instance.</HoverCard.Dropdown>
        </HoverCard>
      </Menu.Dropdown>
    </Menu>
  )
}

const DeleteBudgetModal = ({
  visible,
  onClose,
  budget
}: {
  visible: boolean
  onClose: () => void
  budget?: FormsBudget
}) => {
  const {
    store: { lastVisitLocation }
  } = useStores()
  const { mutateAsync: deleteBudget, isLoading } = useDeleteBudget()
  const { refetch: refreshList } = useListAllBudgets(lastVisitLocation.orgId, { query: { enabled: false } })

  return (
    <Modal
      size="xl"
      opened={visible}
      centered
      trapFocus={false}
      onClose={onClose}
      title={
        <>
          Delete <ModalCodeTitle maxWidth={400}>{budget?.name}</ModalCodeTitle>
        </>
      }
    >
      <Typography variant="body-lg">
        Are you sure you want to delete this budget alert? Once deleted, you will no longer receive over-limit
        notifications.
      </Typography>

      <Form
        layout="none"
        form={undefined}
        onSubmit={async () => {
          await deleteBudget({ tenantId: lastVisitLocation.orgId, budgetId: String(budget?.spendlimit_setting_id!) })
          refreshList()
          onClose()
        }}
        onCancel={onClose}
        actionsProps={{
          loading: isLoading,
          confirmText: 'I understand, delete it.',
          confirmProps: {
            color: 'red',
            variant: 'light',
            'data-mp-event': 'Deleted A Budget Alert Config'
          },
          cancelProps: {
            'data-mp-event': 'Cancel Delete Budget Alert Modal Button Clicked'
          }
        }}
      />
    </Modal>
  )
}
