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

import { cn } from "@/lib/utils"

type AttributeLayout = "inline" | "end" | "stacked" | "two-column"
type AttributeDensity = "compact" | "default" | "comfortable"
type AttributeAlign = "start" | "end" | "stretch"
type AttributeValueDir = "auto" | "ltr" | "rtl"

type AttributeContextValue = {
  density: AttributeDensity
  dividers: boolean
  labelWidth: string
  layout: AttributeLayout
}

type AttributeStyle = React.CSSProperties & {
  "--bh-attribute-label-width"?: string
}

const defaultAttributeContext: AttributeContextValue = {
  density: "default",
  dividers: false,
  labelWidth: "var(--bh-attribute-label-width)",
  layout: "inline",
}

const AttributeContext = React.createContext<AttributeContextValue>(
  defaultAttributeContext
)

const attributeCardVariants = cva(
  [
    "min-w-0 overflow-hidden rounded-[var(--bh-radius-2xl-12)]",
    "border border-[var(--bh-border-subtle)] bg-[var(--bh-bg-default)]",
    "text-[var(--bh-content-default)]",
  ],
  {
    variants: {
      padding: {
        compact:
          "px-[var(--bh-space-4xl-20)] pb-[var(--bh-space-xl-12)] pt-[var(--bh-space-4xl-20)]",
        default:
          "px-[var(--bh-space-5xl-24)] pb-[var(--bh-space-3xl-16)] pt-[var(--bh-space-5xl-24)]",
      },
    },
    defaultVariants: {
      padding: "default",
    },
  }
)

const attributeHeaderVariants = cva(
  "flex min-w-0 flex-col items-start gap-[var(--bh-space-md-8)] pb-[var(--bh-space-4xl-20)]",
  {
    variants: {
      size: {
        default: "",
        compact: "gap-[var(--bh-space-sm-6)] pb-[var(--bh-space-3xl-16)]",
      },
    },
    defaultVariants: {
      size: "default",
    },
  }
)

const attributeListVariants = cva("grid min-w-0", {
  variants: {
    layout: {
      inline: "grid-cols-1",
      end: "grid-cols-1",
      stacked: "grid-cols-1",
      "two-column":
        "grid-cols-1 gap-x-[var(--bh-space-6xl-32)] sm:grid-cols-2",
    },
    bordered: {
      true: "overflow-hidden rounded-[var(--bh-radius-lg-8)] border border-[var(--bh-border-subtle)]",
      false: "",
    },
  },
  defaultVariants: {
    layout: "inline",
    bordered: false,
  },
})

const attributeItemVariants = cva("min-w-0", {
  variants: {
    layout: {
      inline:
        "grid grid-cols-1 items-center gap-x-[var(--bh-space-xs-4)] sm:grid-cols-[var(--bh-attribute-label-width)_minmax(0,1fr)]",
      end:
        "grid grid-cols-1 items-center gap-x-[var(--bh-space-xs-4)] sm:grid-cols-[var(--bh-attribute-label-width)_minmax(0,1fr)]",
      stacked:
        "flex flex-col items-start justify-center gap-[var(--bh-space-xs-4)]",
      "two-column":
        "flex flex-col items-start justify-center gap-[var(--bh-space-xs-4)]",
    },
    density: {
      compact: "py-[var(--bh-space-md-8)]",
      default: "py-[var(--bh-space-xl-12)] sm:py-[var(--bh-space-3xl-16)]",
      comfortable: "py-[var(--bh-space-3xl-16)]",
    },
    divided: {
      true: "border-b border-[var(--bh-border-subtle)] last:border-b-0",
      false: "",
    },
    fullSpan: {
      true: "sm:col-span-2",
      false: "",
    },
  },
  defaultVariants: {
    layout: "inline",
    density: "default",
    divided: false,
    fullSpan: false,
  },
})

const attributeLabelVariants = cva(
  [
    "min-w-0 break-words 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)]",
    "text-[var(--bh-content-subtle)]",
  ],
  {
    variants: {
      truncate: {
        true: "truncate",
        false: "",
      },
    },
    defaultVariants: {
      truncate: false,
    },
  }
)

