import * as React from "react"
import { MoreHorizontalIcon } from "lucide-react"
import { cva, type VariantProps } from "class-variance-authority"

import { cn } from "@/lib/utils"
import { Button, type ButtonProps } from "./button"

type CardOrientation = "vertical" | "horizontal"
type CardType = "leading-image" | "trailing-image" | "full-image"
type CardBreakpoint = "desktop" | "mobile"

const cardVariants = cva(
  [
    "group/card relative flex min-w-0 max-w-full overflow-hidden",
    "rounded-[var(--bh-card-radius)] bg-[var(--bh-card-bg)] text-start",
    "text-[var(--bh-content-default)] shadow-[inset_0_0_0_var(--bh-border-width-default)_var(--bh-card-border)]",
    "[--bh-card-bg:var(--bh-bg-raised)]",
    "[--bh-card-border:var(--bh-border-subtle)]",
    "[--bh-card-radius:var(--bh-radius-3xl-16)]",
  ],
  {
    variants: {
      orientation: {
        vertical: "flex-col",
        horizontal: "items-stretch",
      },
      type: {
        "leading-image": "",
        "trailing-image": "",
        "full-image": "",
      },
      breakpoint: {
        desktop: [
          "[--bh-card-content-padding:var(--bh-card-desktop-content-padding)]",
          "[--bh-card-horizontal-padding:var(--bh-card-desktop-horizontal-padding)]",
          "[--bh-card-media-height:var(--bh-card-desktop-media-height)]",
          "[--bh-card-full-height:var(--bh-card-desktop-full-height)]",
        ],
        mobile: [
          "[--bh-card-content-padding:var(--bh-card-mobile-content-padding)]",
          "[--bh-card-horizontal-padding:var(--bh-card-mobile-horizontal-padding)]",
          "[--bh-card-media-height:var(--bh-card-mobile-media-height)]",
          "[--bh-card-full-height:var(--bh-card-mobile-full-height)]",
        ],
      },
    },
    compoundVariants: [
      {
        orientation: "vertical",
        class: "w-[min(100%,var(--bh-card-vertical-width))]",
      },
      {
        orientation: "vertical",
        type: "full-image",
        class: "h-[var(--bh-card-full-height)]",
      },
      {
        orientation: "horizontal",
        breakpoint: "desktop",
        class:
          "min-h-[var(--bh-card-horizontal-min-height)] w-[min(100%,var(--bh-card-horizontal-width))]",
      },
      {
        orientation: "horizontal",
        breakpoint: "mobile",
        class: "w-[min(100%,var(--bh-card-vertical-width))] flex-col",
      },
    ],
    defaultVariants: {
      orientation: "vertical",
      type: "leading-image",
      breakpoint: "desktop",
    },
  }
)

type CardProps = Omit<React.ComponentProps<"article">, "title"> &
  VariantProps<typeof cardVariants> & {
    actions?: React.ReactNode
    description?: React.ReactNode
    footer?: React.ReactNode
    imageAlt?: string
    imageSrc?: string
    moreButtonLabel?: string
    onMoreClick?: React.MouseEventHandler<HTMLButtonElement>
    primaryActionLabel?: React.ReactNode
    primaryActionProps?: ButtonProps
    secondaryActionLabel?: React.ReactNode
    secondaryActionProps?: ButtonProps
    showDescription?: boolean
    showFooter?: boolean
    showImage?: boolean
    showMoreButton?: boolean
    showSubtitle?: boolean
    subtitle?: React.ReactNode
    title?: React.ReactNode
  }

type CardMediaProps = React.ComponentProps<"div"> & {
  imageAlt?: string
  imageSrc?: string
}

type CardActionsProps = React.ComponentProps<"div"> & {
  primaryLabel?: React.ReactNode
  primaryProps?: ButtonProps
  secondaryLabel?: React.ReactNode
  secondaryProps?: ButtonProps
}

type CardFooterProps = React.ComponentProps<"div">

