/* global React, I */
/* Pamp — shared components */

const { useState, useEffect, useRef, useMemo } = React;

/* ----------------------------- BRAND --------------------------------- */
function Brand({ collapsed = false }) {
  return (
    <div className="row-tight">
      <div className="brand-mark"><span>P</span></div>
      {!collapsed && <span className="brand-text">pamp</span>}
    </div>
  );
}

/* ----------------------------- AVATAR -------------------------------- */
function Avatar({ name = "A B", size, tone, src }) {
  const initials = name.split(" ").map((p) => p[0]).slice(0, 2).join("");
  const hue = useMemo(() => {
    let h = 0; for (let i = 0; i < name.length; i++) h = (h * 31 + name.charCodeAt(i)) % 360;
    return h;
  }, [name]);
  const cls = `av${size === "sm" ? " av-sm" : size === "lg" ? " av-lg" : size === "xl" ? " av-xl" : ""}`;
  const bg = tone === "accent" ? "var(--accent-soft)" : `oklch(92% 0.04 ${hue})`;
  const fg = tone === "accent" ? "var(--accent-ink)" : `oklch(38% 0.10 ${hue})`;
  if (src) return <div className={cls} style={{ background: `url(${src}) center/cover`, color: "transparent" }}>{initials}</div>;
  return (
    <div className={cls} style={{ background: bg, color: fg, borderColor: "transparent" }}>
      {initials}
    </div>
  );
}

function AvatarStack({ names = [], size = "sm", max = 4 }) {
  const visible = names.slice(0, max);
  const extra = names.length - visible.length;
  return (
    <div className="av-stack">
      {visible.map((n, i) => <Avatar key={i} name={n} size={size} />)}
      {extra > 0 && <div className={`av${size === "sm" ? " av-sm" : ""}`} style={{ background: "var(--surface-3)", color: "var(--ink-2)" }}>+{extra}</div>}
    </div>
  );
}

/* ----------------------------- BUTTON -------------------------------- */
function Btn({ children, variant = "primary", size, icon, iconRight, onClick, className = "", ...p }) {
  const sz = size === "sm" ? " btn-sm" : size === "lg" ? " btn-lg" : "";
  return (
    <button className={`btn btn-${variant}${sz} ${className}`} onClick={onClick} {...p}>
      {icon && <span className="row-tight">{icon}</span>}
      {children}
      {iconRight && <span className="row-tight">{iconRight}</span>}
    </button>
  );
}

function IconBtn({ children, onClick, title, className = "", ...p }) {
  return <button className={`icon-btn ${className}`} onClick={onClick} title={title} {...p}>{children}</button>;
}

/* ----------------------------- CHIP ---------------------------------- */
function Chip({ children, tone = "", icon }) {
  const c = tone ? `chip chip-${tone}` : "chip";
  return <span className={c}>{icon}{children}</span>;
}

/* ----------------------------- INPUT --------------------------------- */
function Input({ icon, placeholder, suffix, value, onChange, type = "text", style }) {
  return (
    <div className="input" style={style}>
      {icon && <span style={{ color: "var(--ink-3)", display: "inline-flex" }}>{icon}</span>}
      <input type={type} placeholder={placeholder} value={value} onChange={onChange} />
      {suffix}
    </div>
  );
}

/* ----------------------------- CARD ---------------------------------- */
function Card({ title, action, children, padded = true, className = "", style }) {
  return (
    <div className={`card ${className}`} style={{ padding: padded ? undefined : 0, ...style }}>
      {(title || action) && (
        <div className="row between" style={{ marginBottom: 16 }}>
          {title && <div className="h3">{title}</div>}
          {action}
        </div>
      )}
      {children}
    </div>
  );
}

/* ----------------------------- METRIC -------------------------------- */
function Metric({ label, value, delta, deltaTone = "pos", spark, icon, hero }) {
  return (
    <div className={`metric${hero ? " metric-hero" : ""}`}>
      <div className="metric-head">
        <div className="metric-label">{label}</div>
        {icon}
      </div>
      <div className="metric-value">{value}</div>
      <div className="metric-foot">
        {delta && <Chip tone={deltaTone}>{delta}</Chip>}
        {spark}
      </div>
    </div>
  );
}

