import { Group } from '@tidbcloud/uikit'
import {
  Alert,
  Checkbox,
  Descriptions,
  DescriptionsProps,
  Input,
  message,
  Modal,
  Typography
} from 'antd'
import dayjs, { Dayjs } from 'dayjs'
import { useEffect, useRef, useState } from 'react'
import { Controller, FormProvider, useForm } from 'react-hook-form'
import { useQueryClient } from 'react-query'

import { getGetApiV1DevopsBillingProxyApiV1BillingUnitsQueryKey } from 'apiClient/api'
import {
  usePostApiV1DevopsBillingProxyApiV1BillingUnitsCreateByGroup,
  usePostApiV1DevopsBillingProxyApiV1BillingUnitsCreateByGroupDryrun
} from 'apiClient/services/billing'
import {
  FormsSKUGroup,
  FormsUnitByGroup,
  FormsUnitInfo
} from 'apiClient/services/billing/interface'
import DatePicker from 'components/DatePicker'

import {
  extractPlaceholders,
  extractPriceFormulaPlaceholders,
  isManualPriceSKUGroup
} from './utils'

export interface UnitListModalProps {
  skuGroup: FormsSKUGroup
  open: boolean
  onClose?: () => void
}

export const UnitListModal = ({
  skuGroup,
  open,
  onClose: _onClose
}: UnitListModalProps) => {
  const client = useQueryClient()
  const [isDryRun, setIsDryRun] = useState(true)
  const methods = useForm<FormsUnitByGroup>({
    defaultValues: { group_id: skuGroup.group_id }
  })
  const {
    data: dryRunData,
    mutateAsync: dryRun,
    isLoading: isDryRunning,
    isError: isDryRunError,
    error: dryRunError,
    reset: resetDryRun
  } = usePostApiV1DevopsBillingProxyApiV1BillingUnitsCreateByGroupDryrun()
  const {
    mutateAsync: create,
    isLoading: isCreating,
    isError: isCreateError,
    error: createError,
    reset: resetCreate
  } = usePostApiV1DevopsBillingProxyApiV1BillingUnitsCreateByGroup()
  const isLoading = isDryRunning || isCreating
  const isError = isDryRunError || isCreateError
  const error = dryRunError || createError
  const dataRef = useRef<FormsUnitByGroup>()
  const providerValue = methods.watch('provider')
  const regionValue = methods.watch('region')
  const priceValue = methods.watch('price')
  const unitKeyParameters = methods.watch('unit_key_parameters')
  const isManualPrice = isManualPriceSKUGroup(skuGroup)
  const unitKeyParameterKeys = extractPlaceholders(
    skuGroup.unit_key_pattern || ''
  )
  const hasMissingManualValues =
    isManualPrice &&
    (!priceValue ||
      unitKeyParameterKeys.some((key) => !unitKeyParameters?.[key]))

  const onClose = () => {
    methods.reset({ group_id: skuGroup.group_id })
    resetDryRun()
    resetCreate()
    _onClose?.()
  }

  useEffect(() => {
    if (!open) {
      return
    }
    methods.reset({ group_id: skuGroup.group_id })
  }, [open])

  return (
    <Modal
      title="Create Unit"
      centered
      visible={open}
      okText={isDryRun ? 'Dry Run' : 'Create'}
      okButtonProps={{
        disabled:
          isDryRun &&
          (isManualPrice
            ? hasMissingManualValues
            : !providerValue || !regionValue)
      }}
      onOk={async () => {
        const data = normalizeUnitByGroupData(methods.getValues(), skuGroup)
        if (!validateUnitByGroupData(data, skuGroup)) {
          return
        }
        if (data.beta_end_time) {
          data.beta_end_time =
            dayjs(data.beta_end_time).format('YYYY-MM-DD') + 'T00:00:00Z'
        }
        try {
          if (isDryRun) {
            await dryRun({ data })
            dataRef.current = data
            setIsDryRun(false)
          } else {
            await create({ data: dataRef.current! })
            message.success('Create successful!')
            client.refetchQueries(
              getGetApiV1DevopsBillingProxyApiV1BillingUnitsQueryKey()
            )
            onClose?.()
            setIsDryRun(true)
          }
        } catch {}
      }}
      onCancel={() => {
        if (isLoading) {
          return
        }
        if (isDryRun) {
          onClose?.()
        }
        setIsDryRun(true)
        dataRef.current = undefined
      }}
      confirmLoading={isLoading}
      closable={isDryRun}
      maskClosable={isDryRun}
    >
      {isDryRun ? (
        <FormProvider {...methods}>
          <UnitListForm skuGroup={skuGroup} />
        </FormProvider>
      ) : (
        <UnitListResult data={dryRunData?.data!} />
      )}

      {isError && (
        <Alert type="error" message={(error as any)?.response?.data?.message} />
      )}
    </Modal>
  )
}

const DESC_PROPS: DescriptionsProps = {
  colon: false,
  column: 2,
  layout: 'vertical',
  contentStyle: { paddingRight: '4px' }
}

interface UnitListFormProps {
  skuGroup: FormsSKUGroup
}

const disabeldDate = (date: Dayjs) => {
  const end = dayjs().add(0, 'day').utc().startOf('day')
  return dayjs(date).utc().startOf('day').isBefore(end)
}

