import React from 'react'
import PropTypes from 'prop-types'
import {
  A11yText,
  Divider,
  selectSystemProps,
  Typography,
  useThemeTokens,
  useViewport,
  getTokensPropType,
  a11yProps
} from '@telus-uds/components-base'
import styled from 'styled-components'
import FootnoteLink from '../Footnote/FootnoteLink'
import getTypographyTokens from './tokens'
import { htmlAttrs, warn } from '../utils'
import defaultDictionary from './dictionary'

const PRICE_BASELINE_VARIANT = 'baseline'

const [selectProps, selectedSystemPropTypes] = selectSystemProps([htmlAttrs, a11yProps])

const PriceLockupContainer = styled.div`
  align-items: ${({ alignItemsText }) => alignItemsText};
  display: flex;
  flex-direction: column;
  width: fit-content;
`
const PriceContainer = styled.div.attrs({
  'aria-hidden': 'true'
})`
  display: flex;
  margin-bottom: ${({ priceMarginBottom }) => priceMarginBottom};
`
const FootnoteContainer = styled.div`
  display: flex;
  margin-top: ${({ footnoteMarginTop }) => footnoteMarginTop};
  gap: ${({ footnoteGap }) => footnoteGap};
`

const BottomTextContainer = styled.div`
  margin-top: ${({ bottomTextMarginTop }) => bottomTextMarginTop};
`

const BottomLinksContainer = styled.div`
  align-self: center;
  margin-left: ${({ bottomLinksMarginLeft }) => bottomLinksMarginLeft};
`

const TopTextContainer = styled.div.attrs({
  'aria-hidden': 'true'
})`
  margin-bottom: ${({ topTextMarginBottom }) => topTextMarginBottom};
`

const PriceTextContainer = styled.div`
  display: flex;
  flex-direction: ${({ ratePosition }) => (ratePosition === 'bottom' ? 'column' : 'row')};
`

const RateContainer = styled.div`
  display: flex;
`

const RateTextContainer = styled.div`
  align-self: ${({ ratePosition }) => (ratePosition === 'bottom' ? 'flex-start' : 'flex-end')};
`

const StrikeThroughContainer = styled.div`
  display: flex;
  position: relative;
  align-items: center;
  ::before {
    content: '';
    width: 100%;
    top: ${({ strikeThroughPosition }) => `${strikeThroughPosition}px`};
    height: ${({ strikeThroughHeight }) => `${strikeThroughHeight}px`};
    background: ${({ strikeThroughColor }) => strikeThroughColor};
    position: absolute;
  }
`
const TypographyContainer = styled.div`
  display: flex;
  padding-top: ${({ paddingTop }) => `${paddingTop || 0}px`};
`
const InlineTextWithFootnote = styled.div`
  display: inline;
  position: relative;

  > *:first-child {
    display: inline;
  }

  > *:last-child {
    display: inline-block;
    position: relative;
    top: -0.5em;
    font-size: 0.75em;
    margin-left: 2px;
    line-height: 0;
    vertical-align: bottom;
  }
`

const selectFootnoteLinkStyles = ({
  footnoteLinkColor,
  footnoteLinkFontName,
  footnoteLinkFontWeight,
  footnoteLinkLineHeight
}) => {
  return {
    color: footnoteLinkColor,
    fontName: footnoteLinkFontName,
    fontWeight: footnoteLinkFontWeight,
    lineHeight: footnoteLinkLineHeight
  }
}

const selectStrikeThroughTokens = ({
  strikeThroughPosition,
  strikeThroughHeight,
  strikeThroughColor
}) => ({
  strikeThroughHeight,
  strikeThroughPosition,
  strikeThroughColor
})