/* ----------------------------- SIDEBAR ------------------------------- */
function Sidebar({ items = [], current, onNav, collapsed, footer }) {
  return (
    <aside className="sidebar">
      <div className="sidebar-head">
        <Brand collapsed={collapsed} />
      </div>
      <nav className="sidebar-nav">
        {items.map((it, i) => {
          if (it.section) return <div key={`s${i}`} className="sidebar-section">{it.section}</div>;
          if (it.divider) return <div key={`d${i}`} className="divider" style={{ margin: "10px 0" }} />;
          const Ic = it.icon;
          return (
            <button
              key={it.key}
              className={`nav-item${current === it.key ? " active" : ""}`}
              onClick={() => onNav?.(it.key)}
              title={collapsed ? it.label : undefined}
              data-tip={it.label}
            >
              <span className="ni-icon"><Ic size={18} /></span>
              <span className="ni-label">{it.label}</span>
              {it.badge && <span className="ni-badge">{it.badge}</span>}
            </button>
          );
        })}
      </nav>
      {footer && <div className="sidebar-foot">{footer}</div>}
    </aside>
  );
}

/* ----------------------------- TOPBAR -------------------------------- */
function Topbar({ onToggleSidebar, onTheme, theme, search = "Search anything", right }) {
  return (
    <div className="topbar">
      <IconBtn title="Toggle sidebar" onClick={onToggleSidebar}><I.panel size={18} /></IconBtn>
      <div className="input" style={{ flex: 1, maxWidth: 420 }}>
        <span style={{ color: "var(--ink-3)" }}><I.search size={16} /></span>
        <input placeholder={search} />
        <span className="kbd">⌘K</span>
      </div>
      <div className="spacer" />
      {right}
      <IconBtn title="Theme" onClick={onTheme}>
        {theme === "dark" ? <I.sun size={18} /> : <I.moon size={18} />}
      </IconBtn>
      <IconBtn title="Notifications" style={{ position: "relative" }}>
        <I.bell size={18} />
        <span style={{
          position: "absolute", top: 8, right: 8, width: 7, height: 7, borderRadius: "50%",
          background: "var(--accent)", boxShadow: "0 0 0 2px var(--bg)"
        }} />
      </IconBtn>
      <Avatar name="Alex Mor" />
    </div>
  );
}

/* ----------------------------- PAGE HEAD ----------------------------- */
function PageHead({ eyebrow, title, sub, right }) {
  return (
    <div className="page-head">
      <div className="page-title">
        {eyebrow && <div className="eyebrow">{eyebrow}</div>}
        <h1 className="h1">{title}</h1>
        {sub && <div className="muted" style={{ fontSize: 14 }}>{sub}</div>}
      </div>
      {right && <div className="row" style={{ gap: 8 }}>{right}</div>}
    </div>
  );
}

/* ----------------------------- SPARKLINE ----------------------------- */
function Spark({ data, width = 70, height = 28, color = "var(--accent)", filled = true, responsive = false }) {
  const min = Math.min(...data), max = Math.max(...data);
  const range = max - min || 1;
  const w = typeof width === "number" ? width : 200;
  const step = w / (data.length - 1);
  const pts = data.map((d, i) => [i * step, height - ((d - min) / range) * (height - 4) - 2]);
  const path = pts.map((p, i) => (i === 0 ? `M ${p[0]} ${p[1]}` : `L ${p[0]} ${p[1]}`)).join(" ");
  const area = `${path} L ${w} ${height} L 0 ${height} Z`;
  const gid = useMemo(() => `sg-${Math.random().toString(36).slice(2, 8)}`, []);
  return (
    <svg
      width={responsive ? "100%" : width}
      height={height}
      viewBox={`0 0 ${w} ${height}`}
      preserveAspectRatio="none"
      style={{ display: "block", maxWidth: "100%" }}
    >
      {filled && (
        <defs>
          <linearGradient id={gid} x1="0" x2="0" y1="0" y2="1">
            <stop offset="0%" stopColor={color} stopOpacity="0.25" />
            <stop offset="100%" stopColor={color} stopOpacity="0" />
          </linearGradient>
        </defs>
      )}
      {filled && <path d={area} fill={`url(#${gid})`} />}
      <path d={path} fill="none" stroke={color} strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" vectorEffect="non-scaling-stroke" />
    </svg>
  );
}