const Card = React.forwardRef<HTMLElement, CardProps>(function Card({
  actions,
  breakpoint = "desktop",
  className,
  description,
  footer,
  imageAlt = "",
  imageSrc,
  moreButtonLabel = "Open card menu",
  onMoreClick,
  orientation = "vertical",
  primaryActionLabel = "Button",
  primaryActionProps,
  secondaryActionLabel = "Button",
  secondaryActionProps,
  showDescription = true,
  showFooter = true,
  showImage = true,
  showMoreButton = true,
  showSubtitle = true,
  subtitle,
  title,
  type = "leading-image",
  ...props
}, ref) {
  const cardOrientation = orientation ?? "vertical"
  const cardBreakpoint = breakpoint ?? "desktop"
  const cardType =
    cardOrientation === "horizontal" && type === "full-image"
      ? "leading-image"
      : type ?? "leading-image"
  const fullImage = cardOrientation === "vertical" && cardType === "full-image"
  const renderedTitle = title ?? "Card Title"
  const renderedSubtitle = subtitle ?? "Card Subtitle"
  const renderedDescription =
    description ??
    "Journey through time as we explore the mysteries of the Great Pyramids and the Sphinx."

  const media = showImage ? (
    <CardMedia
      imageAlt={imageAlt}
      imageSrc={imageSrc}
      className={getCardMediaClassName(cardOrientation, cardBreakpoint)}
    />
  ) : null

  const body = (
    <CardBody
      actions={actions}
      breakpoint={cardBreakpoint}
      description={renderedDescription}
      footer={footer}
      moreButtonLabel={moreButtonLabel}
      onMoreClick={onMoreClick}
      orientation={cardOrientation}
      primaryActionLabel={primaryActionLabel}
      primaryActionProps={primaryActionProps}
      secondaryActionLabel={secondaryActionLabel}
      secondaryActionProps={secondaryActionProps}
      showDescription={showDescription}
      showFooter={showFooter && !fullImage}
      showMoreButton={showMoreButton}
      showSubtitle={showSubtitle}
      subtitle={renderedSubtitle}
      title={renderedTitle}
    />
  )

  return (
    <article
      data-slot="card"
      data-breakpoint={cardBreakpoint}
      data-orientation={cardOrientation}
      data-type={cardType}
      ref={ref}
      className={cn(
        cardVariants({
          orientation: cardOrientation,
          type: cardType,
          breakpoint: cardBreakpoint,
        }),
        className
      )}
      {...props}
    >
      {fullImage ? (
        <>
          <CardMedia
            imageAlt={imageAlt}
            imageSrc={imageSrc}
            className="absolute inset-0 size-full"
          />
          <CardFullImageContent
            breakpoint={cardBreakpoint}
            moreButtonLabel={moreButtonLabel}
            onMoreClick={onMoreClick}
            showMoreButton={showMoreButton}
            showSubtitle={showSubtitle}
            subtitle={renderedSubtitle}
            title={renderedTitle}
          />
        </>
      ) : cardOrientation === "horizontal" &&
        cardBreakpoint === "desktop" &&
        cardType === "trailing-image" ? (
        <>
          {body}
          {media}
        </>
      ) : (
        <>
          {media}
          {body}
        </>
      )}
    </article>
  )
})

const CardMedia = React.forwardRef<HTMLDivElement, CardMediaProps>(
  function CardMedia({ children, className, imageAlt = "", imageSrc, ...props }, ref) {
    return (
      <div
        data-slot="card-media"
        ref={ref}
        className={cn(
          "relative min-h-0 shrink-0 overflow-hidden bg-[var(--bh-bg-neutral-subtle)]",
          className
        )}
        {...props}
      >
        {children ??
          (imageSrc ? (
            <img
              alt={imageAlt}
              className="absolute inset-0 size-full object-cover"
              src={imageSrc}
            />
          ) : (
            <CardImagePlaceholder />
          ))}
      </div>
    )
  }
)

const CardFooter = React.forwardRef<HTMLDivElement, CardFooterProps>(
  function CardFooter({ children, className, ...props }, ref) {
    return (
      <div
        data-slot="card-footer"
        ref={ref}
        className={cn("flex min-w-0 shrink-0 flex-col items-stretch", className)}
        {...props}
      >
        {children}
      </div>
    )
  }
)

const CardActions = React.forwardRef<HTMLDivElement, CardActionsProps>(
  function CardActions({
    children,
    className,
    primaryLabel = "Button",
    primaryProps,
    secondaryLabel = "Button",
    secondaryProps,
    ...props
  }, ref) {
    return (
      <div
        data-slot="card-actions"
        ref={ref}
        className={cn(
          "grid w-full max-w-[var(--bh-card-actions-max-width)] grid-cols-2 gap-[var(--bh-space-xl-12)]",
          className
        )}
        {...props}
      >
        {children ?? (
          <>
            <Button {...primaryProps} className={cn("min-w-0", primaryProps?.className)}>
              {primaryLabel}
            </Button>
            <Button
              variant="soft"
              {...secondaryProps}
              className={cn("min-w-0", secondaryProps?.className)}
            >
              {secondaryLabel}
            </Button>
          </>
        )}
      </div>
    )
  }
)

