"use client";

/**
 * BeebBeeb shared UI primitives.
 *
 * This is the ONLY place a UI primitive may be defined, see CLAUDE-uiux-addition.md.
 * Visual contract: documents/design-system.md. Keep this file identical across
 * superadmin/ and investor/ (website/ deliberately does not use it).
 *
 * House rules encoded here:
 *  - buttons are always pills
 *  - every tinted surface goes through `tone` + the .tint formula, never a hand-picked hex
 *  - a tile's label is always --muted; color identifies the metric, not the text
 *  - numerals are tabular + LTR-isolated (.numeric) so Arabic never reorders money
 */

import { useEffect } from "react";
import { useLocale } from "@/i18n/LocaleProvider";
import { TranslationKey } from "@/i18n/dictionaries";

export type Tone =
  | "brand"
  | "success"
  | "danger"
  | "info"
  | "accent"
  | "warning"
  | "neutral";

const toneClass: Record<Tone, string> = {
  brand: "tone-brand",
  success: "tone-success",
  danger: "tone-danger",
  info: "tone-info",
  accent: "tone-accent",
  warning: "tone-warning",
  neutral: "tone-neutral",
};

const toneText: Record<Tone, string> = {
  brand: "text-brand",
  success: "text-success",
  danger: "text-danger",
  info: "text-info",
  accent: "text-accent",
  warning: "text-warning",
  neutral: "",
};

/* ------------------------------------------------------------------ layout */

export function PageHeader({
  titleKey,
  actions,
}: {
  titleKey: TranslationKey;
  actions?: React.ReactNode;
}) {
  const { t } = useLocale();
  return (
    <div className="mb-6 flex items-center justify-between gap-4">
      <h1 className="text-2xl font-bold tracking-tight">{t(titleKey)}</h1>
      {actions}
    </div>
  );
}

export function Card({
  children,
  className = "",
}: {
  children: React.ReactNode;
  className?: string;
}) {
  return <div className={`card p-5 ${className}`}>{children}</div>;
}

export function SectionTitle({ children }: { children: React.ReactNode }) {
  return <h2 className="mb-3 text-lg font-semibold">{children}</h2>;
}

/* ----------------------------------------------------------------- actions */

type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
  variant?: "primary" | "secondary" | "ghost" | "danger";
};

/**
 * Variant → class, written out in full.
 *
 * These MUST be literal strings. Tailwind scans source text, so a constructed name like
 * `btn-${variant}` is invisible to it and the rule is silently never emitted, the button
 * then renders as unstyled text.
 */
const buttonClass: Record<NonNullable<ButtonProps["variant"]>, string> = {
  primary: "btn-primary",
  secondary: "btn-secondary",
  ghost: "btn-ghost",
  danger: "btn-danger",
};

/** Always a pill. Exactly one `primary` per screen. */
export function Button({
  variant = "secondary",
  className = "",
  ...props
}: ButtonProps) {
  return <button className={`${buttonClass[variant]} ${className}`} {...props} />;
}

/* -------------------------------------------------------------- indicators */

export function IconChip({
  children,
  tone = "neutral",
  className = "",
}: {
  children: React.ReactNode;
  tone?: Tone;
  className?: string;
}) {
  return (
    <span className={`icon-chip ${toneClass[tone]} ${className}`}>{children}</span>
  );
}

export function Badge({ children }: { children: React.ReactNode }) {
  return (
    <span className="inline-flex items-center rounded-full border border-line bg-background px-2 py-0.5 text-xs font-medium text-muted">
      {children}
    </span>
  );
}

/** Tinted status pill. Always carries text, color is never the only signal. */
export function StatusBadge({
  children,
  tone = "neutral",
}: {
  children: React.ReactNode;
  tone?: Tone;
}) {
  return (
    <span className={`status-badge ${toneClass[tone]}`}>
      <span className="h-1.5 w-1.5 rounded-full bg-current" aria-hidden />
      {children}
    </span>
  );
}