/* ----------------------------- LINE CHART --------------------------- */
function LineChart({ series = [], height = 220, labels = [], yTicks = 4, currency = false }) {
  const ref = useRef();
  const [w, setW] = useState(600);
  useEffect(() => {
    if (!ref.current) return;
    const ro = new ResizeObserver(([e]) => setW(e.contentRect.width));
    ro.observe(ref.current);
    return () => ro.disconnect();
  }, []);
  const pad = { l: 44, r: 16, t: 16, b: 28 };
  const innerW = w - pad.l - pad.r;
  const innerH = height - pad.t - pad.b;
  const all = series.flatMap(s => s.data);
  const min = Math.min(0, ...all);
  const max = Math.max(...all) * 1.1;
  const range = max - min || 1;
  const n = series[0]?.data.length || 0;
  const xStep = n > 1 ? innerW / (n - 1) : 0;
  const y = (v) => pad.t + innerH - ((v - min) / range) * innerH;
  const fmt = (v) => currency ? `$${v >= 1000 ? (v / 1000).toFixed(0) + "k" : v}` : v >= 1000 ? `${(v / 1000).toFixed(1)}k` : v;

  return (
    <div ref={ref} style={{ width: "100%", position: "relative" }}>
      <svg width={w} height={height} className="chart-grid chart-axis">
        {Array.from({ length: yTicks + 1 }).map((_, i) => {
          const v = min + (range * (yTicks - i) / yTicks);
          const yy = pad.t + (innerH * i / yTicks);
          return (
            <g key={i}>
              <line x1={pad.l} x2={w - pad.r} y1={yy} y2={yy} />
              <text x={pad.l - 10} y={yy + 3} textAnchor="end">{fmt(Math.round(v))}</text>
            </g>
          );
        })}
        {labels.map((lbl, i) => (
          <text key={i} x={pad.l + i * xStep} y={height - 8} textAnchor="middle">{lbl}</text>
        ))}
        {series.map((s, si) => {
          const pts = s.data.map((d, i) => [pad.l + i * xStep, y(d)]);
          const path = pts.map((p, i) => (i === 0 ? `M ${p[0]} ${p[1]}` : `L ${p[0]} ${p[1]}`)).join(" ");
          const area = `${path} L ${pad.l + (n - 1) * xStep} ${pad.t + innerH} L ${pad.l} ${pad.t + innerH} Z`;
          const gid = `lc-${si}-${Math.random().toString(36).slice(2, 6)}`;
          return (
            <g key={si}>
              <defs>
                <linearGradient id={gid} x1="0" x2="0" y1="0" y2="1">
                  <stop offset="0%" stopColor={s.color} stopOpacity={si === 0 ? 0.18 : 0.08} />
                  <stop offset="100%" stopColor={s.color} stopOpacity="0" />
                </linearGradient>
              </defs>
              {si === 0 && <path d={area} fill={`url(#${gid})`} />}
              <path d={path} fill="none" stroke={s.color} strokeWidth="2" strokeDasharray={s.dashed ? "4 4" : undefined} strokeLinecap="round" strokeLinejoin="round" />
              {pts.map((p, i) => (
                <circle key={i} cx={p[0]} cy={p[1]} r={i === pts.length - 1 ? 4 : 0} fill="var(--surface)" stroke={s.color} strokeWidth="2" />
              ))}
            </g>
          );
        })}
      </svg>
    </div>
  );
}

/* ----------------------------- BAR CHART ---------------------------- */
function BarChart({ data = [], labels = [], height = 200, activeIndex, color = "var(--accent)", trackColor = "var(--surface-3)" }) {
  const max = Math.max(...data) * 1.1 || 1;
  return (
    <div style={{ display: "flex", alignItems: "flex-end", gap: 12, height, padding: "8px 0" }}>
      {data.map((v, i) => {
        const h = (v / max) * (height - 32);
        const isActive = activeIndex === i;
        return (
          <div key={i} style={{ flex: 1, display: "flex", flexDirection: "column", alignItems: "center", gap: 8 }}>
            <div style={{ flex: 1, width: "100%", display: "flex", alignItems: "flex-end", position: "relative" }}>
              <div style={{
                width: "100%",
                height: h,
                background: isActive ? color : trackColor,
                borderRadius: 8,
                position: "relative",
                transition: "background 0.2s",
              }}>
                {isActive && <div style={{
                  position: "absolute", top: -28, left: "50%", transform: "translateX(-50%)",
                  background: "var(--ink)", color: "var(--bg)", padding: "3px 8px", borderRadius: 6,
                  fontSize: 11, fontFamily: "var(--font-mono)", whiteSpace: "nowrap"
                }}>{v.toLocaleString()}</div>}
              </div>
            </div>
            <div style={{ fontSize: 11, color: isActive ? "var(--ink)" : "var(--ink-3)", fontWeight: isActive ? 600 : 400 }}>{labels[i]}</div>
          </div>
        );
      })}
    </div>
  );
}

