/* Shared UI primitives. Exports to window at end. */

// domain color from hue
function domainColor(domainId, light = false) {
  const d = (window.DATA.domains || []).find((x) => x.id === domainId);
  const hue = d ? d.hue : 240;
  return light ? `hsl(${hue} 70% 96%)` : `hsl(${hue} 62% 55%)`;
}

// initials
function initials(name) {
  return name.split(/\s+/).slice(0, 2).map((w) => w[0]).join("").toUpperCase();
}

function Avatar({ name, size = 30, accent = false, square = false }) {
  const st = {
    width: size, height: size, borderRadius: square ? Math.round(size * 0.26) : 999, flex: "none",
    display: "grid", placeItems: "center",
    fontSize: size * 0.36, fontWeight: 650, letterSpacing: "-0.02em",
    background: accent ? "var(--accent-soft-2)" : "var(--surface-3)",
    color: accent ? "var(--accent)" : "var(--text-muted)",
    border: "1px solid var(--border)",
  };
  return React.createElement("div", { style: st }, initials(name));
}

/* Portrait - loads a real photo with graceful initials fallback. */
function Portrait({ p, w, h, radius = 6 }) {
  const [err, setErr] = React.useState(false);
  const common = { width: w, height: h, borderRadius: radius, flex: "none", objectFit: "cover", display: "block" };
  if (p.photo && !err) {
    return <img src={p.photo} alt={p.name} style={{ ...common, filter: "saturate(0.92) contrast(1.02)" }} onError={() => setErr(true)} />;
  }
  return (
    <div style={{ ...common, display: "grid", placeItems: "center", background: "#c9b2bf", color: "#5b3a4d", fontSize: h * 0.34, fontWeight: 650 }}>{initials(p.name)}</div>
  );
}

