import React from 'react'
import PropTypes from 'prop-types'
import {
  Card as CardBase,
  getTokensPropType,
  paddingProp,
  responsiveProps,
  selectSystemProps,
  useThemeTokens,
  useThemeTokensCallback,
  variantProp,
  a11yProps,
  viewProps,
  useResponsiveProp,
  hrefAttrsProp,
  useAllViewportTokens
} from '@telus-uds/components-base'
import { CardFooter } from './CardFooter'
import {
  FullBleedContent,
  getFullBleedBorderRadius,
  useFullBleedContentProps
} from '../shared/FullBleedContent'
import { SelectionContent } from './SelectionContent'
import { InteractiveBody } from './InteractiveBody'
import { FullBleedInteractiveLayout } from './FullBleedInteractiveLayout'
import { FullBleedLayout } from './FullBleedLayout'
import { DefaultContent } from './DefaultContent'
import { calculateFullBleedPadding, GRID_COLUMNS, POSITION } from './helpers'

// Passes React Native-oriented system props through UDS Card
const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])

const PADDING_KEYS = ['paddingTop', 'paddingBottom', 'paddingLeft', 'paddingRight']

const isOverlayColor = (color) => color && typeof color === 'string' && color.startsWith('rgba(')

const getInteractiveCardTokensForPressable = (interactiveCard, interactiveCardSolidBg) => {
  if (!interactiveCardSolidBg) return interactiveCard?.tokens
  return Object.fromEntries(
    Object.entries(interactiveCard?.tokens || {}).filter(([key]) => key !== 'backgroundColor')
  )
}

const getCardBaseTokens = (tokens, { interactiveCardSolidBg, userSolidBg } = {}) => {
  const filtered = Object.fromEntries(
    Object.entries(tokens).filter(([key]) => !PADDING_KEYS.includes(key))
  )
  const hasSolidBg = Boolean(interactiveCardSolidBg || userSolidBg)
  return {
    ...filtered,
    ...(interactiveCardSolidBg && { backgroundColor: interactiveCardSolidBg }),
    ...(hasSolidBg && { backgroundGradient: null })
  }
}

/**
 * A basic card component, unstyled by default.
 *
 * ## Render modes and composition
 *
 * Card supports five render modes (default, selection, interactive body, full-bleed
 * layout, and full-bleed interactive) and stacks its visual props in a predictable order.
 *
 * ## Component API
 *
 * ### With Footer
 *
 * Pass any component or simply a string in the `footer` prop in order to render
 * a card with a footer (which uses the `feature2` style).
 *
 * ### With Full Bleed Content
 *
 * Use `fullBleedContent` prop to add a full bleed style image, video or other content to the
 * card. This prop accepts an object with the following properties:
 * - `alt`: alt tag for an image,
 * - `src`: default image source,
 * - `position`: `none`, `bottom`, `left`, `right` or `top`, depending on where you would like your full bleed image to be placed,
 * - `imgCol`: set the span of the image per viewport (based on 12 column value for width of card) when `position` of image is on `left` or `right`
 * - all the props from the `ResponsiveImage` component in case you want that full bleed image to be responsive,
 * - `content`: pass a custom JSX to be used for rendering of the full bleed content (defaults to `ResponsiveImage`
 *   receiving the other props).
 *
 * Note that `position` can be responsive, i.e. different for different viewports. A full bleed content with position
 * {xs: 'none', md: 'left'} for example, will have a full bleed content to the left of card content when viewed on desktops
 * viewports, and no content when viewed on mobile viewports.
 *
 * ## Accessibility
 * `Card` component accepts all the standard accessibility props.
 */