/** Tabular, LTR-isolated numerals, money and timers must not reorder under RTL. */
export function Numeric({
  children,
  className = "",
}: {
  children: React.ReactNode;
  className?: string;
}) {
  return <span className={`numeric ${className}`}>{children}</span>;
}

/** Signed amount, colored by direction, with the currency after the figure. */
export function MoneyText({
  amount,
  currency = "SAR",
  signed = false,
  className = "",
}: {
  amount: number;
  currency?: string;
  signed?: boolean;
  className?: string;
}) {
  const tone =
    !signed || amount === 0 ? "" : amount > 0 ? "text-success" : "text-danger";
  const sign = signed && amount > 0 ? "+" : "";
  return (
    <span className={`font-semibold ${tone} ${className}`}>
      <Numeric>
        {sign}
        {amount.toFixed(2)}
      </Numeric>{" "}
      <span className="text-xs font-normal text-muted">{currency}</span>
    </span>
  );
}

/** Prototype form: icon chip on top, one big tabular value, muted caption. */
export function StatTile({
  label,
  value,
  tone = "neutral",
  icon,
  accent = false,
}: {
  label: string;
  value: string | number;
  tone?: Tone;
  icon?: React.ReactNode;
  /** @deprecated use `tone="brand"` */
  accent?: boolean;
}) {
  const resolved: Tone = accent && tone === "neutral" ? "brand" : tone;
  return (
    <div className="card p-5">
      {icon && (
        <IconChip tone={resolved} className="mb-3">
          {icon}
        </IconChip>
      )}
      <div className={`text-3xl font-bold ${toneText[resolved]}`}>
        <Numeric>{value}</Numeric>
      </div>
      <div className="mt-1 text-[13px] text-muted">{label}</div>
    </div>
  );
}

export function Toggle({
  checked,
  onChange,
  label,
}: {
  checked: boolean;
  onChange: (v: boolean) => void;
  label: string;
}) {
  return (
    <button
      type="button"
      role="switch"
      aria-checked={checked}
      aria-label={label}
      onClick={() => onChange(!checked)}
      className={`relative h-6 w-11 shrink-0 rounded-full transition ${
        checked ? "bg-brand" : "bg-line-strong"
      }`}
    >
      <span
        className={`absolute top-0.5 h-5 w-5 rounded-full bg-white transition-all ${
          checked ? "start-[22px]" : "start-0.5"
        }`}
      />
    </button>
  );
}

/* ------------------------------------------------------------------ inputs */

export function Field({
  label,
  error,
  icon,
  className = "",
  ...props
}: React.InputHTMLAttributes<HTMLInputElement> & {
  label?: string;
  error?: string;
  icon?: React.ReactNode;
}) {
  return (
    <div className={className}>
      {label && <label className="field-label">{label}</label>}
      <div className="relative">
        <input
          className={`field ${icon ? "pe-10" : ""} ${
            error ? "border-danger" : ""
          }`}
          aria-invalid={!!error}
          {...props}
        />
        {icon && (
          <span className="pointer-events-none absolute inset-y-0 end-3 flex items-center text-muted">
            {icon}
          </span>
        )}
      </div>
      {error && <p className="mt-1.5 text-xs text-danger">{error}</p>}
    </div>
  );
}

export function SegmentedControl<T extends string>({
  options,
  value,
  onChange,
}: {
  options: { value: T; label: string }[];
  value: T;
  onChange: (v: T) => void;
}) {
  return (
    <div
      role="tablist"
      className="inline-flex gap-1 rounded-full border border-line bg-surface p-1"
    >
      {options.map((o) => (
        <button
          key={o.value}
          role="tab"
          aria-selected={o.value === value}
          onClick={() => onChange(o.value)}
          className={`rounded-full px-4 py-1.5 text-sm font-medium transition ${
            o.value === value
              ? "bg-brand text-brand-ink"
              : "text-muted hover:text-foreground"
          }`}
        >
          {o.label}
        </button>
      ))}
    </div>
  );
}