const attributeValueVariants = cva(
  [
    "flex min-w-0 flex-wrap items-center gap-[var(--bh-space-xs-4)]",
    "break-words text-[length:var(--bh-text-body-md-medium-font-size)]",
    "font-[var(--bh-text-body-md-medium-font-weight)]",
    "leading-[var(--bh-text-body-md-medium-line-height)]",
    "tracking-[var(--bh-text-body-md-medium-letter-spacing)]",
    "text-[var(--bh-content-default)]",
  ],
  {
    variants: {
      align: {
        start: "justify-self-start text-start",
        end: "justify-self-end justify-end text-end rtl:justify-self-start rtl:justify-start rtl:text-right",
        stretch: "w-full justify-self-stretch text-start",
      },
      truncate: {
        true: "[&_[data-slot='attribute-value-text']]:truncate",
        false: "",
      },
    },
    defaultVariants: {
      align: "start",
      truncate: false,
    },
  }
)

type AttributeCardProps = React.ComponentProps<"section"> &
  VariantProps<typeof attributeCardVariants>

const AttributeCard = React.forwardRef<HTMLElement, AttributeCardProps>(
  function AttributeCard({ className, padding, ...props }, ref) {
    return (
      <section
        data-slot="attribute-card"
        ref={ref}
        className={cn(attributeCardVariants({ padding, className }))}
        {...props}
      />
    )
  }
)

type AttributeHeaderProps = Omit<React.ComponentProps<"header">, "title"> &
  VariantProps<typeof attributeHeaderVariants> & {
    description?: React.ReactNode
    icon?: React.ReactNode
    title: React.ReactNode
  }

const AttributeHeader = React.forwardRef<HTMLElement, AttributeHeaderProps>(
  function AttributeHeader({
    className,
    description,
    icon,
    size,
    title,
    ...props
  }, ref) {
    return (
      <header
        data-slot="attribute-header"
        ref={ref}
        className={cn(attributeHeaderVariants({ size, className }))}
        {...props}
      >
        {icon ? (
          <span
            data-slot="attribute-header-icon"
            className="grid size-[var(--bh-attribute-header-icon-size)] place-items-center rounded-[var(--bh-radius-full)] bg-[var(--bh-bg-neutral-soft)] text-[var(--bh-content-subtle)] sm:size-[var(--bh-attribute-header-icon-size-lg)] [&_svg]:size-[var(--bh-space-4xl-20)] sm:[&_svg]:size-[var(--bh-space-5xl-24)]"
          >
            {icon}
          </span>
        ) : null}
        <span
          data-slot="attribute-header-content"
          className="flex min-w-0 flex-col gap-[var(--bh-space-xs-4)]"
        >
          <span
            data-slot="attribute-header-title"
            dir="auto"
            className="break-words text-[length:var(--bh-text-heading-sm-medium-font-size)] font-[var(--bh-text-heading-sm-medium-font-weight)] leading-[var(--bh-text-heading-sm-medium-line-height)] tracking-[var(--bh-text-heading-sm-medium-letter-spacing)] text-[var(--bh-content-default)]"
          >
            {title}
          </span>
          {description ? (
            <span
              data-slot="attribute-header-description"
              dir="auto"
              className="break-words 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)] text-[var(--bh-content-subtle)]"
            >
              {description}
            </span>
          ) : null}
        </span>
      </header>
    )
  }
)

type AttributeListProps = React.ComponentProps<"dl"> &
  VariantProps<typeof attributeListVariants> & {
    density?: AttributeDensity
    dividers?: boolean
    labelWidth?: string
  }

const AttributeList = React.forwardRef<HTMLDListElement, AttributeListProps>(
  function AttributeList({
    bordered = false,
    className,
    density = "default",
    dividers = false,
    labelWidth = defaultAttributeContext.labelWidth,
    layout = "inline",
    style,
    ...props
  }, ref) {
    const value = React.useMemo(
      () => ({
        density,
        dividers,
        labelWidth,
        layout: (layout || "inline") as AttributeLayout,
      }),
      [density, dividers, labelWidth, layout]
    )
    const attributeStyle = {
      "--bh-attribute-label-width": labelWidth,
      ...style,
    } as AttributeStyle

    return (
      <AttributeContext.Provider value={value}>
        <dl
          data-density={density}
          data-layout={layout}
          data-slot="attribute-list"
          ref={ref}
          style={attributeStyle}
          className={cn(attributeListVariants({ layout, bordered, className }))}
          {...props}
        />
      </AttributeContext.Provider>
    )
  }
)

