import moment from 'moment'
import React from 'react'
import { NumericFormat } from 'react-number-format'

import Badge from '../Badge/Badge'
import { type BadgeProps } from '../Badge/Badge'
import Button from '../Button/Button'
import Checkbox from '../Form/Checkbox'
import { hasValue, largeNumConversion } from '../../services/HelperServiceTyped'
import Icon from '../Icons/Icon'
import { type IconStringList } from '../Icons/Icon.models'
import Mdash from '../Mdash/Mdash'
import MdashCheck from '../Mdash/MdashCheck'
import PercentageCheck from '../PercentageCheck/PercentageCheck'
import Tooltip from '../Tooltip/Tooltip'
import { type TooltipProps } from '../Tooltip/Tooltip'
import { useMediaQuery } from '../../hooks/responsiveHooks'
import {
  getComparisonDates,
  isCurrentDateUsedForComparison,
} from '../../services/DateHelpers'
import styles from './header_metric_group.module.scss'
import { type chartColors } from './HeaderMetricGroup'
import {
  type CompareWith,
  type DateType,
  determineChangeValue,
  formatDates,
  getPartialPeriodDates,
  getPeriodDisplays,
  getPreviousPeriodRange,
  type TimeframeType,
} from './HeaderMetricHelpers'
import { Skeleton } from '../Skeleton/Skeleton'
import { c } from '../../translations/LibraryTranslationService'

type HeaderMetricBase = {
  /** The key override for the metric - if it needs to be different than `title` */
  key?: string
  /** Text to display in metric */
  title: string
  /** Tooltip content to display next to title in metric */
  tooltip?: TooltipProps['tooltipContent']
  // TODO: We need to deprecate these font size values in favor of tailwind values.
  /** The font size that will change the main data of the metric. */
  fontSize?: 'fs-16' | 'fs-18' | 'fs-22'
  /** Boolean to determine loading state */
  loading: boolean
  /** Optional className can be added */
  className?: string
  /** Optionally move the change metrics below the main metric. */
  showChangeBelowMainMetric?: boolean
  /** Option to not multiple pctChange value by 100 (default: false) */
  noPctConversion?: boolean
  /** Optional boolean to display beta tag in metric */
  beta?: boolean
  /** Optional props to pass to the Badge component. If provided, this will be used instead of the default beta badge */
  badgeProps?: BadgeProps
  /** Optional prop to add a test id to the HeaderMetric for QA testing */
  qaTestId?: string
}

type HeaderMetricWithLink = HeaderMetricBase & {
  /** Link to display in metric, internal only */
  linkTo?: string
  /** Router component (e.g. Using Link from react-router-dom) */
  routerComponent?: React.ElementType
  /** Allow any props to be passed in */
  routerProp?: string
  actionButton?: never
}

type HeaderMetricWithActionButton = HeaderMetricBase & {
  /** Link to display in metric, internal only */
  linkTo?: never
  /** Action button to display in metric */
  actionButton?: React.JSX.Element
  routerComponent?: never
  routerProp?: never
}

type HeaderMetricBaseProps = HeaderMetricWithActionButton | HeaderMetricWithLink