/* Person ID badge - original dark badge style, now with a real photo. */
function IDCard({ id, width = 178, dim = false, selected = false, photoSize = 40 }) {
  const D = window.DATA;
  const p = D.byId[id];
  if (!p) return null;
  const org = p.org ? D.byId[p.org] : null;
  const aff = org ? org.name : (p.team ? "The Wellspring Fund" : "Independent");
  const affColor = org ? domainColor(org.domain) : "var(--accent)";
  return (
    <div style={{
      width, background: "var(--surface)", borderRadius: 12,
      border: "1px solid var(--border-strong)",
      boxShadow: selected ? "0 0 0 4px var(--accent-ring), var(--shadow-lg)" : "var(--shadow)",
      overflow: "hidden", opacity: dim ? 0.32 : 1,
    }}>
      {/* accent header strip */}
      <div style={{ height: 4, background: p.team ? "var(--accent)" : affColor, opacity: 0.9 }} />
      <div style={{ padding: "9px 10px" }}>
        <div style={{ display: "flex", gap: 9, alignItems: "center" }}>
          {p.photo
            ? <Portrait p={p} w={photoSize} h={photoSize} radius={Math.round(photoSize * 0.24)} />
            : <Avatar name={p.name} size={photoSize} accent={p.team} square />}
          <div style={{ minWidth: 0, flex: 1 }}>
            <div style={{ fontSize: 12.5, fontWeight: 650, lineHeight: 1.15, letterSpacing: "-0.01em", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{p.name}</div>
            <div style={{ fontSize: 10.5, color: "var(--text-muted)", lineHeight: 1.25, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", marginTop: 1 }}>{p.title}</div>
          </div>
        </div>
        <div style={{ height: 1, background: "var(--border)", margin: "8px 0 7px" }} />
        <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 6 }}>
          <span className="mono" style={{ fontSize: 9.5, color: "var(--text-faint)", letterSpacing: "0.02em", flex: "none" }}>{p.idcode}</span>
          <span style={{ display: "inline-flex", alignItems: "center", gap: 4, minWidth: 0 }}>
            <span style={{ width: 6, height: 6, borderRadius: 999, background: p.team ? "var(--accent)" : affColor, flex: "none" }} />
            <span style={{ fontSize: 9.5, fontWeight: 600, color: "var(--text-muted)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{p.team ? "Team" : aff}</span>
          </span>
        </div>
      </div>
    </div>
  );
}

// funding badge
function FundingBadge({ funded }) {
  const st = {
    background: funded ? "var(--good-bg)" : "var(--surface-2)",
    color: funded ? "var(--good)" : "var(--text-muted)",
    borderColor: funded ? "transparent" : "var(--border)",
  };
  return (
    <span className="badge" style={st}>
      <span className="statdot" style={{ background: funded ? "var(--good)" : "var(--text-faint)" }} />
      {funded ? "Funded" : "Not funded"}
    </span>
  );
}

// relationship badge
function RelBadge({ value }) {
  const map = {
    "Grantee": { c: "var(--info)", b: "var(--info-bg)" },
    "Active supporter": { c: "var(--good)", b: "var(--good-bg)" },
    "Prospect": { c: "var(--warn)", b: "var(--warn-bg)" },
  };
  const s = map[value] || { c: "var(--text-muted)", b: "var(--surface-2)" };
  return <span className="badge" style={{ color: s.c, background: s.b, borderColor: "transparent" }}>{value}</span>;
}

// priority pill
function PriorityPill({ priority }) {
  if (!priority) return null;
  if (priority === "key")
    return <span className="badge" style={{ color: "var(--accent)", background: "var(--accent-soft)", borderColor: "transparent" }}>★ Key org</span>;
  return <span className="badge" style={{ color: "var(--rose)", background: "var(--rose-bg)", borderColor: "transparent" }}>High need</span>;
}

// kind icon + label
const KIND_META = {
  org: { icon: "building", label: "Organisation" },
  person: { icon: "user", label: "Person" },
  project: { icon: "folder", label: "Project" },
  event: { icon: "calendar", label: "Event" },
  funder: { icon: "target", label: "Funder" },
};

function KindBadge({ kind }) {
  const m = KIND_META[kind] || KIND_META.org;
  return (
    <span className="badge">
      <Icon name={m.icon} size={13} />
      {m.label}
    </span>
  );
}

// segmented control
function Segmented({ value, onChange, options }) {
  return (
    <div style={{ display: "inline-flex", padding: 3, gap: 2, background: "var(--surface-2)", border: "1px solid var(--border)", borderRadius: "var(--radius)" }}>
      {options.map((o) => {
        const active = o.value === value;
        return (
          <button key={o.value + (active ? "-on" : "-off")} className="tap focusable" onClick={() => onChange(o.value)}
            style={{
              display: "inline-flex", alignItems: "center", gap: 6, cursor: "pointer",
              font: "inherit", fontSize: 13.5, fontWeight: 560,
              padding: "8px 13px", borderRadius: 10, border: "1px solid transparent",
              background: active ? "var(--surface)" : "transparent",
              color: active ? "var(--text)" : "var(--text-muted)",
              boxShadow: active ? "var(--shadow-sm)" : "none",
            }}>
            {o.icon && <Icon name={o.icon} size={15} />}
            {o.label}
          </button>
        );
      })}
    </div>
  );
}

// small labelled field for read view
function FieldRow({ label, children, icon }) {
  return (
    <div style={{ display: "flex", gap: 12, padding: "13px 0", borderBottom: "1px solid var(--border)" }}>
      <div style={{ width: 140, flex: "none", display: "flex", alignItems: "center", gap: 8, color: "var(--text-muted)", fontSize: 14 }}>
        {icon && <Icon name={icon} size={15} />}{label}
      </div>
      <div style={{ flex: 1, fontSize: 15, color: "var(--text)", minWidth: 0 }}>{children || <span style={{ color: "var(--text-faint)" }}>-</span>}</div>
    </div>
  );
}

// section header inside panels
function SectionLabel({ children, right }) {
  return (
    <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", margin: "26px 0 12px" }}>
      <div className="t-eyebrow">{children}</div>
      {right}
    </div>
  );
}

// empty state
function EmptyState({ icon = "spark", title, body, action }) {
  return (
    <div className="grid-texture" style={{ display: "grid", placeItems: "center", padding: "52px 28px", textAlign: "center", border: "1px dashed var(--border-strong)", borderRadius: "var(--radius-lg)", background: "var(--surface-2)" }}>
      <div style={{ maxWidth: 340 }}>
        <div style={{ width: 52, height: 52, margin: "0 auto 16px", display: "grid", placeItems: "center", borderRadius: 16, background: "var(--surface)", border: "1px solid var(--border)", color: "var(--accent)" }}>
          <Icon name={icon} size={24} />
        </div>
        <div style={{ fontWeight: 600, fontSize: 17, marginBottom: 6 }}>{title}</div>
        <div style={{ color: "var(--text-muted)", fontSize: 14.5, lineHeight: 1.55 }}>{body}</div>
        {action && <div style={{ marginTop: 18 }}>{action}</div>}
      </div>
    </div>
  );
}

// relationship chip → clickable entity
function EntityChip({ id, onOpen }) {
  const e = window.DATA.byId[id];
  if (!e) return null;
  const kind = window.DATA.kindOf(id);
  const m = KIND_META[kind] || KIND_META.org;
  const dotColor = kind === "org" ? domainColor(e.domain) : "var(--text-faint)";
  return (
    <button className="chip focusable" onClick={() => onOpen && onOpen(id)}>
      {kind === "org"
        ? <span className="dot" style={{ background: dotColor }} />
        : <Icon name={m.icon} size={13} style={{ color: "var(--text-muted)" }} />}
      <span style={{ whiteSpace: "nowrap" }}>{e.name}</span>
    </button>
  );
}

/* ---- Type-coding language: shape + colour + icon per entity type ----
   org = rounded card · person = ID card · event = diamond · project = folder chip
   · funder = ring. Used by the map legend and Intel view.                */
function typeMeta(kind) {
  const M = {
    funder:  { label: "Funder",       icon: "target",   shape: "ring",    color: "var(--accent)" },
    org:     { label: "Organisation", icon: "building",  shape: "card",    color: "var(--info)" },
    person:  { label: "Person",       icon: "user",      shape: "id",      color: "var(--good)" },
    project: { label: "Project",      icon: "folder",    shape: "chip",    color: "var(--warn)" },
    event:   { label: "Event",        icon: "calendar",  shape: "diamond", color: "var(--rose)" },
  };
  return M[kind] || M.org;
}

// small glyph that conveys the entity TYPE by shape - for legends/dossiers
function EntityGlyph({ kind, size = 26, color }) {
  const m = typeMeta(kind);
  const c = color || m.color;
  const base = { width: size, height: size, flex: "none", display: "grid", placeItems: "center", color: c };
  if (m.shape === "ring")
    return <span style={{ ...base, borderRadius: 999, border: `2px solid ${c}`, background: "color-mix(in srgb," + "currentColor 16%, transparent)" }}><Icon name={m.icon} size={size * 0.5} /></span>;
  if (m.shape === "diamond")
    return <span style={{ ...base, transform: "rotate(45deg)", borderRadius: size * 0.18, border: `2px solid ${c}`, background: "color-mix(in srgb, " + c + " 14%, transparent)" }}><span style={{ transform: "rotate(-45deg)", display: "grid", placeItems: "center" }}><Icon name={m.icon} size={size * 0.46} /></span></span>;
  if (m.shape === "chip")
    return <span style={{ ...base, borderRadius: size * 0.28, border: `2px dashed ${c}`, background: "color-mix(in srgb, " + c + " 12%, transparent)" }}><Icon name={m.icon} size={size * 0.5} /></span>;
  if (m.shape === "id")
    return <span style={{ ...base, borderRadius: size * 0.22, border: `2px solid ${c}`, background: "color-mix(in srgb, " + c + " 14%, transparent)" }}><Icon name={m.icon} size={size * 0.5} /></span>;
  // card
  return <span style={{ ...base, borderRadius: size * 0.3, border: `2px solid ${c}`, background: "color-mix(in srgb, " + c + " 14%, transparent)" }}><Icon name={m.icon} size={size * 0.5} /></span>;
}

Object.assign(window, {
  domainColor, initials, Avatar, Portrait, IDCard, FundingBadge, RelBadge, PriorityPill,
  KindBadge, KIND_META, Segmented, FieldRow, SectionLabel, EmptyState, EntityChip,
  typeMeta, EntityGlyph,
});

/* ============================================================
   Shared module primitives - used by the operational pages
   (Funding Hub, Pipeline, Applications, Impact, Forecasting,
   Tasks, Documents, Workflows, Alerts).
   ============================================================ */
function useLocalState(key, initial) {
  const [v, setV] = React.useState(() => {
    try { const s = localStorage.getItem(key); return s != null ? JSON.parse(s) : initial; } catch (e) { return initial; }
  });
  React.useEffect(() => { try { localStorage.setItem(key, JSON.stringify(v)); } catch (e) {} }, [key, v]);
  return [v, setV];
}

/* full-page module wrapper with an editorial hero header */
function ModulePage({ icon, eyebrow, title, subtitle, actions, children, maxWidth = 1320, tabs, tab, onTab }) {
  return (
    <div style={{ position: "absolute", inset: 0, overflow: "auto", background: "var(--bg)" }}>
      <div style={{ width: "100%", maxWidth, margin: "0 auto", padding: "34px 40px 72px" }}>
        <div style={{ display: "flex", alignItems: "flex-end", justifyContent: "space-between", gap: 16, flexWrap: "wrap", marginBottom: tabs ? 16 : 24 }}>
          <div>
            <div style={{ display: "flex", alignItems: "center", gap: 11, marginBottom: 8 }}>
              <span style={{ width: 34, height: 34, flex: "none", borderRadius: 10, display: "grid", placeItems: "center", background: "var(--accent-soft)", color: "var(--accent)" }}><Icon name={icon} size={19} /></span>
              {eyebrow && <span className="t-eyebrow">{eyebrow}</span>}
            </div>
            <div className="display" style={{ fontSize: 34 }}>{title}</div>
            {subtitle && <div style={{ fontSize: 15, color: "var(--text-muted)", marginTop: 8, maxWidth: 720 }}>{subtitle}</div>}
          </div>
          {actions && <div style={{ display: "flex", gap: 10, flexWrap: "wrap" }}>{actions}</div>}
        </div>
        {tabs && (
          <div style={{ display: "flex", gap: 4, borderBottom: "1px solid var(--border)", marginBottom: 24 }}>
            {tabs.map((t) => {
              const on = tab === t.id;
              return <button key={t.id} className="focusable" onClick={() => onTab(t.id)}
                style={{ display: "inline-flex", alignItems: "center", gap: 7, font: "inherit", fontSize: 13.5, fontWeight: 550, cursor: "pointer", padding: "10px 14px", border: "none", borderBottom: "2px solid " + (on ? "var(--accent)" : "transparent"), background: "none", color: on ? "var(--text)" : "var(--text-muted)", marginBottom: -1 }}>
                <Icon name={t.icon} size={15} /> {t.label}{t.n != null && <span className="badge mono" style={{ fontSize: 10.5, padding: "1px 6px" }}>{t.n}</span>}
              </button>;
            })}
          </div>
        )}
        {children}
      </div>
    </div>
  );
}

function ModulePanel({ title, icon, iconColor, right, pad = 22, children, style }) {
  return (
    <div className="card" style={{ padding: pad, ...style }}>
      {(title || right) && <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 16 }}>
        {icon && <Icon name={icon} size={17} style={{ color: iconColor || "var(--text-muted)" }} />}
        {title && <div style={{ fontWeight: 650, fontSize: 15.5 }}>{title}</div>}
        {right && <div style={{ marginLeft: "auto" }}>{right}</div>}
      </div>}
      {children}
    </div>
  );
}

function StatTile({ icon, label, value, sub, accent, color, onClick }) {
  return (
    <div className={onClick ? "card focusable" : "card"} onClick={onClick} style={{ padding: 20, cursor: onClick ? "pointer" : "default" }}>
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 14 }}>
        <span style={{ width: 40, height: 40, borderRadius: 11, display: "grid", placeItems: "center", background: accent ? "var(--accent-soft)" : "var(--surface-3)", color: color || (accent ? "var(--accent)" : "var(--text-muted)") }}><Icon name={icon} size={20} /></span>
        {onClick && <Icon name="arrowRight" size={15} style={{ color: "var(--text-faint)" }} />}
      </div>
      <div className="mono" style={{ fontSize: 28, fontWeight: 600, letterSpacing: "-0.02em", lineHeight: 1, color: color || "var(--text)" }}>{value}</div>
      <div style={{ fontSize: 13, color: "var(--text-muted)", marginTop: 7 }}>{label}</div>
      {sub && <div style={{ fontSize: 12, color: "var(--text-faint)", marginTop: 2 }}>{sub}</div>}
    </div>
  );
}