const UnitListForm = ({ skuGroup }: UnitListFormProps) => {
  const [isBetaPrice, setIsBetaPrice] = useState(false)
  const isManualPrice = isManualPriceSKUGroup(skuGroup)
  const keyPatternArr = extractPlaceholders(skuGroup.unit_key_pattern || '')
  const priceFormulaArr = extractPriceFormulaPlaceholders(
    skuGroup.price_formula || ''
  )
  const groupLabels = [
    ...new Set([...keyPatternArr, ...priceFormulaArr])
  ].filter((l) => l !== 'provider' && l !== 'region')

  return (
    <>
      <Typography.Title level={5}>SKU Group Fields</Typography.Title>

      <Controller
        name="group_id"
        render={() => {
          return <></>
        }}
      />
      <Descriptions {...DESC_PROPS}>
        {isManualPrice ? (
          <>
            {keyPatternArr.map((key) => (
              <Descriptions.Item
                key={key}
                label={
                  <Typography.Text>
                    {key} <Typography.Text type="danger">*</Typography.Text>
                  </Typography.Text>
                }
              >
                <Controller
                  name={`unit_key_parameters.${key}`}
                  render={({ field }) => <Input {...field} />}
                />
              </Descriptions.Item>
            ))}
            <Descriptions.Item
              label={
                <Typography.Text>
                  Price <Typography.Text type="danger">*</Typography.Text>
                </Typography.Text>
              }
            >
              <Controller
                name="price"
                render={({ field }) => <Input {...field} />}
              />
            </Descriptions.Item>
          </>
        ) : (
          <>
            <Descriptions.Item
              label={
                <Typography.Text>
                  Provider <Typography.Text type="danger">*</Typography.Text>
                </Typography.Text>
              }
            >
              <Controller
                name="provider"
                render={({ field }) => {
                  return <Input {...field} />
                }}
              />
            </Descriptions.Item>
            <Descriptions.Item
              label={
                <Typography.Text>
                  Region <Typography.Text type="danger">*</Typography.Text>
                </Typography.Text>
              }
            >
              <Controller
                name="region"
                render={({ field }) => <Input {...field} />}
              />
            </Descriptions.Item>

            {groupLabels.map((l) => (
              <Descriptions.Item key={l} label={l}>
                <Controller
                  name={`group_labels.${l}`}
                  render={({ field }) => <Input {...field} />}
                />
              </Descriptions.Item>
            ))}
          </>
        )}
      </Descriptions>

      <Descriptions>
        <Descriptions.Item label="Beta Price">
          <Group>
            <Checkbox
              value={isBetaPrice}
              onChange={(e) => setIsBetaPrice(e.target.checked)}
            />
            <Controller
              name="beta_end_time"
              render={({ field }) => (
                <DatePicker
                  {...field}
                  format={'YYYY-MM-DD'}
                  value={field.value}
                  onChange={(date) => {
                    field.onChange(date)
                  }}
                  disabled={!isBetaPrice}
                  disabledDate={disabeldDate}
                />
              )}
            />
            <Typography>(UTC+0)</Typography>
          </Group>
        </Descriptions.Item>
      </Descriptions>
    </>
  )
}

const normalizeUnitByGroupData = (
  data: FormsUnitByGroup,
  skuGroup: FormsSKUGroup
): FormsUnitByGroup => {
  if (!isManualPriceSKUGroup(skuGroup)) {
    const { price, unit_key_parameters, ...restData } = data

    return restData
  }

  const unitKeyParameters = extractPlaceholders(
    skuGroup.unit_key_pattern || ''
  ).reduce<NonNullable<FormsUnitByGroup['unit_key_parameters']>>((acc, key) => {
    const value = data.unit_key_parameters?.[key]?.trim()

    if (value) {
      acc[key] = value
    }

    return acc
  }, {})

  return {
    beta_end_time: data.beta_end_time,
    group_id: data.group_id,
    price: data.price?.trim(),
    unit_key_parameters: unitKeyParameters
  }
}

const validateUnitByGroupData = (
  data: FormsUnitByGroup,
  skuGroup: FormsSKUGroup
) => {
  if (!isManualPriceSKUGroup(skuGroup)) {
    if (!data.provider || !data.region) {
      message.error('Provider and Region are required.')

      return false
    }

    return true
  }

  const missingKeys = extractPlaceholders(
    skuGroup.unit_key_pattern || ''
  ).filter((key) => !data.unit_key_parameters?.[key])

  if (missingKeys.length > 0 || !data.price) {
    message.error(
      [...missingKeys, !data.price ? 'price' : ''].filter(Boolean).join(', ') +
        ' required.'
    )

    return false
  }

  return true
}

interface UnitListResultProps {
  data: FormsUnitInfo
}

const UnitListResult = ({ data }: UnitListResultProps) => {
  return (
    <>
      <Typography.Title level={5}>Dry run result</Typography.Title>

      <Descriptions column={1}>
        <Descriptions.Item label="Unit Key">{data.unit}</Descriptions.Item>
        <Descriptions.Item label="Price">{data.price}</Descriptions.Item>
        <Descriptions.Item label="Price Type">
          {data.price_type}
        </Descriptions.Item>
        <Descriptions.Item label="Beta Price">
          {!!data.beta_end_time ? 'Yes' : 'No'}
        </Descriptions.Item>
        {!!data.beta_end_time && (
          <Descriptions.Item label="Beta Expired At">
            {data.beta_end_time} (UTC+0)
          </Descriptions.Item>
        )}
      </Descriptions>
    </>
  )
}