function CardBody({
  actions,
  breakpoint,
  description,
  footer,
  moreButtonLabel,
  onMoreClick,
  orientation,
  primaryActionLabel,
  primaryActionProps,
  secondaryActionLabel,
  secondaryActionProps,
  showDescription,
  showFooter,
  showMoreButton,
  showSubtitle,
  subtitle,
  title,
}: {
  actions?: React.ReactNode
  breakpoint: CardBreakpoint
  description: React.ReactNode
  footer?: React.ReactNode
  moreButtonLabel: string
  onMoreClick?: React.MouseEventHandler<HTMLButtonElement>
  orientation: CardOrientation
  primaryActionLabel: React.ReactNode
  primaryActionProps?: ButtonProps
  secondaryActionLabel: React.ReactNode
  secondaryActionProps?: ButtonProps
  showDescription: boolean
  showFooter: boolean
  showMoreButton: boolean
  showSubtitle: boolean
  subtitle: React.ReactNode
  title: React.ReactNode
}) {
  const horizontal = orientation === "horizontal"

  return (
    <div
      data-slot="card-content"
      className={cn(
        "flex min-w-0 flex-col",
        horizontal
          ? "flex-1 gap-[var(--bh-space-5xl-24)] p-[var(--bh-card-horizontal-padding)]"
          : "w-full gap-[var(--bh-space-5xl-24)] p-[var(--bh-card-content-padding)]"
      )}
    >
      <div
        data-slot="card-copy"
        className={cn(
          "flex min-w-0 flex-col",
          horizontal
            ? "gap-[var(--bh-space-xl-12)]"
            : "gap-[var(--bh-space-md-8)]"
        )}
      >
        <CardHeader
          moreButtonLabel={moreButtonLabel}
          onMoreClick={onMoreClick}
          onImage={false}
          showMoreButton={showMoreButton}
          showSubtitle={showSubtitle}
          subtitle={subtitle}
          title={title}
          subtitleClassName={getSubtitleClassName(breakpoint)}
        />
        {showDescription ? (
          <p
            data-slot="card-description"
            dir="auto"
            className={cn(
              "min-w-0 break-words text-[var(--bh-content-subtle)]",
              getDescriptionClassName(orientation, breakpoint)
            )}
          >
            {description}
          </p>
        ) : null}
      </div>

      {showFooter ? (
        <CardFooter>
          {footer ??
            actions ?? (
              <CardActions
                primaryLabel={primaryActionLabel}
                primaryProps={primaryActionProps}
                secondaryLabel={secondaryActionLabel}
                secondaryProps={secondaryActionProps}
              />
            )}
        </CardFooter>
      ) : null}
    </div>
  )
}

function CardFullImageContent({
  breakpoint,
  moreButtonLabel,
  onMoreClick,
  showMoreButton,
  showSubtitle,
  subtitle,
  title,
}: {
  breakpoint: CardBreakpoint
  moreButtonLabel: string
  onMoreClick?: React.MouseEventHandler<HTMLButtonElement>
  showMoreButton: boolean
  showSubtitle: boolean
  subtitle: React.ReactNode
  title: React.ReactNode
}) {
  return (
    <div
      data-slot="card-overlay"
      className={[
        "absolute inset-x-0 bottom-0 flex min-w-0 flex-col",
        "bg-gradient-to-b from-transparent to-[var(--bh-card-media-overlay)]",
        "px-[var(--bh-card-content-padding)] pb-[var(--bh-card-content-padding)]",
        "pt-[var(--bh-space-6xl-32)] text-[var(--bh-content-on-color)]",
      ].join(" ")}
    >
      <CardHeader
        moreButtonLabel={moreButtonLabel}
        onMoreClick={onMoreClick}
        onImage
        showMoreButton={showMoreButton}
        showSubtitle={showSubtitle}
        subtitle={subtitle}
        subtitleClassName={getSubtitleClassName(breakpoint)}
        title={title}
      />
    </div>
  )
}