/* AI recommendation callout - used across modules to surface NeuroAI suggestions */
function AINote({ title, children, onAction, actionLabel }) {
  return (
    <div style={{ display: "flex", gap: 12, padding: "15px 17px", borderRadius: 14, background: "var(--accent-soft)", border: "1px solid color-mix(in srgb, var(--accent) 26%, transparent)" }}>
      <span style={{ width: 30, height: 30, flex: "none", borderRadius: 9, display: "grid", placeItems: "center", background: "var(--accent)", color: "var(--accent-contrast)" }}><Icon name="spark" size={16} /></span>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 12, fontWeight: 700, color: "var(--accent)", letterSpacing: "0.02em", marginBottom: 3 }}>NEUROAI · {title}</div>
        <div style={{ fontSize: 13.5, color: "var(--text)", lineHeight: 1.55 }}>{children}</div>
        {onAction && <button className="btn btn-sm" style={{ marginTop: 11 }} onClick={onAction}>{actionLabel || "Apply"} <Icon name="arrowRight" size={14} /></button>}
      </div>
    </div>
  );
}

function relDay(isoStr) {
  const d = new Date(isoStr); const t = new Date(); t.setHours(0, 0, 0, 0);
  const diff = Math.round((d - t) / 86400000);
  if (diff === 0) return "Today";
  if (diff === 1) return "Tomorrow";
  if (diff === -1) return "Yesterday";
  if (diff < 0) return `${-diff}d overdue`;
  if (diff < 7) return `in ${diff}d`;
  return d.toLocaleDateString("en-GB", { day: "numeric", month: "short" });
}

Object.assign(window, { useLocalState, ModulePage, ModulePanel, StatTile, AINote, relDay });