const PriceLockup = React.forwardRef(
  (
    {
      size = 'medium',
      signDirection = 'left',
      footnoteLinks = [],
      topText,
      price,
      currencySymbol = '$',
      rateText,
      ratePosition = 'right',
      bottomText,
      onClickFootnote,
      strikeThrough,
      a11yText,
      linkPosition = 'default',
      tokens: priceLockupTokens,
      variant = {},
      copy = 'en',
      dictionary = defaultDictionary,
      ...rest
    },
    ref
  ) => {
    const viewport = useViewport()
    const {
      footnoteMarginTop,
      footnoteGap,
      bottomTextMarginTop,
      priceMarginBottom,
      bottomLinksMarginLeft,
      topTextMarginBottom,
      fontColor,
      dividerColor,
      footnoteLinkFontSize,
      alignItemsText,
      ...themeTokens
    } = useThemeTokens(
      'PriceLockup',
      priceLockupTokens,
      { ...variant, size },
      { viewport, strikeThrough }
    )

    const isPriceBaseline = variant?.price === PRICE_BASELINE_VARIANT

    const typographyTokens = getTypographyTokens(themeTokens)
    const priceString = String(price)
    const lastDotPosition = priceString.lastIndexOf('.')
    const lastCommaPosition = priceString.lastIndexOf(',')
    const [separator, separatorPosition] =
      lastDotPosition > lastCommaPosition ? ['.', lastDotPosition] : [',', lastCommaPosition]

    // If the separator is at the fourth character from the end of the string or more, it's most probably
    // a part of the amount, and the cents are not included in the price string
    const hasCents = separatorPosition !== -1 && separatorPosition >= priceString.length - 3
    const amount = hasCents ? priceString.substring(0, separatorPosition) : priceString
    const cents = hasCents ? priceString.substring(separatorPosition + 1) : null

    const renderTypography = (value, tokens, position) => {
      const { paddingTop, ...restOfTokens } = tokens
      const customProps =
        position === 'bottom'
          ? { variant: { size: 'micro' }, tokens: { color: fontColor } }
          : { tokens: { ...restOfTokens, color: fontColor } }

      return (
        <TypographyContainer paddingTop={paddingTop}>
          <Typography {...customProps}>{value}</Typography>
        </TypographyContainer>
      )
    }

    const renderCurrencySymbol = () =>
      renderTypography(`${currencySymbol}`, typographyTokens.dollarSign)

    const renderFootnoteLinks = () =>
      footnoteLinks && footnoteLinks.length > 0 ? (
        <FootnoteLink
          fontSize={footnoteLinkFontSize}
          tokens={selectFootnoteLinkStyles(themeTokens)}
          number={footnoteLinks}
          onClick={onClickFootnote}
        />
      ) : null

    const renderAmount = () => {
      const amountComponent = renderTypography(amount, typographyTokens.amount)
      if (strikeThrough) {
        return (
          <>
            <A11yText text={a11yText} />
            <StrikeThroughContainer {...selectStrikeThroughTokens(themeTokens)}>
              {amountComponent}
            </StrikeThroughContainer>
          </>
        )
      }

      return amountComponent
    }

    const renderPrice = () => (
      <>
        <PriceContainer priceMarginBottom={`${priceMarginBottom}px`}>
          <PriceTextContainer ratePosition={ratePosition}>
            <RateContainer>
              {signDirection === 'left' && renderCurrencySymbol()}
              {renderAmount()}
              {cents && renderTypography(`${separator}${cents}`, typographyTokens.cents)}
              {signDirection === 'right' && <>&nbsp;{renderCurrencySymbol()}</>}
            </RateContainer>
            {rateText && (
              <RateTextContainer ratePosition={ratePosition}>
                {renderTypography(rateText, typographyTokens.rate, ratePosition)}
              </RateTextContainer>
            )}
          </PriceTextContainer>

          {!bottomText && footnoteLinks.length <= 3 && (
            <BottomLinksContainer bottomLinksMarginLeft={`${bottomLinksMarginLeft}px`}>
              {renderFootnoteLinks()}
            </BottomLinksContainer>
          )}
        </PriceContainer>
        {!bottomText && footnoteLinks.length > 3 && renderFootnoteLinks()}
      </>
    )

    const renderFootnoteContent = () => {
      const containerProps = {
        footnoteMarginTop: `${footnoteMarginTop}px`,
        footnoteGap: `${footnoteGap}px`
      }

      const shouldUseInline = linkPosition === 'inline'

      const bottomTextContent = shouldUseInline ? (
        <InlineTextWithFootnote>
          <Typography tokens={typographyTokens.bottomText} as="span">
            {bottomText}
          </Typography>
          {renderFootnoteLinks()}
        </InlineTextWithFootnote>
      ) : (
        renderTypography(bottomText, typographyTokens.bottomText)
      )

      const showFootnotesInContainer = !shouldUseInline && footnoteLinks.length <= 3
      const showFootnotesOutside = !shouldUseInline && footnoteLinks.length > 3

      return (
        <>
          <FootnoteContainer {...containerProps}>
            <BottomTextContainer bottomTextMarginTop={`${bottomTextMarginTop}px`}>
              {bottomTextContent}
            </BottomTextContainer>
            {showFootnotesInContainer && renderFootnoteLinks()}
          </FootnoteContainer>
          {showFootnotesOutside && renderFootnoteLinks()}
        </>
      )
    }

    if (strikeThrough && !a11yText) {
      warn('PriceLockup', 'a11yText must be provided with strikethrough pricing')
    }

    /**
     * Converts rateText to string if rateText is an JSX.
     */
    function getRateText(rateTextValue) {
      if (!rateTextValue) return ''

      switch (typeof rateTextValue) {
        case 'string':
          return rateTextValue.replace('/', '')

        case 'object':
          if (React.isValidElement(rateTextValue)) {
            return React.Children.toArray(rateTextValue.props.children).map(getRateText).join('')
          }
          // If it's an object but not a React element, return empty string
          break

        default:
          break
      }

      return ''
    }

    const getAriaContent = () => {
      const {
        price: dictionaryPrice,
        priceWithCents,
        priceLeft,
        priceWithCentsLeft,
        rate,
        rateOnly
      } = dictionary[copy]

      const priceTemplate = isPriceBaseline ? priceLeft : dictionaryPrice
      const priceWithCentsTemplate = isPriceBaseline ? priceWithCentsLeft : priceWithCents

      let ariaLabel = hasCents
        ? priceWithCentsTemplate
            ?.replace('%{amount}', amount)
            .replace('%{separator}', separator)
            .replace('%{cents}', cents)
            .replace('%{currency}', currencySymbol)
        : priceTemplate?.replace('%{amount}', amount).replace('%{currency}', currencySymbol)

      if (!ariaLabel?.includes(currencySymbol)) {
        ariaLabel = `${ariaLabel} ${currencySymbol}`
      }

      if (rateText) {
        if (isPriceBaseline) {
          ariaLabel += ` ${rateOnly.replace('%{rateText}', getRateText(rateText))}`
        } else {
          ariaLabel += ` ${rate.replace('%{rateText}', getRateText(rateText))}`
        }
      }
      return ariaLabel
    }

    const ariaLabel = selectProps(rest)['aria-label'] ?? getAriaContent()

    return (
      <PriceLockupContainer
        {...selectProps(rest)}
        alignItemsText={alignItemsText}
        ref={ref}
        role="group"
        aria-label={ariaLabel}
      >
        {topText && (
          <TopTextContainer topTextMarginBottom={`${topTextMarginBottom}px`}>
            {renderTypography(topText, typographyTokens.topText)}
          </TopTextContainer>
        )}
        {renderPrice()}
        {bottomText && (
          <Divider
            testID="price-lockup-divider"
            role="separator"
            tokens={{ color: dividerColor ?? 'rgba(0, 0, 0, 0)' }}
          />
        )}
        {bottomText && renderFootnoteContent()}
      </PriceLockupContainer>
    )
  }
)

