import { mergeProps } from "@base-ui/react/merge-props"
import { useRender } from "@base-ui/react/use-render"
import { cva, type VariantProps } from "class-variance-authority"

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

/**
 * CSS variable architecture:
 *
 * The root owns four variables so every part of the tile stays in proportion
 * and stays overridable from a single `className`:
 *
 *   --icon-tile-size       tile width/height
 *   --icon-tile-icon-size  glyph size applied to child svgs
 *   --icon-tile-radius     corner radius (also drives the nested inner card)
 *   --icon-tile-inset      gap between the outer ring and the inner card
 *
 * The `frame` and `soft` variants paint their inner card with an `::after`
 * pseudo element instead of a wrapper node. `isolate` makes the root a stacking
 * context, so the negative z-index pseudo paints above the root background but
 * below the in-flow icon - no extra DOM, and `render` composition keeps working.
 *
 * Tone: `soft` and `solid` derive every fill and border from `currentColor`, so
 * a single text color class (e.g. `text-success`) retints the whole tile. They
 * default to `text-primary`; override it to recolor without touching internals.
 */
const iconTileVariants = cva(
  [
    "relative inline-flex shrink-0 items-center justify-center align-middle",
    "size-(--icon-tile-size) rounded-(--icon-tile-radius)",
    "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=size-])]:size-(--icon-tile-icon-size)",
  ],
  {
    variants: {
      variant: {
        /** Plain bordered surface. The quiet default for list rows and toolbars. */
        outline: "border border-border bg-background dark:bg-input/32",
        /** Raised muted fill with a background-colored ring. Reads as a physical chip. */
        elevated:
          "border-2 border-background bg-muted text-accent-foreground shadow-[0_1px_3px_0_rgb(0_0_0/0.14)] dark:border",
        /**
         * Tinted double container: an opacity-filled outer ring with no border
         * around a bordered inner card, all derived from `currentColor`. The
         * quiet, colorful sibling of `frame`. Retint with a text color class.
         */
        soft: [
          "isolate p-(--icon-tile-inset) text-primary bg-current/10",
          "after:absolute after:-z-10 after:inset-(--icon-tile-inset)",
          "after:rounded-[calc(var(--icon-tile-radius)-var(--icon-tile-inset))]",
          "after:border after:border-current/20 after:bg-current/5",
        ],
        /** Filled tone with a contrasting glyph. Retint with `bg-*` + a text color. */
        solid: "bg-primary text-primary-foreground",
        /** Double container - a muted ring around an inset card, matching Frame. */
        frame: [
          "isolate border border-border bg-muted/50 p-(--icon-tile-inset)",
          "after:absolute after:-z-10 after:inset-(--icon-tile-inset)",
          "after:rounded-[calc(var(--icon-tile-radius)-var(--icon-tile-inset))]",
          "after:border after:border-border after:bg-card after:shadow-xs",
        ],
      },
      size: {
        xs: "[--icon-tile-size:--spacing(6)] [--icon-tile-icon-size:--spacing(3.5)] [--icon-tile-inset:--spacing(0.5)]",
        sm: "[--icon-tile-size:--spacing(8)] [--icon-tile-icon-size:--spacing(4)] [--icon-tile-inset:--spacing(0.5)]",
        default:
          "[--icon-tile-size:--spacing(10)] [--icon-tile-icon-size:--spacing(4.5)] [--icon-tile-inset:--spacing(0.75)]",
        lg: "[--icon-tile-size:--spacing(12)] [--icon-tile-icon-size:--spacing(5.5)] [--icon-tile-inset:--spacing(0.75)]",
        xl: "[--icon-tile-size:--spacing(14)] [--icon-tile-icon-size:--spacing(7)] [--icon-tile-inset:--spacing(1)]",
      },
      /**
       * `default`: active style radius. `full`: circular.
       * The plain value is the fallback outside a style scope; the `style-*`
       * tokens win by specificity inside one, and survive the registry
       * transform as the single resolved value per generated style.
       *
       * Each style token is clamped to a fraction of the tile. A flat radius
       * is a circle once it reaches half the box, so the soft styles used to
       * render `xs` (24px) and `sm` (32px) as plain circles and swallow the
       * `full` variant's meaning. Clamping keeps one corner ratio at every
       * size instead, so a tile still reads as the same shape when it scales.
       * Luma and Rhea take the gentler quarter ratio; Maia stays at a third.
       */
      radius: {
        default:
          "[--icon-tile-radius:min(var(--radius-md),calc(var(--icon-tile-size)/3))] [--icon-tile-radius:min(var(--radius-md),calc(var(--icon-tile-size)/3))]",
        full: "[--icon-tile-radius:calc(infinity*1px)]",
      },
    },
    defaultVariants: {
      variant: "outline",
      size: "default",
      radius: "default",
    },
  }
)

interface IconTileProps extends useRender.ComponentProps<"span"> {
  variant?: VariantProps<typeof iconTileVariants>["variant"]
  size?: VariantProps<typeof iconTileVariants>["size"]
  radius?: VariantProps<typeof iconTileVariants>["radius"]
}

function IconTile({
  className,
  variant = "outline",
  size = "default",
  radius = "default",
  render,
  ...props
}: IconTileProps) {
  const defaultProps = {
    "data-slot": "icon-tile",
    "data-variant": variant,
    "data-size": size,
    className: cn(iconTileVariants({ variant, size, radius, className })),
  }

  return useRender({
    defaultTagName: "span",
    render,
    props: mergeProps<"span">(defaultProps, props),
  })
}

export { IconTile, iconTileVariants, type IconTileProps }