/**
 * Presentational primitives.
 *
 * Deliberately free of hooks and event handlers so they render in both server and client
 * components — most NABDH pages fetch on the server and only hydrate the interactive
 * islands, and this file is what lets that split stay cheap.
 */

import type { ReactNode } from 'react'
import Link from 'next/link'

import { cn } from '@/lib/cn'
import { styleForState, type StateStyle } from '@/lib/ui/state-styles'
import type { GridState } from '@/lib/domain/enums'

// ── Panel ───────────────────────────────────────────────────────────────────

export function Panel({
  className,
  children,
  as: Tag = 'section',
  glow = false,
}: {
  className?: string
  children: ReactNode
  as?: 'section' | 'div' | 'article' | 'aside'
  glow?: boolean
}) {
  return (
    <Tag
      className={cn(
        // `min-w-0` matters: a grid or flex child defaults to min-width:auto, which
        // refuses to shrink below its content and pushes the whole page wider than the
        // viewport on a narrow screen.
        'min-w-0 rounded-[--radius-panel] border border-border bg-surface/70 backdrop-blur-sm',
        glow && 'shadow-[0_0_0_1px_#24d07f22,0_18px_50px_-28px_#24d07f55]',
        className,
      )}
    >
      {children}
    </Tag>
  )
}

export function PanelHeader({
  title,
  subtitle,
  action,
  className,
  icon,
}: {
  title: ReactNode
  subtitle?: ReactNode
  action?: ReactNode
  className?: string
  icon?: ReactNode
}) {
  return (
    <div
      className={cn(
        'flex flex-wrap items-start justify-between gap-3 border-b border-border px-4 py-3 sm:px-5',
        className,
      )}
    >
      <div className="flex min-w-0 items-start gap-3">
        {icon ? <span className="mt-0.5 text-text-muted">{icon}</span> : null}
        <div className="min-w-0">
          <h2 className="truncate text-sm font-semibold tracking-wide text-text">{title}</h2>
          {subtitle ? (
            <p className="mt-0.5 text-xs leading-relaxed text-text-muted">{subtitle}</p>
          ) : null}
        </div>
      </div>
      {action ? <div className="min-w-0 max-w-full">{action}</div> : null}
    </div>
  )
}

export function PanelBody({ className, children }: { className?: string; children: ReactNode }) {
  return <div className={cn('p-4 sm:p-5', className)}>{children}</div>
}

// ── Page heading ────────────────────────────────────────────────────────────

export function PageHeader({
  title,
  subtitle,
  action,
  badge,
}: {
  title: string
  subtitle?: string
  action?: ReactNode
  badge?: ReactNode
}) {
  return (
    <header className="mb-6 flex flex-wrap items-end justify-between gap-4">
      <div className="min-w-0">
        <div className="flex flex-wrap items-center gap-3">
          <h1 className="text-xl font-semibold tracking-tight text-text sm:text-2xl">{title}</h1>
          {badge}
        </div>
        {subtitle ? (
          <p className="mt-1.5 max-w-3xl text-sm leading-relaxed text-text-muted">{subtitle}</p>
        ) : null}
      </div>
      {/* `min-w-0` rather than `shrink-0`: a header action wide enough to matter — an
          asset picker, a pair of buttons — must be allowed to wrap onto its own line on a
          narrow screen instead of pushing the page wider than the viewport. */}
      {action ? <div className="min-w-0 max-w-full">{action}</div> : null}
    </header>
  )
}

export function SectionTitle({
  children,
  className,
}: {
  children: ReactNode
  className?: string
}) {
  return (
    <h3
      className={cn(
        'text-[11px] font-semibold uppercase tracking-[0.14em] text-text-faint',
        className,
      )}
    >
      {children}
    </h3>
  )
}

// ── Badges ──────────────────────────────────────────────────────────────────

export function Badge({
  children,
  tone = 'neutral',
  className,
  dot = false,
}: {
  children: ReactNode
  tone?: 'neutral' | 'brand' | 'info' | 'accent' | 'muted'
  className?: string
  dot?: boolean
}) {
  const tones: Record<string, string> = {
    neutral: 'border-border-strong bg-surface-2 text-text-muted',
    brand: 'border-brand/35 bg-brand/12 text-brand',
    info: 'border-info/35 bg-info/12 text-info',
    accent: 'border-accent/35 bg-accent/12 text-accent',
    muted: 'border-transparent bg-surface-2 text-text-faint',
  }
  return (
    <span
      className={cn(
        'inline-flex items-center gap-1.5 rounded-full border px-2.5 py-0.5 text-[11px] font-medium whitespace-nowrap',
        tones[tone],
        className,
      )}
    >
      {dot ? <span className="size-1.5 rounded-full bg-current" /> : null}
      {children}
    </span>
  )
}