export function Stepper({ step, total }: { step: number; total: number }) {
  return (
    <div className="flex items-center gap-3" aria-label={`${step} / ${total}`}>
      {Array.from({ length: total }, (_, i) => i + 1).map((n) => (
        <div key={n} className="flex flex-1 items-center gap-3 last:flex-none">
          <span
            className={`flex h-7 w-7 shrink-0 items-center justify-center rounded-full text-xs font-bold ${
              n <= step
                ? "bg-brand text-brand-ink"
                : "border border-line text-muted"
            }`}
          >
            <Numeric>{n}</Numeric>
          </span>
          {n < total && (
            <span
              className={`h-px flex-1 ${n < step ? "bg-brand" : "bg-line"}`}
            />
          )}
        </div>
      ))}
    </div>
  );
}

export function Rating({
  value,
  onChange,
  label,
}: {
  value: number;
  onChange: (v: number) => void;
  label: string;
}) {
  return (
    <div className="flex gap-2" role="radiogroup" aria-label={label}>
      {[1, 2, 3, 4, 5].map((n) => (
        <button
          key={n}
          role="radio"
          aria-checked={n === value}
          aria-label={`${n}`}
          onClick={() => onChange(n)}
          className={`text-2xl transition ${
            n <= value ? "text-brand" : "text-line-strong"
          }`}
        >
          ★
        </button>
      ))}
    </div>
  );
}

/** Icon chip + title + trailing value. The settings/list-row workhorse. */
export function ListRow({
  icon,
  tone = "neutral",
  title,
  value,
  onClick,
}: {
  icon?: React.ReactNode;
  tone?: Tone;
  title: string;
  value?: React.ReactNode;
  onClick?: () => void;
}) {
  const className =
    "flex w-full items-center gap-3 border-b border-line px-5 py-4 text-start last:border-b-0";
  const inner = (
    <>
      {icon && <IconChip tone={tone}>{icon}</IconChip>}
      <span className="flex-1 text-sm font-medium">{title}</span>
      {value && <span className="text-sm text-muted">{value}</span>}
    </>
  );
  return onClick ? (
    <button onClick={onClick} className={className}>
      {inner}
    </button>
  ) : (
    <div className={className}>{inner}</div>
  );
}

/* ------------------------------------------------------------ three states */

export function Spinner() {
  const { t } = useLocale();
  return (
    <div className="flex items-center gap-2 text-sm text-muted">
      <span className="h-3 w-3 animate-spin rounded-full border-2 border-line border-t-brand" />
      {t("common.loading")}
    </div>
  );
}

/** Loading state must match the final layout's shape, never a spinner on a blank page. */
export function Skeleton({ className = "" }: { className?: string }) {
  return (
    <span
      aria-hidden
      className={`relative block overflow-hidden rounded-chip bg-line ${className}`}
    >
      <span className="absolute inset-0 -translate-x-full animate-shimmer bg-gradient-to-r from-transparent via-white/10 to-transparent" />
    </span>
  );
}

export function SkeletonCard({ rows = 3 }: { rows?: number }) {
  return (
    <div className="card space-y-3 p-5">
      <Skeleton className="h-4 w-1/3" />
      {Array.from({ length: rows }).map((_, i) => (
        <Skeleton key={i} className="h-3 w-full" />
      ))}
    </div>
  );
}

export function SkeletonTable({ rows = 5 }: { rows?: number }) {
  return (
    <div className="card divide-y divide-line">
      {Array.from({ length: rows }).map((_, i) => (
        <div key={i} className="flex items-center gap-4 p-4">
          <Skeleton className="h-9 w-9 rounded-chip" />
          <Skeleton className="h-3 flex-1" />
          <Skeleton className="h-3 w-16" />
        </div>
      ))}
    </div>
  );
}