type HeaderMetricValueType = HeaderMetricBaseProps & {
  /** Value to display in metric */
  value?: number
  /** Change value to display in metric */
  change?: number
  /** Percentage change to display in metric */
  pctChange?: number
  /** Currency to display in metric */
  currency?: CurrencyProps
  /** Value format to display in metric */
  formatType?: 'number' | 'percentage' // TODO: add currency type and refactor types
  /** Metric icon to display in metric */
  metricIcon?: IconStringList
  /** Decimal scale applied to value in metric */
  decimalScale?: number
  /** Decimal scale applied to change value in metric */
  changeValueDecimalScale?: number
  /** When we have a percentage value we should use this if we want to round that percentage value */
  roundNumber?: boolean
  /** Reverse the change value display */
  reverse?: boolean
  /** Allow the radio button to be displayed */
  showRadio?: boolean
  /** When showRadio is true, use this prop to handle the function that needs to be called when the radio is clicked */
  callout?: ({ ...param }: CalloutProps) => void
  /** This will set the metric number and checkbox color from list of chartColors */
  checkboxColor?: chartColors
  /** When true, this will set the change value, and change icon to our purple color */
  isNeutralColor?: boolean
  /** Checked state override of the metric radio button by default */
  isChecked?: boolean
  /** Optional className can be added to the metric value */
  metricValueClassName?: string
  /** Display values in a truncated format. i.e. 1.4M instead of 1,400,000 */
  truncateValues?: boolean
  /** Displays the full percentage change value if set to true. Otherwise, it will display `<1%` if set to false */
  showLessThanZeroPercentageChange?: boolean
  /** There are some instances where a custom element needs to replace the percentage values. This is where that should live. */
  customSecondaryValue?: React.ReactNode
  secondaryInfo?: never
  /** Information needed to display comparison tooltip */
  comparisonTooltip?: {
    /** Optional title to display for the primary metric value on comparison tooltip */
    primaryMetricTitle?: string
    /** The value to display from the comparison period */
    comparison: number
    /** The value to display for the whole comparison period */
    wholePreviousPeriodComparison?: number
    /** The selected date range from the current period */
    currentPeriodDates: DateType
    /** The timeframe object needed for the display text and comparison dates */
    timeframe: TimeframeType
    /** Optional param that will be used for showing compareWith global timeframe filters */
    compareWith?: CompareWith
    /** Optional param to remove current month for comparison if the current month has no data and the timeframe is quarterly/yearly */
    hasNoCurrentMonthData?: boolean
    /** Optional props to show partial period dates */
    showPartialPeriodDates?: boolean
    /** Optional param to show footer content in the tooltip */
    footerContent?: React.ReactNode
  }
  /** Change value format to display in metric */
  changeValueFormatType?: 'number' | 'percentage'
}

type HeaderMetricTextType = HeaderMetricBaseProps & {
  /** Any value apart from change metrics. Eg: string, text */
  secondaryInfo: string
  value?: never
  change?: never
  pctChange?: never
  currency?: never
  formatType?: never
  metricIcon?: never
  decimalScale?: never
  changeValueDecimalScale?: never
  roundNumber?: never
  reverse?: never
  showRadio?: never
  callout?: never
  checkboxColor?: never
  isNeutralColor?: never
  isChecked?: never
  metricValueClassName?: never
  truncateValues?: never
  showLessThanZeroPercentageChange?: never
  customSecondaryValue?: never
  comparisonTooltip?: never
  changeValueFormatType?: 'number' | 'percentage'
}

export type HeaderMetricProps = HeaderMetricValueType | HeaderMetricTextType