/**
 * A state chip. The label is always rendered — colour is a secondary cue, never the
 * only one.
 */
export function StateBadge({
  state,
  label,
  className,
  pulse = false,
  size = 'md',
}: {
  state: GridState
  label: string
  className?: string
  pulse?: boolean
  size?: 'sm' | 'md'
}) {
  const style = styleForState(state)
  return (
    <span
      className={cn(
        'inline-flex items-center gap-1.5 rounded-full border font-medium whitespace-nowrap',
        size === 'sm' ? 'px-2 py-0.5 text-[10px]' : 'px-2.5 py-0.5 text-[11px]',
        style.bg,
        style.border,
        style.text,
        className,
      )}
    >
      <span className={cn('size-1.5 rounded-full', style.dot, pulse && 'nabdh-pulse')} />
      {label}
    </span>
  )
}

/** Marks any figure that comes from the simulation rather than an operator system. */
export function DemoDataBadge({ label, title }: { label: string; title?: string }) {
  return (
    <span
      title={title}
      className="inline-flex items-center gap-1.5 rounded-full border border-accent/30 bg-accent/10 px-2.5 py-0.5 text-[11px] font-medium text-accent"
    >
      <span className="size-1.5 rounded-full bg-accent" />
      {label}
    </span>
  )
}

// ── Stat card ───────────────────────────────────────────────────────────────

export function StatCard({
  label,
  value,
  unit,
  hint,
  state,
  trend,
  href,
  className,
  emphasis = false,
}: {
  label: string
  value: ReactNode
  unit?: string
  hint?: ReactNode
  state?: GridState
  trend?: { value: string; direction: 'up' | 'down' | 'flat' }
  href?: string
  className?: string
  emphasis?: boolean
}) {
  const style: StateStyle | null = state ? styleForState(state) : null
  const body = (
    <>
      <div className="flex items-start justify-between gap-2">
        <p className="text-[11px] font-medium uppercase tracking-[0.1em] text-text-faint">{label}</p>
        {style ? <span className={cn('mt-1 size-2 rounded-full', style.dot)} /> : null}
      </div>
      <p
        className={cn(
          'tnum mt-2 flex items-baseline gap-1.5 font-semibold tracking-tight',
          emphasis ? 'text-3xl' : 'text-2xl',
          style ? style.text : 'text-text',
        )}
      >
        {value}
        {unit ? <span className="text-xs font-medium text-text-muted">{unit}</span> : null}
      </p>
      {hint ? <p className="mt-1.5 text-xs leading-relaxed text-text-muted">{hint}</p> : null}
      {trend ? (
        <p
          className={cn(
            'tnum mt-2 text-xs font-medium',
            trend.direction === 'up'
              ? 'text-critical'
              : trend.direction === 'down'
                ? 'text-normal'
                : 'text-text-faint',
          )}
        >
          {trend.direction === 'up' ? '▲' : trend.direction === 'down' ? '▼' : '■'} {trend.value}
        </p>
      ) : null}
    </>
  )

  const classes = cn(
    'rounded-[--radius-panel] border border-border bg-surface/70 p-4 transition-colors',
    href && 'hover:border-border-strong hover:bg-surface-2/70',
    className,
  )

  if (href) {
    return (
      <Link href={href} className={classes}>
        {body}
      </Link>
    )
  }
  return <div className={classes}>{body}</div>
}

// ── Key/value ───────────────────────────────────────────────────────────────

export function KeyValue({
  label,
  value,
  className,
  mono = false,
}: {
  label: ReactNode
  value: ReactNode
  className?: string
  mono?: boolean
}) {
  return (
    <div className={cn('flex items-baseline justify-between gap-4 py-1.5', className)}>
      <dt className="text-xs text-text-muted">{label}</dt>
      <dd className={cn('text-end text-sm font-medium text-text', mono && 'tnum font-mono')}>
        {value}
      </dd>
    </div>
  )
}

// ── Bars and meters ─────────────────────────────────────────────────────────