export const Card = React.forwardRef(
  (
    {
      children,
      footer,
      footerPadding,
      fullBleedImage,
      fullBleedContent = fullBleedImage,
      fullBleedPadding,
      tokens = {},
      variant,
      interactiveCard,
      onPress,
      dataSet,
      backgroundImage,
      testID,
      ...rest
    } = {
      fullBleedContent: { position: null }
    },
    ref
  ) => {
    const { hrefAttrs: cardLevelHrefAttrs, rest: restWithoutHrefAttrs } = hrefAttrsProp.bundle(rest)
    const { href: cardLevelHref, ...restProps } = restWithoutHrefAttrs

    const {
      contentStackAlign,
      contentStackDirection,
      fullBleedContentPosition,
      fullBleedContentProps,
      fullBleedContentChildrenAlign,
      fullBleedPaddingSides
    } = useFullBleedContentProps(fullBleedContent)

    const {
      imgCol,
      interactive: fullBleedInteractive,
      onPress: fullBleedOnPress,
      href: fullBleedHref,
      hrefAttrs: fullBleedHrefAttrs,
      ...fullBleedContentPropsClean
    } = fullBleedContentProps

    const effectiveFullBleedOnPress = fullBleedOnPress || onPress
    const effectiveFullBleedHref = fullBleedHref || cardLevelHref
    const effectiveFullBleedHrefAttrs = fullBleedHrefAttrs || cardLevelHrefAttrs

    const allThemeTokens = useThemeTokens('Card', tokens, variant)
    const { borderRadius } = allThemeTokens

    // Get viewport-aware tokens for responsive full bleed padding
    // useThemeTokens (above) doesn't pass viewport state, so it resolves to base/mobile tokens.
    // useAllViewportTokens resolves tokens for ALL viewports + the current one (reactive to resize).
    // This ensures fullBleedPadding matches CardContent's padding at every breakpoint.
    const allViewportTokens = useAllViewportTokens('Card', tokens, variant)

    // Interactive cards: merge variants for CardBase (outer container)
    // The outer variant takes priority over interactiveCard.variant for the style property
    // This ensures the gradient is only applied to CardBase, not PressableCardBase and avoid duplication
    const interactiveStyle = interactiveCard?.variant?.style
    const outerStyle = variant?.style
    const mergedVariant = {
      ...variant,
      style: outerStyle || interactiveStyle
    }

    const interactiveCardSolidBg =
      interactiveCard?.tokens?.backgroundColor &&
      !isOverlayColor(interactiveCard.tokens.backgroundColor)
        ? interactiveCard.tokens.backgroundColor
        : undefined

    // Interactive cards: build configuration for PressableCardBase
    // This determines which style to use for interactive states (hover, pressed, etc.)
    // without causing gradient duplication
    let interactiveCardConfig = {}
    if (interactiveCard?.body) {
      const styleToUse = interactiveCard?.variant?.style || variant?.style
      const { style, ...otherVariantProps } = interactiveCard?.variant || {}

      interactiveCardConfig = {
        interactive: true,
        ...(styleToUse && !interactiveCardSolidBg && { style: styleToUse }),
        ...otherVariantProps
      }
    }

    const interactiveCardTokensForPressable = getInteractiveCardTokensForPressable(
      interactiveCard,
      interactiveCardSolidBg
    )

    const getThemeTokensBase = useThemeTokensCallback(
      'Card',
      interactiveCardTokensForPressable,
      interactiveCardConfig
    )

    const getThemeTokens = React.useCallback(
      (pressableState) => {
        const resolvedTokens = getThemeTokensBase(pressableState)
        const { gradient, backgroundGradient, ...tokensWithoutGradient } = resolvedTokens
        return tokensWithoutGradient
      },
      [getThemeTokensBase]
    )

    const getFocusBorderTokens = useThemeTokensCallback(
      'Card',
      {},
      {
        ...variant,
        interactive: true
      }
    )

    // Keep backgroundColor for CardContent, it won't affect FullBleedContent image
    const tokensWithoutBg = tokens

    const fullBleedInteractiveVariant = {
      ...variant,
      interactive: true
    }

    const getFullBleedInteractiveTokens = useThemeTokensCallback(
      'Card',
      tokensWithoutBg,
      fullBleedInteractiveVariant
    )

    const getFullBleedInteractiveCardTokens = React.useCallback(
      (cardState) => ({
        ...getFullBleedInteractiveTokens(cardState),
        paddingTop: 0,
        paddingBottom: 0,
        paddingLeft: 0,
        paddingRight: 0,
        borderWidth: 0,
        ...(interactiveCard?.body ? { gradient: undefined } : {})
      }),
      [getFullBleedInteractiveTokens, interactiveCard?.body]
    )

    const hasFooter = Boolean(footer)
    const fullBleedBorderRadius = getFullBleedBorderRadius(
      borderRadius,
      fullBleedContentPosition,
      hasFooter
    )

    // Calculate full bleed content padding values
    // When fullBleedPadding is truthy, apply padding around full bleed content
    // Uses viewport-aware tokens (allViewportTokens.current) so padding matches
    // CardContent's responsive padding at every breakpoint
    const hasFullBleedPadding = Boolean(fullBleedPadding)
    const fullBleedPaddingValues = hasFullBleedPadding
      ? calculateFullBleedPadding(
          allViewportTokens.current,
          fullBleedPadding,
          fullBleedPaddingSides
        )
      : null

    // takes imgCol from fullBleedContent if present, to dynamically set width of image
    // card content will adapt to the size of image to add up to 100% width of card width
    // pass as props to ConditionalWrapper
    const imgColCurrentViewport = useResponsiveProp(imgCol)
    const maxCol = GRID_COLUMNS

    const hasValidImgCol =
      imgCol && imgColCurrentViewport !== undefined && !Number.isNaN(imgColCurrentViewport)
    const fullBleedImageWidth = hasValidImgCol
      ? `${(imgColCurrentViewport / maxCol) * 100}%`
      : undefined
    const adaptiveContentWidth = hasValidImgCol
      ? `${((maxCol - imgColCurrentViewport) / maxCol) * 100}%`
      : undefined

    const isImageWidthAdjustable =
      hasValidImgCol &&
      (fullBleedContentPosition === POSITION.LEFT || fullBleedContentPosition === POSITION.RIGHT)

    const contentWrapperStyleProps = {
      ...(hasValidImgCol && { $width: adaptiveContentWidth }),
      ...(hasValidImgCol && imgColCurrentViewport >= maxCol && { $display: 'none' }),
      ...(fullBleedContentChildrenAlign && {
        $alignSelf: fullBleedContentChildrenAlign
      })
    }

    const columnFlex = {
      flexGrow: interactiveCard?.body ? 0 : 1,
      flexShrink: 1,
      justifyContent: 'space-between'
    }

    const userSolidBg =
      tokens?.backgroundColor && !isOverlayColor(tokens.backgroundColor)
        ? tokens.backgroundColor
        : undefined

    const cardBaseTokens = getCardBaseTokens(tokens, {
      interactiveCardSolidBg,
      userSolidBg
    })

    const cardBaseVariant = {
      ...(interactiveCard?.body ? mergedVariant : variant),
      padding: 'custom'
    }

    const isHorizontalFullBleed =
      fullBleedContentPosition === POSITION.LEFT || fullBleedContentPosition === POSITION.RIGHT
    const isVerticalFullBleed =
      fullBleedContentPosition === POSITION.TOP || fullBleedContentPosition === POSITION.BOTTOM

    const imageWrapperStyleProps = {
      ...(isImageWidthAdjustable && { $width: fullBleedImageWidth }),
      ...(isImageWidthAdjustable &&
        imgColCurrentViewport >= maxCol && { $borderRadius: borderRadius, $overflow: 'hidden' }),
      ...(isImageWidthAdjustable && imgColCurrentViewport === 0 && { $display: 'none' })
    }

    const systemProps = selectProps(restProps)

    const showSelection = interactiveCard?.selectionType && children
    const showInteractiveBody = interactiveCard?.body && !interactiveCard.selectionType
    const showFullBleedInteractive = fullBleedInteractive
    const showDefault =
      !fullBleedInteractive &&
      !interactiveCard?.body &&
      fullBleedContentPosition === POSITION.NONE &&
      children
    const showFullBleed = !fullBleedInteractive && fullBleedContentPosition !== POSITION.NONE

    return (
      <CardBase
        ref={ref}
        variant={cardBaseVariant}
        tokens={cardBaseTokens}
        backgroundImage={backgroundImage}
        onPress={fullBleedInteractive ? undefined : onPress}
        testID={testID}
        {...(interactiveCard?.selectionType && { interactiveCard, id: rest.id })}
        {...systemProps}
      >
        {showSelection && (
          <SelectionContent
            tokens={tokens}
            variant={variant}
            withFooter={hasFooter}
            backgroundImage={backgroundImage}
            testID={testID}
          >
            {children}
          </SelectionContent>
        )}
        {showInteractiveBody && (
          <InteractiveBody
            ref={ref}
            interactiveCard={interactiveCard}
            tokens={tokens}
            variant={variant}
            withFooter={hasFooter}
            backgroundImage={backgroundImage}
            getThemeTokens={getThemeTokens}
            dataSet={dataSet}
            onPress={onPress}
            systemProps={systemProps}
            fullBleedContentPosition={fullBleedContentPosition}
            fullBleedInteractive={fullBleedInteractive}
            testID={testID}
          >
            {children}
          </InteractiveBody>
        )}
        {showFullBleedInteractive && (
          <FullBleedInteractiveLayout
            ref={ref}
            tokensWithoutBg={tokensWithoutBg}
            variant={variant}
            withFooter={hasFooter}
            backgroundImage={backgroundImage}
            contentStackDirection={contentStackDirection}
            contentStackAlign={contentStackAlign}
            columnFlex={columnFlex}
            contentWrapperStyleProps={contentWrapperStyleProps}
            imageWrapperStyleProps={imageWrapperStyleProps}
            isImageWidthAdjustable={isImageWidthAdjustable}
            isHorizontalFullBleed={isHorizontalFullBleed}
            isVerticalFullBleed={isVerticalFullBleed}
            fullBleedContentPosition={fullBleedContentPosition}
            fullBleedContentChildrenAlign={fullBleedContentChildrenAlign}
            fullBleedContentPropsClean={fullBleedContentPropsClean}
            fullBleedBorderRadius={fullBleedBorderRadius}
            fullBleedPaddingValues={fullBleedPaddingValues}
            hasFullBleedPadding={hasFullBleedPadding}
            borderRadius={borderRadius}
            getFullBleedInteractiveCardTokens={getFullBleedInteractiveCardTokens}
            getFocusBorderTokens={getFocusBorderTokens}
            dataSet={dataSet}
            effectiveFullBleedOnPress={effectiveFullBleedOnPress}
            effectiveFullBleedHref={effectiveFullBleedHref}
            effectiveFullBleedHrefAttrs={effectiveFullBleedHrefAttrs}
            systemProps={systemProps}
            testID={testID}
          >
            {children}
          </FullBleedInteractiveLayout>
        )}
        {showDefault && (
          <DefaultContent
            tokens={tokens}
            variant={variant}
            withFooter={hasFooter}
            backgroundImage={backgroundImage}
            fullBleedContentChildrenAlign={fullBleedContentChildrenAlign}
            testID={testID}
          >
            {children}
          </DefaultContent>
        )}
        {showFullBleed && (
          <FullBleedLayout
            tokens={tokens}
            variant={variant}
            withFooter={hasFooter}
            backgroundImage={backgroundImage}
            contentStackDirection={contentStackDirection}
            contentStackAlign={contentStackAlign}
            columnFlex={columnFlex}
            contentWrapperStyleProps={contentWrapperStyleProps}
            imageWrapperStyleProps={imageWrapperStyleProps}
            isImageWidthAdjustable={isImageWidthAdjustable}
            isHorizontalFullBleed={isHorizontalFullBleed}
            isVerticalFullBleed={isVerticalFullBleed}
            fullBleedContentPosition={fullBleedContentPosition}
            fullBleedContentChildrenAlign={fullBleedContentChildrenAlign}
            fullBleedContentPropsClean={fullBleedContentPropsClean}
            fullBleedBorderRadius={fullBleedBorderRadius}
            fullBleedPaddingValues={fullBleedPaddingValues}
            hasFullBleedPadding={hasFullBleedPadding}
            testID={testID}
          >
            {children}
          </FullBleedLayout>
        )}
        {footer && (
          <CardFooter
            padding={footerPadding}
            tokens={tokens}
            variant={variant}
            data-testid={testID && `${testID}-card-footer`}
          >
            {footer}
          </CardFooter>
        )}
      </CardBase>
    )
  }
)