function CardHeader({
  moreButtonLabel,
  onImage,
  onMoreClick,
  showMoreButton,
  showSubtitle,
  subtitle,
  subtitleClassName,
  title,
}: {
  moreButtonLabel: string
  onImage: boolean
  onMoreClick?: React.MouseEventHandler<HTMLButtonElement>
  showMoreButton: boolean
  showSubtitle: boolean
  subtitle: React.ReactNode
  subtitleClassName: string
  title: React.ReactNode
}) {
  return (
    <div
      data-slot="card-header"
      className="flex min-w-0 items-start gap-[var(--bh-space-3xl-16)]"
    >
      <div className="flex min-w-0 flex-1 flex-col gap-[var(--bh-space-xxs-2)]">
        <h3
          data-slot="card-title"
          dir="auto"
          className={cn(
            "min-w-0 break-words text-[length:var(--bh-text-heading-sm-semibold-font-size)]",
            "font-[var(--bh-text-heading-sm-semibold-font-weight)]",
            "leading-[var(--bh-text-heading-sm-semibold-line-height)]",
            "tracking-[var(--bh-text-heading-sm-semibold-letter-spacing)]",
            onImage ? "text-[var(--bh-content-on-color)]" : "text-[var(--bh-content-default)]"
          )}
        >
          {title}
        </h3>
        {showSubtitle ? (
          <p
            data-slot="card-subtitle"
            dir="auto"
            className={cn(
              "min-w-0 break-words",
              onImage
                ? "text-[var(--bh-content-on-color)]"
                : "text-[var(--bh-content-subtle)]",
              subtitleClassName
            )}
          >
            {subtitle}
          </p>
        ) : null}
      </div>
      {showMoreButton ? (
        <CardMoreButton
          aria-label={moreButtonLabel}
          onClick={onMoreClick}
          onImage={onImage}
        />
      ) : null}
    </div>
  )
}

function CardMoreButton({
  onImage,
  ...props
}: React.ComponentProps<"button"> & { onImage: boolean }) {
  return (
    <button
      data-slot="card-more"
      type="button"
      className={cn(
        "inline-flex size-[var(--bh-card-more-size)] shrink-0 items-center justify-center",
        "rounded-[var(--bh-control-default)] outline-none",
        "transition-[background-color,color,box-shadow]",
        "focus-visible:shadow-[var(--shadow-button-focus)]",
        onImage
          ? "text-[var(--bh-content-on-color)] hover:bg-[var(--bh-interactive-ghost-hover)]"
          : "text-[var(--bh-content-subtle)] hover:bg-[var(--bh-interactive-ghost-hover)] hover:text-[var(--bh-content-default)]"
      )}
      {...props}
    >
      <MoreHorizontalIcon className="size-[var(--bh-card-more-icon-size)]" aria-hidden="true" />
    </button>
  )
}

function CardImagePlaceholder() {
  return (
    <div
      aria-hidden="true"
      data-slot="card-image-placeholder"
      className={[
        "absolute inset-0",
        "bg-[linear-gradient(135deg,var(--bh-bg-accent-sky-subtle),var(--bh-bg-accent-green-subtle))]",
      ].join(" ")}
    />
  )
}

function getCardMediaClassName(
  orientation: CardOrientation,
  breakpoint: CardBreakpoint
) {
  if (orientation === "horizontal" && breakpoint === "desktop") {
    return "h-auto w-[var(--bh-card-horizontal-media-width)]"
  }

  return "h-[var(--bh-card-media-height)] w-full"
}

function getSubtitleClassName(breakpoint: CardBreakpoint) {
  if (breakpoint === "mobile") {
    return [
      "text-[length:var(--bh-text-body-md-regular-font-size)]",
      "font-[var(--bh-text-body-md-regular-font-weight)]",
      "leading-[var(--bh-text-body-md-regular-line-height)]",
      "tracking-[var(--bh-text-body-md-regular-letter-spacing)]",
    ].join(" ")
  }

  return [
    "text-[length:var(--bh-text-body-lg-regular-font-size)]",
    "font-[var(--bh-text-body-lg-regular-font-weight)]",
    "leading-[var(--bh-text-body-lg-regular-line-height)]",
    "tracking-[var(--bh-text-body-lg-regular-letter-spacing)]",
  ].join(" ")
}

function getDescriptionClassName(
  orientation: CardOrientation,
  breakpoint: CardBreakpoint
) {
  if (orientation === "horizontal" && breakpoint === "desktop") {
    return getSubtitleClassName("desktop")
  }

  return [
    "text-[length:var(--bh-text-body-md-regular-font-size)]",
    "font-[var(--bh-text-body-md-regular-font-weight)]",
    "leading-[var(--bh-text-body-md-regular-line-height)]",
    "tracking-[var(--bh-text-body-md-regular-letter-spacing)]",
  ].join(" ")
}

export {
  Card,
  CardActions,
  CardFooter,
  CardMedia,
  cardVariants,
  type CardActionsProps,
  type CardBreakpoint,
  type CardFooterProps,
  type CardMediaProps,
  type CardOrientation,
  type CardProps,
  type CardType,
}