/** Real empty state, icon + copy + optional action, never a bare table header. */
export function EmptyState({
  icon,
  title,
  description,
  action,
}: {
  icon?: React.ReactNode;
  title: string;
  description?: string;
  action?: React.ReactNode;
}) {
  return (
    <div className="card flex flex-col items-center gap-3 p-10 text-center">
      {icon && (
        <IconChip tone="neutral" className="h-12 w-12">
          {icon}
        </IconChip>
      )}
      <p className="font-semibold">{title}</p>
      {description && (
        <p className="max-w-sm text-sm text-muted">{description}</p>
      )}
      {action}
    </div>
  );
}

/** Error state always offers a retry, never a raw error string. */
export function ErrorState({
  onRetry,
  message,
}: {
  onRetry?: () => void;
  message?: string;
}) {
  const { t } = useLocale();
  return (
    <div className="card tint tone-danger p-5">
      <p className="text-sm font-medium">{message ?? t("common.error")}</p>
      {onRetry && (
        <Button className="mt-3" onClick={onRetry}>
          {t("common.retry")}
        </Button>
      )}
    </div>
  );
}

/* ------------------------------------------------------------------ layers */

function useLockedScroll(open: boolean) {
  useEffect(() => {
    if (!open) return;
    const prev = document.body.style.overflow;
    document.body.style.overflow = "hidden";
    return () => {
      document.body.style.overflow = prev;
    };
  }, [open]);
}

function Scrim({ onClose }: { onClose: () => void }) {
  return (
    <div
      className="absolute inset-0 animate-fade-in bg-black/60"
      onClick={onClose}
    />
  );
}

/** Bottom sheet, grabber, top-rounded, scrim. The mobile-shaped disclosure. */
export function Sheet({
  open,
  onClose,
  title,
  children,
}: {
  open: boolean;
  onClose: () => void;
  title?: string;
  children: React.ReactNode;
}) {
  useLockedScroll(open);
  if (!open) return null;
  return (
    <div className="fixed inset-0 z-50 flex items-end justify-center">
      <Scrim onClose={onClose} />
      <div
        role="dialog"
        aria-modal="true"
        aria-label={title}
        className="relative w-full max-w-lg animate-sheet-in rounded-t-sheet border border-line bg-surface-raised p-5 pb-8"
      >
        <div className="mx-auto mb-4 h-1 w-10 rounded-full bg-line-strong" />
        {title && (
          <h2 className="mb-4 text-center text-lg font-semibold">{title}</h2>
        )}
        {children}
      </div>
    </div>
  );
}

/** Centered confirm dialog, icon on top, then title, body, actions. */
export function Dialog({
  open,
  onClose,
  icon,
  tone = "neutral",
  title,
  description,
  children,
  actions,
}: {
  open: boolean;
  onClose: () => void;
  icon?: React.ReactNode;
  tone?: Tone;
  title: string;
  description?: string;
  children?: React.ReactNode;
  actions?: React.ReactNode;
}) {
  useLockedScroll(open);
  if (!open) return null;
  return (
    <div className="fixed inset-0 z-50 flex items-center justify-center p-4">
      <Scrim onClose={onClose} />
      <div
        role="dialog"
        aria-modal="true"
        aria-label={title}
        className="relative w-full max-w-sm animate-dialog-in rounded-card border border-line bg-surface-raised p-6 text-center"
      >
        {icon && (
          <IconChip tone={tone} className="mx-auto mb-4 h-12 w-12">
            {icon}
          </IconChip>
        )}
        <h2 className="text-lg font-semibold">{title}</h2>
        {description && <p className="mt-1 text-sm text-muted">{description}</p>}
        {children && <div className="mt-4 text-start">{children}</div>}
        {actions && <div className="mt-6 flex gap-3">{actions}</div>}
      </div>
    </div>
  );
}

/* ------------------------------------------------------------ placeholders */

/** Placeholders stay honest, this is still a surface a stakeholder may see. */
export function ComingSoon({ titleKey }: { titleKey: TranslationKey }) {
  const { t } = useLocale();
  return (
    <div>
      <PageHeader titleKey={titleKey} />
      <EmptyState
        icon={<span className="text-xl">🛠</span>}
        title={t("common.comingSoon")}
      />
    </div>
  );
}