/* ----------------------------- RING (donut) --------------------------- */
function Ring({ value = 0, size = 120, stroke = 10, color = "var(--accent)", track = "var(--surface-3)", label, sub }) {
  const r = (size - stroke) / 2;
  const C = 2 * Math.PI * r;
  const dash = (value / 100) * C;
  return (
    <div style={{ position: "relative", width: size, height: size }}>
      <svg width={size} height={size}>
        <circle cx={size / 2} cy={size / 2} r={r} stroke={track} strokeWidth={stroke} fill="none" />
        <circle cx={size / 2} cy={size / 2} r={r} stroke={color} strokeWidth={stroke}
          fill="none" strokeDasharray={`${dash} ${C}`} strokeLinecap="round"
          transform={`rotate(-90 ${size / 2} ${size / 2})`} />
      </svg>
      <div style={{ position: "absolute", inset: 0, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center" }}>
        <div className="display" style={{ fontSize: size * 0.28, lineHeight: 1 }}>{label}</div>
        {sub && <div className="muted" style={{ fontSize: 11, marginTop: 4 }}>{sub}</div>}
      </div>
    </div>
  );
}

/* ----------------------------- GAUGE (half-ring) --------------------- */
function Gauge({ value = 0, size = 160, color = "var(--accent)", track = "var(--surface-3)", label, sub, segments = 36 }) {
  const cx = size / 2;
  const cy = size * 0.72;
  const r = size * 0.40;
  const arc = 180;
  const filled = Math.round((value / 100) * segments);
  return (
    <div style={{ width: size, height: size * 0.82, position: "relative" }}>
      <svg width={size} height={size * 0.82}>
        {Array.from({ length: segments }).map((_, i) => {
          const t = (i / (segments - 1)) * arc - 180;
          const rad = (t * Math.PI) / 180;
          const x1 = cx + Math.cos(rad) * (r - 8);
          const y1 = cy + Math.sin(rad) * (r - 8);
          const x2 = cx + Math.cos(rad) * (r + 8);
          const y2 = cy + Math.sin(rad) * (r + 8);
          return <line key={i} x1={x1} y1={y1} x2={x2} y2={y2} stroke={i < filled ? color : track} strokeWidth="3" strokeLinecap="round" />;
        })}
      </svg>
      <div style={{ position: "absolute", left: 0, right: 0, bottom: 4, textAlign: "center" }}>
        <div className="display" style={{ fontSize: size * 0.22, lineHeight: 1 }}>{label}</div>
        {sub && <div className="muted tiny" style={{ marginTop: 4 }}>{sub}</div>}
      </div>
    </div>
  );
}

/* ----------------------------- HEAT / GRID --------------------------- */
function Heat({ data, cols = 24, color = "var(--accent)" }) {
  return (
    <div style={{ display: "grid", gridTemplateColumns: `repeat(${cols}, 1fr)`, gap: 3 }}>
      {data.map((v, i) => (
        <div key={i} style={{
          aspectRatio: "1",
          background: v === 0 ? "var(--surface-3)" : color,
          opacity: v === 0 ? 1 : 0.18 + Math.min(0.82, v / 10) * 0.82,
          borderRadius: 3,
        }} />
      ))}
    </div>
  );
}

/* ----------------------------- PLACEHOLDER IMG ---------------------- */
function Placeholder({ label = "image", w = "100%", h = 120, radius }) {
  return (
    <div className="placeholder-img" style={{ width: w, height: h, borderRadius: radius }}>
      [ {label} ]
    </div>
  );
}

window.S = {
  Brand, Avatar, AvatarStack, Btn, IconBtn, Chip, Input, Card, Metric, Sidebar, Topbar,
  PageHead, Spark, LineChart, BarChart, Ring, Gauge, Heat, Placeholder,
};

/* ============================================================
   PAMP HOOKS + FORMATTERS
   ============================================================ */

/* useHashRoute — bookmarkable URLs while keeping React state */
function useHashRoute(defaultRoute) {
  const parse = () => {
    const hash = window.location.hash.replace(/^#/, "");
    if (!hash || hash === "/") return { ...defaultRoute };
    const parts = hash.split("/").filter(Boolean); // e.g. ["admin","wallets"]
    if (parts.length === 0) return { ...defaultRoute };
    const name = parts[0];
    if (["admin", "user", "vendor"].includes(name)) {
      return { name, page: parts[1] || "dashboard" };
    }
    if (name === "auth") return { name: "auth", role: parts[1] || "user" };
    return { name };
  };
  const [route, setRouteState] = React.useState(parse);

  React.useEffect(() => {
    const handler = () => setRouteState(parse());
    window.addEventListener("hashchange", handler);
    return () => window.removeEventListener("hashchange", handler);
  }, []);

  const setRoute = (next) => {
    let hash = "/";
    if (next.name === "auth")  hash = `/auth/${next.role || "user"}`;
    else if (["admin","user","vendor"].includes(next.name)) {
      hash = `/${next.name}/${next.page || "dashboard"}`;
    } else if (next.name && next.name !== "landing") {
      hash = `/${next.name}`;
    }
    if (window.location.hash !== "#" + hash) {
      window.location.hash = hash;
    } else {
      setRouteState(next);
    }
  };

  return [route, setRoute];
}

/* useAdminSession — caches profile + accessToken once */
function useAdminSession() {
  const [session, setSession] = React.useState(window._PAMP_ADMIN_SESSION || null);

  React.useEffect(() => {
    if (session) return;
    let alive = true;
    (async () => {
      const SB = window.PampSupabase;
      if (!SB) return;
      const sess = await SB.getSession();
      if (!sess || !alive) return;
      const profile = await SB.getProfileWithToken(sess.user.id, sess.access_token);
      const next = { profile, accessToken: sess.access_token, user: sess.user };
      window._PAMP_ADMIN_SESSION = next;
      setSession(next);
    })();
    return () => { alive = false; };
  }, []);

  return [session, setSession];
}

function useDebounce(value, delay = 250) {
  const [v, setV] = React.useState(value);
  React.useEffect(() => {
    const id = setTimeout(() => setV(value), delay);
    return () => clearTimeout(id);
  }, [value, delay]);
  return v;
}

/* ── Formatters ─────────────────────────────────────────── */
function fmtMoney(n, cur) {
  const num = Number(n || 0);
  const sym = cur === "GHS" ? "GH₵" : "$";
  return `${sym}${num.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
}
function fmtDate(s) {
  if (!s) return "—";
  const d = new Date(s);
  if (Number.isNaN(d.getTime())) return "—";
  return d.toLocaleDateString(undefined, { month: "short", day: "numeric", year: "numeric" });
}
function fmtDateTime(s) {
  if (!s) return "—";
  const d = new Date(s);
  if (Number.isNaN(d.getTime())) return "—";
  return d.toLocaleString(undefined, { month: "short", day: "numeric", hour: "numeric", minute: "2-digit" });
}
function fmtTime(t) {
  if (!t) return "—";
  // t is "HH:MM:SS" or "HH:MM"
  const [h, m] = t.split(":").map(Number);
  const date = new Date();
  date.setHours(h, m, 0);
  return date.toLocaleTimeString(undefined, { hour: "numeric", minute: "2-digit" });
}
function statusLabel(s) {
  if (!s) return "—";
  return s.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
}
function statusTone(s) {
  if (!s) return "";
  if (["active","completed","confirmed","approved","paid","resolved","success"].includes(s)) return "pos";
  if (["cancelled","declined","failed","banned","suspended","expired","closed"].includes(s)) return "neg";
  if (["pending","in_progress","draft"].includes(s)) return "warn";
  return "info";
}

/* ── Loading / Empty / Demo helpers ─────────────────────── */
function LoadingState({ label = "Loading…" }) {
  return (
    <div className="loading-state">
      <div className="spin" />
      <div>{label}</div>
    </div>
  );
}
function EmptyState({ icon, title, sub, action }) {
  return (
    <div className="empty-state">
      {icon && <div className="empty-icon">{icon}</div>}
      <div style={{ fontWeight: 500, color: "var(--ink-2)", marginBottom: 4 }}>{title}</div>
      {sub && <div className="tiny">{sub}</div>}
      {action && <div style={{ marginTop: 14 }}>{action}</div>}
    </div>
  );
}
function DemoChip() {
  return <span className="demo-chip" title="This screen uses mock data — backend model not implemented yet">Demo data</span>;
}

window.useHashRoute = useHashRoute;
window.useAdminSession = useAdminSession;
window.useDebounce = useDebounce;
window.fmtMoney = fmtMoney;
window.fmtDate = fmtDate;
window.fmtDateTime = fmtDateTime;
window.fmtTime = fmtTime;
window.statusLabel = statusLabel;
window.statusTone = statusTone;
window.LoadingState = LoadingState;
window.EmptyState = EmptyState;
window.DemoChip = DemoChip;

/* ── DemoBanner — non-intrusive banner for un-wired pages ── */
function DemoBanner({ feature }) {
  return (
    <div style={{
      background: "var(--warn-soft)",
      color: "var(--warn)",
      borderRadius: "var(--r-md)",
      padding: "10px 16px",
      marginBottom: 20,
      fontSize: 12.5,
      display: "flex",
      alignItems: "center",
      gap: 10,
      border: "1px dashed currentColor",
    }}>
      <window.DemoChip />
      <span>This view is rendering mock data. Backend wiring for <strong>{feature}</strong> is coming in a later phase.</span>
    </div>
  );
}
window.DemoBanner = DemoBanner;
