import React, { useId, ComponentType } from "react";
import classNames from "classnames";
import { ArcSizeBreakpointsL, ArcSizeBreakpointsM } from "@arc-ui/tokens-arc";
import { Carousel } from "@arc-ui/components/Carousel";
import { Heading, type HeadingLevel } from "@arc-ui/components/Heading";
import { useMediaQuery } from "@arc-ui/components/use-media-query";
import { SectionHeading, type SectionHeadingProps } from "../SectionHeading";
import { MediaCard, type MediaCardProps } from "@arc-ui/components/MediaCard";
import { filterAttrs } from "@arc-ui/community-utils/filter-attrs";
import {
  ImpactCard,
  type ImpactCardProps,
} from "@arc-ui/components/ImpactCard";

import {
  InformationCard,
  type InformationCardProps,
} from "@arc-ui/components/InformationCard";

import {
  TypographyCard,
  type TypographyCardProps,
} from "@arc-ui/components/TypographyCard";

import { ButtonV2, type ButtonV2Props } from "@arc-ui/components/ButtonV2";

import { VerticalSpace } from "@arc-ui/components/VerticalSpace";

import styles from "./PromoListing.module.css";
import { BtIconArrowRightFill } from "@arc-ui/icons";

/**
 * Use `PromoListing` to give supporting information about a page or proposition in the form of the card components (`ImpactCard`, `InformationCard`, `MediaCard` and `TypographyCard`).
 */
export const PromoListing: React.FC<PromoListingProps> = ({
  heading,
  content,
  id,
  isCarousel = false,
  cards,
  headingLevel,
  isHeadingWordWrap,
  columns = "2",
  button = { buttonStyle: "secondary" },
  overlineHeadingLevel,
  overlineLabel,
  ...props
}) => {
  const componentId = useId();

  const isMinWidthArcBreakpointL = useMediaQuery(
    `(min-width: ${ArcSizeBreakpointsL})`,
  );

  const isMinWidthArcBreakpointM = useMediaQuery(
    `(min-width: ${ArcSizeBreakpointsM})`,
  );

  const cardAspectRatios = columns === "3" ? "10-16" : "3-4";

  const getCardAspectRatios = {
    xs: !isCarousel ? "3-4" : undefined,
    s: !isCarousel ? "10-16" : undefined,
    m: !isCarousel ? "3-4" : undefined,
    l: cardAspectRatios,
    xl: cardAspectRatios,
    xxl: cardAspectRatios,
    xxxl: cardAspectRatios,
  } as MediaCardProps["preserveAspectRatios"];

  const commonProps = {
    preserveAspectRatios: getCardAspectRatios,
    minHeight:
      isCarousel && !isMinWidthArcBreakpointL
        ? isMinWidthArcBreakpointM
          ? 557
          : 461
        : undefined,
  };

  const CARD_COMPONENTS = {
    "media-card": MediaCard,
    "impact-card": ImpactCard,
    "information-card": InformationCard,
    "typography-card": TypographyCard,
  } as const;

  const getCard = (card: PromoListCard) => {
    const CardComponent = CARD_COMPONENTS[card.type] as ComponentType<
      typeof card
    >;
    return <CardComponent {...card} {...commonProps} />;
  };

  return (
    <div className={styles.PromoListing} {...filterAttrs(props)}>
      {overlineLabel && (
        <>
          <Heading level={overlineHeadingLevel} fontStyle="overline">
            {overlineLabel}
          </Heading>
          <VerticalSpace size="12" />
        </>
      )}

      <SectionHeading
        isPadded
        id={id}
        data-testid="PromoListing-header"
        heading={heading}
        isHeadingWordWrap={isHeadingWordWrap}
        headingLevel={headingLevel}
        content={content}
      />

      {button.label && (
        <>
          <ButtonV2
            {...button}
            icon={
              button.buttonStyle == "compact" ? BtIconArrowRightFill : undefined
            }
            size="m"
          />
          <VerticalSpace size="40" />
        </>
      )}
      {isCarousel && !isMinWidthArcBreakpointL ? (
        <div className={styles.CarouselWrapper}>
          <Carousel
            overflow="visible"
            itemsPerSlide={1}
            behavior="adaptive"
            withGutter={false}
          >
            {cards?.map((card: PromoListCard, i) => (
              <div
                key={`promolist-carousel-card-${componentId}-${i}`}
                className={(styles.carouselSlide, styles.cardWrapperCarousel)}
              >
                {getCard(card)}
              </div>
            ))}
          </Carousel>
        </div>
      ) : (
        <ul
          className={classNames(styles.cardGrid, styles[`columns-${columns}`])}
        >
          {cards?.map((card: PromoListCard, index) => (
            <li key={`promolist-card--${componentId}-${index}`}>
              <div
                data-testid={`promoList-card${index}`}
                className={styles.cardWrapper}
              >
                {getCard(card)}
              </div>
            </li>
          ))}
        </ul>
      )}
    </div>
  );
};

interface PromoListMediaCard extends MediaCardProps {
  type: "media-card";
}

interface PromoListImpactCard extends ImpactCardProps {
  type: "impact-card";
}

interface PromoListInformationCard extends InformationCardProps {
  type: "information-card";
}

interface PromoListTypographyCard extends TypographyCardProps {
  type: "typography-card";
}

type PromoListCard =
  | PromoListMediaCard
  | PromoListInformationCard
  | PromoListTypographyCard
  | PromoListImpactCard;

export interface PromoListingProps
  extends Omit<SectionHeadingProps, "isPadded"> {
  /**
   * Cards for the PromoListing.
   */
  cards: PromoListCard[];

  /**
   * Shows cards in a carousel on devices below 1024px (m breakpoint) if true.
   */
  isCarousel?: boolean;

  /**
   * Number of columns for desktop breakpoints. "2" and "3" column options available.
   */
  columns?: "2" | "3";

  /**
   * Optional button configuration displayed above the cards.
   */
  button?: PromoListingButton;
  /**
   * Text label displayed above the main heading in overline style
   */
  overlineLabel?: string;

  /**
   * Heading level for the overline label
   */
  overlineHeadingLevel?: HeadingLevel;
}

/**
 * Button configuration for PromoListing component.
 */
interface PromoListingButton
  extends Omit<ButtonV2Props, "buttonStyle" | "icon" | "size"> {
  /**
   * Style variant for the button. Defaults to "secondary".
   */
  buttonStyle?: "secondary" | "compact";
}