PriceLockup.displayName = 'PriceLockup'

// If a language dictionary entry is provided, it must contain every key
const dictionaryContentShape = PropTypes.shape({
  price: PropTypes.string.isRequired,
  priceWithCents: PropTypes.string.isRequired,
  priceLeft: PropTypes.string.isRequired,
  priceWithCentsLeft: PropTypes.string.isRequired,
  rate: PropTypes.string.isRequired,
  rateOnly: PropTypes.string.isRequired
})

PriceLockup.propTypes = {
  ...selectedSystemPropTypes,
  /**
   * Size of the component
   *
   * Micro for mini cart pricing, small for pricing in product catalogue pages, medium for pricing in product comparison cards and large for pricing in banners and promo cards
   */
  size: PropTypes.oneOf(['micro', 'small', 'medium', 'large']),
  /**
   * If currency symbol other than `$` to be used
   */
  currencySymbol: PropTypes.string,
  /**
   * Shows additional info above the price
   */
  topText: PropTypes.string,
  /**
   * Monetary value (including decimals separated by ".")
   */
  price: PropTypes.string.isRequired,
  /**
   * Shows month/year unit
   */
  rateText: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
  /**
   * Shows additional info below the price with a `Divider`
   */
  bottomText: PropTypes.string,
  /**
   * Displays which side the currency should apperar (left, right)
   */
  signDirection: PropTypes.oneOf(['right', 'left']),
  /**
   * Displays where the rate should apperar (bottom, right)
   */
  ratePosition: PropTypes.oneOf(['right', 'bottom']),
  /**
   * Shows additional link for context
   */
  footnoteLinks: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.number, PropTypes.string])),
  /**
   * Function to be called when a footnote link is clicked
   */
  onClickFootnote: PropTypes.func,
  /**
   * To show price savings comparison
   */
  strikeThrough: PropTypes.bool,
  /**
   * To provide a11y text for `PriceLockup` component
   *
   * **Note:** a11yText will override strikethrough price, so it must include price (ie. "was 50 dollars per month")
   */
  a11yText: PropTypes.string,
  /**
   * Select English or French copy for the accessible label.
   */
  copy: PropTypes.oneOf(['en', 'fr']),
  /**
   * Controls footnote link positioning when bottomText is present
   *
   * - 'default': Footnote appears next to the text container (current behavior)
   * - 'inline': Footnote appears inline at the end of the text
   */
  linkPosition: PropTypes.oneOf(['default', 'inline']),
  /* Custom dictionary containing the labels
   */
  dictionary: PropTypes.shape({
    en: dictionaryContentShape,
    fr: dictionaryContentShape
  }),
  /**
   * `PriceLockup` tokens
   */
  tokens: getTokensPropType('PriceLockup')
}

export default PriceLockup