type AttributeItemProps = Omit<React.ComponentProps<"div">, "children"> &
  VariantProps<typeof attributeItemVariants> & {
    children?: React.ReactNode
    label: React.ReactNode
    labelClassName?: string
    truncate?: boolean
    value?: React.ReactNode
    valueAlign?: AttributeAlign
    valueClassName?: string
    valueDir?: AttributeValueDir
  }

const AttributeItem = React.forwardRef<HTMLDivElement, AttributeItemProps>(
  function AttributeItem({
    children,
    className,
    density,
    divided,
    fullSpan = false,
    label,
    labelClassName,
    layout,
    truncate = false,
    value,
    valueAlign,
    valueClassName,
    valueDir = "auto",
    ...props
  }, ref) {
    const context = React.useContext(AttributeContext)
    const selectedLayout = layout || context.layout
    const selectedDensity = density || context.density
    const selectedDivided = divided ?? context.dividers
    const selectedAlign =
      valueAlign || (selectedLayout === "end" ? "end" : "start")

    return (
      <div
        data-slot="attribute-item"
        ref={ref}
        className={cn(
          attributeItemVariants({
            layout: selectedLayout,
            density: selectedDensity,
            divided: selectedDivided,
            fullSpan,
            className,
          })
        )}
        {...props}
      >
        <AttributeLabel className={labelClassName} truncate={truncate}>
          {label}
        </AttributeLabel>
        <AttributeValue
          align={selectedAlign}
          className={valueClassName}
          dir={valueDir}
          truncate={truncate}
        >
          {children ?? value}
        </AttributeValue>
      </div>
    )
  }
)

type AttributeLabelProps = React.ComponentProps<"dt"> &
  VariantProps<typeof attributeLabelVariants>

const AttributeLabel = React.forwardRef<HTMLElement, AttributeLabelProps>(
  function AttributeLabel({ children, className, truncate, ...props }, ref) {
    return (
      <dt
        data-slot="attribute-label"
        ref={ref}
        className={cn(attributeLabelVariants({ truncate, className }))}
        {...props}
      >
        {renderAttributeText(children, "attribute-label-text")}
      </dt>
    )
  }
)

type AttributeValueProps = React.ComponentProps<"dd"> &
  VariantProps<typeof attributeValueVariants> & {
    dir?: AttributeValueDir
  }

const AttributeValue = React.forwardRef<HTMLElement, AttributeValueProps>(
  function AttributeValue({
    align,
    children,
    className,
    dir = "auto",
    truncate,
    ...props
  }, ref) {
    return (
      <dd
        data-slot="attribute-value"
        ref={ref}
        className={cn(attributeValueVariants({ align, truncate, className }))}
        {...props}
      >
        {renderAttributeText(children, "attribute-value-text", dir)}
      </dd>
    )
  }
)

function renderAttributeText(
  children: React.ReactNode,
  slot: string,
  dir: AttributeValueDir = "auto"
): React.ReactNode {
  return React.Children.map(children, (child) => {
    if (
      typeof child === "string" ||
      typeof child === "number" ||
      typeof child === "bigint"
    ) {
      return (
        <span data-slot={slot} dir={dir} className="min-w-0">
          {child}
        </span>
      )
    }

    return child
  })
}

export {
  AttributeCard,
  AttributeHeader,
  AttributeItem,
  AttributeLabel,
  AttributeList,
  AttributeValue,
  attributeCardVariants,
  attributeHeaderVariants,
  attributeItemVariants,
  attributeLabelVariants,
  attributeListVariants,
  attributeValueVariants,
}
export type {
  AttributeAlign,
  AttributeCardProps,
  AttributeDensity,
  AttributeHeaderProps,
  AttributeItemProps,
  AttributeLabelProps,
  AttributeLayout,
  AttributeListProps,
  AttributeValueDir,
  AttributeValueProps,
}