export function Meter({
  value,
  max = 100,
  state,
  className,
  label,
  showValue = false,
}: {
  value: number
  max?: number
  state?: GridState
  className?: string
  label?: string
  showValue?: boolean
}) {
  const pct = Math.max(0, Math.min(100, (value / max) * 100))
  const style = state ? styleForState(state) : null
  return (
    <div className={cn('w-full', className)}>
      {label || showValue ? (
        <div className="mb-1 flex items-baseline justify-between gap-2 text-xs">
          {label ? <span className="text-text-muted">{label}</span> : <span />}
          {showValue ? (
            <span className={cn('tnum font-medium', style ? style.text : 'text-text')}>
              {Math.round(value)}%
            </span>
          ) : null}
        </div>
      ) : null}
      <div
        className="h-1.5 w-full overflow-hidden rounded-full bg-surface-3"
        role="meter"
        aria-valuenow={Math.round(value)}
        aria-valuemin={0}
        aria-valuemax={max}
        aria-label={label}
      >
        <div
          className={cn('h-full rounded-full transition-[width] duration-500', style ? style.dot : 'bg-brand')}
          style={{ width: `${pct}%` }}
        />
      </div>
    </div>
  )
}

// ── Empty, error and loading states ─────────────────────────────────────────

export function EmptyState({
  title,
  body,
  action,
  className,
}: {
  title: string
  body?: string
  action?: ReactNode
  className?: string
}) {
  return (
    <div className={cn('flex flex-col items-center justify-center px-6 py-14 text-center', className)}>
      <div className="mb-3 flex size-11 items-center justify-center rounded-full border border-border bg-surface-2">
        <span aria-hidden className="block size-2 rounded-full bg-text-faint" />
      </div>
      <p className="text-sm font-medium text-text">{title}</p>
      {body ? <p className="mt-1.5 max-w-md text-xs leading-relaxed text-text-muted">{body}</p> : null}
      {action ? <div className="mt-4">{action}</div> : null}
    </div>
  )
}

export function ErrorState({
  title,
  body,
  action,
  className,
}: {
  title: string
  body?: string
  action?: ReactNode
  className?: string
}) {
  return (
    <div
      role="alert"
      className={cn(
        'flex flex-col items-center justify-center rounded-[--radius-panel] border border-critical/30 bg-critical/6 px-6 py-12 text-center',
        className,
      )}
    >
      <div className="mb-3 flex size-11 items-center justify-center rounded-full border border-critical/40 bg-critical/12">
        <span aria-hidden className="text-lg leading-none text-critical">!</span>
      </div>
      <p className="text-sm font-medium text-text">{title}</p>
      {body ? <p className="mt-1.5 max-w-md text-xs leading-relaxed text-text-muted">{body}</p> : null}
      {action ? <div className="mt-4">{action}</div> : null}
    </div>
  )
}

export function Skeleton({ className }: { className?: string }) {
  return (
    <div
      aria-hidden
      className={cn('relative overflow-hidden rounded-md bg-surface-2 nabdh-sweep', className)}
    />
  )
}

// ── Link styled as a button (usable from server components) ─────────────────

const BUTTON_BASE =
  'inline-flex items-center justify-center gap-2 rounded-lg text-sm font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-50'

export const BUTTON_VARIANTS: Record<string, string> = {
  primary: 'bg-brand text-[#052012] hover:bg-[#2ee08c]',
  secondary: 'border border-border-strong bg-surface-2 text-text hover:bg-surface-3',
  ghost: 'text-text-muted hover:bg-surface-2 hover:text-text',
  danger: 'border border-critical/40 bg-critical/12 text-critical hover:bg-critical/20',
  outline: 'border border-brand/40 bg-brand/8 text-brand hover:bg-brand/16',
}

export const BUTTON_SIZES: Record<string, string> = {
  sm: 'h-8 px-3 text-xs',
  md: 'h-9 px-4',
  lg: 'h-11 px-6 text-base',
}

export function buttonClass(
  variant: keyof typeof BUTTON_VARIANTS = 'primary',
  size: keyof typeof BUTTON_SIZES = 'md',
  className?: string,
) {
  return cn(BUTTON_BASE, BUTTON_VARIANTS[variant], BUTTON_SIZES[size], className)
}

export function LinkButton({
  href,
  children,
  variant = 'primary',
  size = 'md',
  className,
  external = false,
}: {
  href: string
  children: ReactNode
  variant?: keyof typeof BUTTON_VARIANTS
  size?: keyof typeof BUTTON_SIZES
  className?: string
  external?: boolean
}) {
  const classes = buttonClass(variant, size, className)
  if (external) {
    return (
      <a href={href} className={classes} rel="noreferrer noopener">
        {children}
      </a>
    )
  }
  return (
    <Link href={href} className={classes}>
      {children}
    </Link>
  )
}
