import clsx from 'clsx'

/**
 * Tiny in-house implementation of the `class-variance-authority` pattern
 * (a.k.a. `cva` / `tailwind-variants`). We roll our own to avoid a runtime
 * dependency while keeping the API surface compatible with the shadcn /
 * cva ecosystem — anyone familiar with cva can read these calls.
 *
 * @example
 *   const button = cv('inline-flex items-center', {
 *     variants: {
 *       intent: {
 *         primary: 'bg-primary text-white',
 *         secondary: 'bg-secondary text-foreground',
 *       },
 *       size: {
 *         sm: 'px-2 py-1 text-xs',
 *         md: 'px-4 py-2 text-sm',
 *       },
 *     },
 *     defaultVariants: { intent: 'primary', size: 'md' },
 *   })
 *
 *   button({ intent: 'secondary' })           // -> "inline-flex … bg-secondary …"
 *   button({ intent: 'secondary', class: 'shadow' })  // append extras
 */

type ClassValue = string | undefined | false | null

export type VariantsConfig = Record<string, Record<string, ClassValue>>

type VariantSelection<V extends VariantsConfig> = {
  [K in keyof V]?: keyof V[K]
}

interface CompoundVariant<V extends VariantsConfig> {
  // Subset of variants that must match for the compound classes to apply.
  // Use string values from each variant key (or arrays for "match any").
  [key: string]: keyof V[keyof V] | Array<keyof V[keyof V]> | ClassValue
  class?: ClassValue
}

export interface CvConfig<V extends VariantsConfig> {
  variants: V
  defaultVariants?: VariantSelection<V>
  compoundVariants?: Array<CompoundVariant<V>>
}

export type VariantProps<F extends (...args: any) => any> = Omit<
  NonNullable<Parameters<F>[0]>,
  'class' | 'className'
>

/**
 * Build a variant-aware class generator.
 *
 * The returned function accepts the variant selection plus an optional
 * `class` / `className` escape hatch and returns the combined Tailwind
 * class string. Compound variants run after the base + per-variant
 * classes, so they take precedence when the Tailwind ordering matters.
 */
export function cv<V extends VariantsConfig>(base: ClassValue, config: CvConfig<V>) {
  return function variantClass(
    props?: VariantSelection<V> & { class?: ClassValue; className?: ClassValue },
  ): string {
    const selected = { ...config.defaultVariants, ...props } as Record<string, unknown>

    const variantClasses: ClassValue[] = []
    for (const [key, valuesMap] of Object.entries(config.variants)) {
      const value = selected[key]
      if (value === undefined || value === null) continue
      const cls = valuesMap[value as string]
      if (cls) variantClasses.push(cls)
    }

    const compoundClasses: ClassValue[] = []
    if (config.compoundVariants) {
      for (const compound of config.compoundVariants) {
        const { class: cClass, className: _cn, ...match } = compound as any
        const matches = Object.entries(match).every(([k, v]) => {
          const current = selected[k]
          if (Array.isArray(v)) return (v as unknown[]).includes(current)
          return current === v
        })
        if (matches && cClass) compoundClasses.push(cClass as ClassValue)
      }
    }

    return clsx(
      base,
      ...variantClasses,
      ...compoundClasses,
      (props?.class ?? props?.className) as ClassValue,
    )
  }
}