export type CalloutProps = {
  /** When showRadio is true, use this prop to the function that needs to be called when the radio is clicked */
  name: string
  checked: boolean
}
export type CurrencyProps = {
  /** Currency code to display after metric (ex: 100฿) */
  currencyCode: string
  /** Currency symbol to display before metric (ex: $100) */
  currencySymbol: string
}
const HeaderMetric = ({
  actionButton,
  beta,
  badgeProps,
  callout,
  change,
  checkboxColor = 'purple',
  className = '',
  currency,
  customSecondaryValue,
  decimalScale = 0,
  changeValueDecimalScale,
  fontSize,
  formatType = 'number',
  isChecked = false,
  isNeutralColor = false,
  linkTo,
  loading,
  metricIcon,
  metricValueClassName = '',
  noPctConversion = false,
  pctChange = 0,
  reverse = false,
  roundNumber = false,
  secondaryInfo,
  showLessThanZeroPercentageChange = true,
  showRadio = false,
  title,
  tooltip,
  truncateValues,
  value,
  showChangeBelowMainMetric = false,
  comparisonTooltip,
  routerComponent,
  routerProp = 'to',
  changeValueFormatType,
  qaTestId = 'header-metric',
}: HeaderMetricProps): React.JSX.Element => {
  const screenIsMdMax = useMediaQuery({ type: 'max', breakpoint: 'md' })
  const screenIsXlMax = useMediaQuery({ type: 'max', breakpoint: 'xl' })
  const changeValueFormatTypeDefault =
    changeValueFormatType ?? formatType ?? 'number'

  const RouterComponent = routerComponent

  const {
    primaryMetricTitle,
    comparison,
    currentPeriodDates,
    timeframe,
    compareWith,
    wholePreviousPeriodComparison,
    hasNoCurrentMonthData,
    showPartialPeriodDates,
    footerContent,
  } = comparisonTooltip || {}

  const isLargeNum = (value?: number) => value && value >= 1000

  // This condition is to check if we are using compareWith filter and quarter/year is of current period and if so, use current date for endDate
  if (
    isCurrentDateUsedForComparison(timeframe, compareWith) &&
    currentPeriodDates?.endDate &&
    !hasNoCurrentMonthData
  ) {
    currentPeriodDates.endDate = moment().format()
  }

  const currencySuffix =
    currency?.currencyCode && currency?.currencyCode !== 'USD'
      ? ` ${currency?.currencyCode}`
      : undefined
  const metricValue = truncateValues ? largeNumConversion(value).val : value
  const metricChangeValue = truncateValues
    ? largeNumConversion(change).val
    : change
  const valueSuffix = truncateValues ? largeNumConversion(value).suffix : null
  const changeSuffix = truncateValues ? largeNumConversion(change).suffix : null
  const metricValueSuffix = `${valueSuffix ? `${valueSuffix} ` : ''}${
    currencySuffix ? currencySuffix : ''
  }`
  const metricChangeSuffix = `${changeSuffix ? `${changeSuffix} ` : ''}${
    currencySuffix ? currencySuffix : ''
  }`
  const largeNumDecimalScale =
    truncateValues && value && value > 1.0e6 ? 2 : decimalScale
  const largeNumChangeDecimalScale =
    (truncateValues && change && change > 1.0e6) ||
    changeValueFormatTypeDefault === 'percentage'
      ? 2
      : currency?.currencyCode
        ? isLargeNum(change)
          ? 0
          : 2
        : (changeValueDecimalScale ?? decimalScale)

  const showChangeValue = (change: number) => {
    const value =
      metricChangeValue && changeValueFormatTypeDefault === 'percentage'
        ? metricChangeValue * 100
        : metricChangeValue

    const {
      textClassName: textClassNameChange,
      iconColor: iconColorChange,
      icon: iconChange,
    } = determineChangeValue(change, reverse, isNeutralColor)

    return (
      <div className={styles.changeMetric}>
        <MdashCheck check={!!hasValue(value)}>
          <NumericFormat
            qa-test-id={`${qaTestId}-change-value`}
            value={Math.abs(Number(value))}
            displayType='text'
            decimalScale={largeNumChangeDecimalScale}
            fixedDecimalScale={
              changeValueFormatTypeDefault === 'percentage' ||
              !!currency?.currencyCode ||
              !!(changeValueDecimalScale ?? decimalScale)
            }
            thousandSeparator
            prefix={currency?.currencySymbol}
            suffix={metricChangeSuffix}
            className={textClassNameChange}
          />
        </MdashCheck>

        <div className={styles.divider}></div>

        <MdashCheck check={!!hasValue(pctChange)}>
          <span className={textClassNameChange}>
            <PercentageCheck
              percent={Math.abs(Number(pctChange))}
              roundNumber={false}
              decimalScale={2}
              showLessThanZero={showLessThanZeroPercentageChange}
              noConversion={noPctConversion}
              qaTestId={`${qaTestId}-change-percentage`}
            />
          </span>
        </MdashCheck>

        <MdashCheck check={!!hasValue(change)}>
          <Icon icon={iconChange} iconSize='12px' color={iconColorChange} />
        </MdashCheck>
      </div>
    )
  }

  const tooltipContent = () => {
    if (!comparisonTooltip || !change || !currentPeriodDates || !timeframe)
      return null
    const comparisonPeriodDates = getComparisonDates({
      startDate: currentPeriodDates.startDate,
      endDate: currentPeriodDates.endDate,
      timeframe,
      compareWith: compareWith ?? undefined,
      hasNoCurrentMonthData: hasNoCurrentMonthData,
    })
    const currentPeriod = getPeriodDisplays({
      timeframe: timeframe,
      column: 1,
    })
    const comparisonPeriod = getPeriodDisplays({
      timeframe: timeframe,
      column: 2,
      compareWith: compareWith,
    })

    const {
      textClassName: newChangeTextClassName,
      iconColor: changeIconColor,
      icon: newChangeIcon,
    } = determineChangeValue(change, reverse, isNeutralColor)
    const {
      textClassName: newPctChangeTextClassName,
      iconColor: newPctIconColor,
      icon: newPctChangeIcon,
    } = determineChangeValue(pctChange, reverse, isNeutralColor)

    let currentPeriodDatesText = ''
    let comparisonPeriodDatesText = ''

    if (
      (timeframe.type === 'historical' || timeframe.type === 'trailing') &&
      timeframe.value === 'custom' &&
      timeframe.compareDisplay
    ) {
      // Updates partial period dates to show the custom date range
      currentPeriodDatesText = timeframe.display
      comparisonPeriodDatesText = timeframe.compareDisplay
    } else {
      // Updates partial period dates to show the current and comparison period dates
      const partialPeriodDates = getPartialPeriodDates(
        currentPeriodDates,
        comparisonPeriodDates,
      )
      currentPeriodDatesText = partialPeriodDates.currentDate
      comparisonPeriodDatesText = partialPeriodDates.comparisonDate
    }

    const dateText = () => {
      if (
        (timeframe.type === 'historical' || timeframe.type === 'trailing') &&
        timeframe.value === 'custom' &&
        timeframe.compareDisplay
      ) {
        return c('timeframeVsCompareTimeframe', {
          timeframeDisplay: timeframe.display,
          compareTimeframeDisplay: timeframe.compareDisplay,
        })
      } else {
        return formatDates(currentPeriodDates, comparisonPeriodDates)
      }
    }

    const prevPeriodDisplayText = getPreviousPeriodRange(
      timeframe,
      currentPeriodDates.startDate,
      currentPeriodDates.endDate,
      compareWith,
    )

    return (
      <div className={styles.tooltipContentContainer}>
        <span className={styles.periodContainer}>
          {currentPeriod} vs {comparisonPeriod}
        </span>
        <div className={styles.subTitle}>
          {showPartialPeriodDates ? (
            <span
              className={styles.partialPeriodLabelWrap}
            >{`* ${c('partialPeriod')}`}</span>
          ) : (
            dateText()
          )}
        </div>
        <div className={styles.tooltipMetricRow}>
          <div className={styles.partialPeriodContainer}>
            <span>{primaryMetricTitle ?? title}</span>
            {showPartialPeriodDates ? (
              <div className={styles.partialPeriodDateWrap}>
                {currentPeriodDatesText}
              </div>
            ) : null}
          </div>
          {formatType === 'percentage' ? (
            <PercentageCheck
              percent={Math.abs(Number(value))}
              roundNumber={false}
              decimalScale={2}
              showLessThanZero
              noConversion={noPctConversion}
            />
          ) : (
            <NumericFormat
              value={value}
              displayType='text'
              decimalScale={
                currency?.currencyCode
                  ? isLargeNum(value)
                    ? 0
                    : 2
                  : decimalScale
              }
              fixedDecimalScale={currency?.currencyCode ? true : !!decimalScale}
              thousandSeparator
              prefix={currency?.currencySymbol}
              suffix={currencySuffix ? currencySuffix : ''}
            />
          )}
        </div>

        <div className={styles.tooltipMetricRow}>
          <div
            className={
              showPartialPeriodDates ? styles.partialPeriodContainer : ''
            }
          >
            <span>{c('comparisonPeriod')}</span>
            {showPartialPeriodDates ? (
              <div className={styles.partialPeriodDateWrap}>
                {comparisonPeriodDatesText}
              </div>
            ) : null}
          </div>
          <MdashCheck check={hasValue(comparison)}>
            {formatType === 'percentage' ? (
              <PercentageCheck
                percent={Math.abs(Number(comparison))}
                decimalScale={2}
                roundNumber={false}
                showLessThanZero
                noConversion={noPctConversion}
              />
            ) : (
              <NumericFormat
                value={comparison}
                displayType='text'
                decimalScale={
                  currency?.currencyCode
                    ? isLargeNum(comparison)
                      ? 0
                      : 2
                    : decimalScale
                }
                fixedDecimalScale={
                  currency?.currencyCode ? true : !!decimalScale
                }
                thousandSeparator
                prefix={currency?.currencySymbol}
                suffix={currencySuffix ? currencySuffix : ''}
              />
            )}
          </MdashCheck>
        </div>

        <div className={styles.tooltipMetricRow}>
          <span>{c('changeValueVsComparison')}</span>
          <MdashCheck check={!!hasValue(change)}>
            <div className={styles.flex}>
              <NumericFormat
                value={Math.abs(
                  formatType === 'percentage' ? change * 100 : change,
                )}
                displayType='text'
                decimalScale={
                  !isLargeNum(change) &&
                  (formatType === 'percentage' || !!currency?.currencyCode)
                    ? 2
                    : decimalScale
                }
                fixedDecimalScale={
                  formatType === 'percentage' ||
                  !!currency?.currencyCode ||
                  !!decimalScale
                }
                thousandSeparator
                prefix={currency?.currencySymbol}
                suffix={currencySuffix ? currencySuffix : ''}
                className={`${newChangeTextClassName} ${styles.rightMargin}`}
              />
              <Icon
                icon={newChangeIcon}
                iconSize='12px'
                color={changeIconColor}
              />
            </div>
          </MdashCheck>
        </div>

        <div className={styles.tooltipMetricRow}>
          <span>{c('change%VsComparison')}</span>
          <MdashCheck check={!!hasValue(pctChange)}>
            <div className={styles.flex}>
              <span
                className={`${newPctChangeTextClassName} ${styles.rightMargin}`}
              >
                {newPctChangeIcon !== 'trendEven' && (
                  <PercentageCheck
                    percent={Math.abs(Number(pctChange))}
                    roundNumber={false}
                    decimalScale={2}
                    showLessThanZero
                    noConversion={noPctConversion}
                  />
                )}
              </span>

              <Icon
                icon={newPctChangeIcon}
                iconSize='12px'
                color={newPctIconColor}
              />
            </div>
          </MdashCheck>
        </div>

        {prevPeriodDisplayText &&
        wholePreviousPeriodComparison &&
        showPartialPeriodDates ? (
          <div className={styles.tooltipMetricRow}>
            <div className={styles.partialPeriodContainer}>
              <span>{comparisonPeriod}</span>
              <div className={styles.partialPeriodDateWrap}>
                {prevPeriodDisplayText}
              </div>
            </div>
            <MdashCheck check={hasValue(wholePreviousPeriodComparison)}>
              {formatType === 'percentage' ? (
                <PercentageCheck
                  percent={Math.abs(Number(wholePreviousPeriodComparison))}
                  decimalScale={2}
                  roundNumber={false}
                  showLessThanZero
                  noConversion={noPctConversion}
                />
              ) : (
                <NumericFormat
                  value={wholePreviousPeriodComparison}
                  displayType='text'
                  decimalScale={
                    currency?.currencyCode
                      ? isLargeNum(wholePreviousPeriodComparison)
                        ? 0
                        : 2
                      : decimalScale
                  }
                  fixedDecimalScale={
                    currency?.currencyCode ? true : !!decimalScale
                  }
                  thousandSeparator
                  prefix={currency?.currencySymbol}
                  suffix={currencySuffix ? currencySuffix : ''}
                />
              )}
            </MdashCheck>
          </div>
        ) : null}

        {footerContent ? (
          <div className={styles.comparisonFooterText}>{footerContent}</div>
        ) : null}
      </div>
    )
  }

  const metricDisplay = () => {
    return secondaryInfo ? (
      <MdashCheck
        check={!!hasValue(secondaryInfo) && !loading}
        customClass={`${
          fontSize ? fontSize : screenIsMdMax ? styles.fs16 : styles.fs18
        }`}
      >
        {secondaryInfo}
      </MdashCheck>
    ) : (
      <Tooltip
        position='bottom'
        className={
          !comparisonTooltip ||
          !change ||
          !currentPeriodDates ||
          !timeframe ||
          !hasValue(metricValue) ||
          !hasValue(value) ||
          loading
            ? styles.none
            : ''
        }
        tooltipContent={tooltipContent()}
        maxWidth='400px'
      >
        <div className={styles.metricContainer}>
          <div className={styles.metricIconAndMainMetric}>
            {metricIcon && <Icon icon={metricIcon} iconSize='12px' />}
            <MdashCheck
              check={!!(hasValue(metricValue) || hasValue(value)) && !loading}
            >
              <div
                className={`${
                  fontSize
                    ? fontSize
                    : screenIsMdMax
                      ? styles.fs16
                      : styles.fs18
                } ${
                  showRadio
                    ? isChecked
                      ? styles.fwSemibold
                      : ''
                    : metricValueClassName
                } `}
              >
                {formatType === 'percentage' ? (
                  <PercentageCheck
                    percent={Math.abs(Number(value))}
                    roundNumber={roundNumber}
                    decimalScale={decimalScale}
                    showLessThanZero
                    noConversion={noPctConversion}
                    qaTestId={`${qaTestId}-primary-metric`}
                  />
                ) : (
                  <NumericFormat
                    value={metricValue}
                    displayType='text'
                    decimalScale={largeNumDecimalScale}
                    fixedDecimalScale={
                      !!currency?.currencyCode || !!decimalScale
                    }
                    thousandSeparator
                    prefix={currency?.currencySymbol}
                    suffix={metricValueSuffix}
                    qa-test-id={`${qaTestId}-primary-metric`}
                  />
                )}
              </div>
            </MdashCheck>
            {!showChangeBelowMainMetric ? <ChangeDisplay /> : null}
          </div>
          {showChangeBelowMainMetric ? <ChangeDisplay /> : null}
        </div>
      </Tooltip>
    )
  }

  const ChangeDisplay = () => {
    if (change === undefined) return <></>
    return (
      <div>
        {!loading &&
          (change !== undefined && !!hasValue(pctChange) ? (
            customSecondaryValue ? (
              customSecondaryValue
            ) : (
              showChangeValue(change)
            )
          ) : (
            <div className={styles.noChangeValue}>
              <Mdash />
              <Tooltip tooltipContent={c('tooltipNoComparisonValues')}>
                <Icon icon='info' color='dark-blue' iconSize='12px' />
              </Tooltip>
            </div>
          ))}
      </div>
    )
  }

  /** Radio button handler */
  const inputHandler = () => {
    callout?.({ name: title, checked: !isChecked })
  }

  const mainAction = () => {
    return (
      <div
        className={
          linkTo
            ? styles.mainActionContainerWithLink
            : styles.mainActionContainerWithoutLink
        }
      >
        <Button
          as='unstyled'
          onClick={inputHandler}
          className={!showRadio ? styles.noCursor : ''}
        >
          <div className={styles.mainAction} style={{ flex: '1 1 100%' }}>
            <div className={styles.labelContainer}>
              <div
                qa-test-id={title ?? `${qaTestId}-title`}
                className={styles.fs12}
              >
                {title ?? c('noDataAvailable')}
              </div>
              {badgeProps ? (
                <Badge {...badgeProps} />
              ) : beta ? (
                <Badge color='lavender'>Beta</Badge>
              ) : null}
              {tooltip ? (
                <Tooltip position='top' tooltipContent={tooltip}>
                  <Icon icon='info' color='dark-blue' iconSize='12px' />
                </Tooltip>
              ) : null}
            </div>
            {metricDisplay()}
          </div>
        </Button>
      </div>
    )
  }

  const routerComponentProps =
    routerProp && linkTo
      ? {
          [routerProp]: linkTo,
        }
      : {}

  return (
    <div
      className={`${styles.headerMetricContainer} ${className}`}
      style={{
        flex: screenIsMdMax
          ? '1 1 100%'
          : screenIsXlMax
            ? '1 1 30.11%'
            : '1 1 14.66%',
      }}
      qa-test-id={qaTestId}
    >
      {loading ? (
        <Skeleton.HeaderMetric />
      ) : (
        <div
          className={styles.mainActionContainer}
          style={{ flex: '1 1 100%' }}
        >
          <div className={styles.statsContainer}>
            {showRadio && (
              <div style={{ marginTop: '2px' }}>
                <Checkbox
                  name={title}
                  stateName={title}
                  checked={isChecked}
                  type='radio'
                  callout={inputHandler}
                  label={title}
                  hideLabel
                  radioSize={'10'}
                  checkboxColor={checkboxColor}
                  disabled={loading}
                />
              </div>
            )}
            {mainAction()}
          </div>

          {/* actionButton and linkTo are mutually exclusive */}
          {actionButton && !linkTo && actionButton}
          {linkTo && !actionButton && routerProp && RouterComponent && (
            <RouterComponent {...routerComponentProps}>
              <Icon icon='right' color='dark-blue' iconSize='16px' />
            </RouterComponent>
          )}
        </div>
      )}
    </div>
  )
}

export default HeaderMetric