const positionValues = Object.values(POSITION)
const alignValues = ['start', 'end', 'center', 'stretch']
const PositionedFullBleedContentPropType = PropTypes.shape({
  position: responsiveProps.getTypeOptionallyByViewport(PropTypes.oneOf(positionValues)).isRequired,
  align: responsiveProps.getTypeOptionallyByViewport(PropTypes.oneOf(alignValues)),
  contentAlign: responsiveProps.getTypeOptionallyByViewport(PropTypes.oneOf(alignValues)),
  /**
   * Make the full bleed content interactive.
   * When true, the entire card (including the full bleed content) becomes interactive.
   */
  interactive: PropTypes.bool,
  /**
   * Function to call when the full bleed content is pressed.
   * If not provided, falls back to the Card's onPress prop for backward compatibility.
   */
  onPress: PropTypes.func,
  /**
   * URL to navigate to when the full bleed content is pressed.
   * If not provided, falls back to the Card's href prop for backward compatibility.
   */
  href: PropTypes.string,
  /**
   * Additional attributes for the href link.
   * If not provided, falls back to the Card's hrefAttrs prop for backward compatibility.
   */
  hrefAttrs: PropTypes.shape(hrefAttrsProp.types),
  // eslint-disable-next-line react/forbid-foreign-prop-types
  ...FullBleedContent.propTypes
})

Card.displayName = 'Card'

Card.propTypes = {
  ...selectedSystemPropTypes,
  /**
   * Card content.
   */
  children: PropTypes.node,
  /**
   * Card footer.
   */
  footer: PropTypes.node,
  /**
   * Custom card footer padding.
   */
  footerPadding: paddingProp.propType,
  /**
   * Custom full bleed content padding.
   * When true, inherits the card's own padding (from variant/tokens).
   * When an object, provides custom padding overrides: { paddingTop, paddingBottom, paddingLeft, paddingRight }.
   * Padding is automatically adjusted based on the full bleed content's position.
   */
  fullBleedPadding: PropTypes.oneOfType([PropTypes.bool, paddingProp.propType]),
  /**
   * Full bleed image to be placed on the card, deprecated in favor of `fullBleedContent`.
   *
   * @deprecated
   */
  fullBleedImage: PositionedFullBleedContentPropType,
  /**
   * Full bleed content to be placed on the card.
   */
  fullBleedContent: PositionedFullBleedContentPropType,
  /**
   * Card tokens.
   */
  tokens: getTokensPropType('Card'),
  /**
   * Card variant.
   */
  variant: variantProp.propType,
  /**
   * Function to call on pressing the card.
   * Note: This is only available when `interactive` variant is used.
   */
  onPress: PropTypes.func,
  /**
   * Object to set interactive card's properties
   * - body: The body of the interactive card, can be any renderable node
   * - tokens: The tokens to be used for the interactive card
   * - variant: The variant to be used for the interactive card
   * - href: The href to be used for the interactive card
   * - hrefAttrs: The href attributes to be used for the interactive card
   */
  interactiveCard: PropTypes.shape({
    body: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
    tokens: getTokensPropType('Card'),
    selectionType: PropTypes.oneOf(['checkbox', 'radiogroup']),
    variant: variantProp.propType,
    href: PropTypes.string,
    hrefAttrs: PropTypes.shape(hrefAttrsProp.types)
  }),
  /**
   * Apply background image to the card.
   */
  backgroundImage: PropTypes.shape({
    // src is either a URI string or an object when used responsively to provide different image sources for different screen sizes
    src: PropTypes.oneOfType([PropTypes.string, PropTypes.object]).isRequired,
    alt: PropTypes.string,
    resizeMode: responsiveProps.getTypeOptionallyByViewport(
      PropTypes.oneOf(['cover', 'contain', 'stretch', 'repeat', 'center'])
    )
  }),
  /**
   * Data set for the card.
   */
  dataSet: PropTypes.object,
  /**
   * A identifier for testing purposes.
   */
  testID: PropTypes.string
}
