/* P.E.S.T Web App — shared helpers, store context, common UI atoms */
const { useState, useEffect, useRef, useContext, useMemo, useCallback } = React;

/* ---------- store context (provider lives in main.jsx) ---------- */
window.StoreCtx = window.StoreCtx || React.createContext(null);
function useStore() { return useContext(window.StoreCtx); }

/* ---------- toast (event-based, no context plumbing) ---------- */
function toast(msg, kind) {
  window.dispatchEvent(new CustomEvent("pest-toast", { detail: { msg, kind: kind || "ok" } }));
}
function ToastHost() {
  const [items, setItems] = useState([]);
  useEffect(() => {
    let id = 0;
    const on = (e) => {
      const t = { id: ++id, ...e.detail };
      setItems((x) => [...x, t]);
      setTimeout(() => setItems((x) => x.filter((i) => i.id !== t.id)), 2600);
    };
    window.addEventListener("pest-toast", on);
    return () => window.removeEventListener("pest-toast", on);
  }, []);
  return (
    <div className="toast-wrap">
      {items.map((t) => (
        <div className={"toast" + (t.kind === "err" ? " err" : "")} key={t.id}>
          <span className="tk">{t.kind === "err" ? <window.AIcon.close /> : <window.AIcon.check />}</span>
          {t.msg}
        </div>
      ))}
    </div>
  );
}

/* ---------- deterministic color for initials logo tiles ---------- */
function hueFromString(s) {
  let h = 0;
  for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) % 360;
  return h;
}
// Resolve a stored logo path to an actual URL:
//   "projects/x.png"  → nc-proxy.php?img=projects/x.png (admin-uploaded, served from Nextcloud)
//   "tokens/x.png"    → nc-proxy.php?img=tokens/x.png
//   "assets/..."      → as-is (static, served alongside the app)
//   "http(s)://..."   → as-is (external)
function resolveLogo(logo) {
  if (!logo) return null;
  if (/^https?:\/\//i.test(logo)) return logo;
  if (logo.indexOf("projects/") === 0 || logo.indexOf("tokens/") === 0) {
    return "nc-proxy.php?img=" + encodeURIComponent(logo);
  }
  return logo;
}

function LogoTile({ project, className }) {
  if (!project) return null;
  const src = resolveLogo(project.logo);
  if (src) {
    return (
      <div className={"logo-tile " + (className || "")}>
        <img src={src} alt={project.name} />
      </div>
    );
  }
  const h = hueFromString(project.name);
  const bg = `linear-gradient(150deg, hsl(${h} 70% 26%), hsl(${(h + 40) % 360} 65% 16%))`;
  const initials = project.name.replace(/[^A-Za-z0-9 ]/g, "").split(" ").map((w) => w[0]).join("").slice(0, 2).toUpperCase();
  return (
    <div className={"logo-tile " + (className || "")} style={{ background: bg }}>
      <span style={{ fontSize: "0.9em" }}>{initials}</span>
    </div>
  );
}

/* ---------- time ago ---------- */
function timeAgo(ts) {
  const d = Math.floor((Date.now() - ts) / 86400000);
  if (d <= 0) return "today";
  if (d === 1) return "yesterday";
  if (d < 7) return d + "d ago";
  if (d < 30) return Math.floor(d / 7) + "w ago";
  return Math.floor(d / 30) + "mo ago";
}

/* ---------- member helpers ---------- */
const BADGE_META = {
  leader: { full: "Leader", short: "Leader", cls: "lead" },
  "co-leader": { full: "Co-Leader", short: "Co-Lead", cls: "co" },
  "genetic-coder": { full: "Genetic Coder", short: "Coder", cls: "gcoder" },
  oracle: { full: "Oracle", short: "Oracle", cls: "oracle" },
};
function memberBadges(m) {
  const b = [];
  if (m.role === "leader") b.push("leader");
  if (m.role === "co-leader") b.push("co-leader");
  (m.tags || []).forEach((t) => BADGE_META[t] && b.push(t));
  return b;
}
function roleLabel(m) {
  if (m.role === "leader") return "Leader";
  if (m.role === "co-leader") return "Co-Leader";
  if ((m.tags || []).includes("oracle")) return "Oracle";
  if ((m.tags || []).includes("genetic-coder")) return "Genetic Coder";
  return "Core-Member";
}
function sortPriority(m) {
  if (m.role === "leader") return 0;
  if (m.role === "co-leader") return 1;
  if ((m.tags || []).includes("genetic-coder")) return 2;
  if ((m.tags || []).includes("oracle")) return 3;
  return 4;
}

/* honorary badges (additional to role) */
const HBADGE_ORDER = ["genius", "supervisor", "curator", "eventhost", "moderator"];
const HBADGE = {
  genius:     { label: "Genius",          tip: "The brain behind every calculation", icon: window.AIcon.genius,     cls: "hb-genius" },
  supervisor: { label: "Supervisor",      tip: "Keeps watch over the hive",           icon: window.AIcon.supervisor, cls: "hb-supervisor" },
  curator:    { label: "Content Curator", tip: "Content Curator",                     icon: window.AIcon.curator,    cls: "hb-curator" },
  eventhost:  { label: "Event Host",      tip: "Event Host",                          icon: window.AIcon.eventhost,  cls: "hb-eventhost" },
  moderator:  { label: "Moderator",       tip: "Moderator",                           icon: window.AIcon.moderator,  cls: "hb-moderator" },
};
function honoraryBadges(m) { return HBADGE_ORDER.filter((k) => (m.badges || []).includes(k)); }
function badgePriority(m) { const b = honoraryBadges(m); return b.length ? HBADGE_ORDER.indexOf(b[0]) : 99; }

function sortMembers(list) {
  return list.map((m, i) => [m, i]).sort((a, b) => sortPriority(a[0]) - sortPriority(b[0]) || badgePriority(a[0]) - badgePriority(b[0]) || a[1] - b[1]).map((x) => x[0]);
}
function memberInitials(m) { return m.nick.slice(0, 2); }

/* honorary medals overlay (on PFP edge) */
function HMedals({ m, big }) {
  const hb = honoraryBadges(m);
  if (!hb.length) return null;
  return (
    <div className={"hb-medals" + (big ? " big" : "")}>
      {hb.map((k) => { const B = HBADGE[k]; const I = B.icon; return <span className={"hb-medal " + B.cls} key={k} title={B.tip}><I /></span>; })}
    </div>
  );
}

/* member avatar */
function MAvatar({ m, idx, className, withMedals }) {
  const pfp = (m.pfps && m.pfps[idx || 0]) || (m.pfps && m.pfps[0]);
  const img = pfp && pfp.img;
  return (
    <div className={className || "m-av"}>
      {img ? <img src={img} alt={m.nick} /> : <div className="ph">{memberInitials(m)}</div>}
      {withMedals && <HMedals m={m} />}
    </div>
  );
}

Object.assign(window, {
  useStore, toast, ToastHost, LogoTile, resolveLogo, hueFromString, timeAgo,
  BADGE_META, memberBadges, roleLabel, sortMembers, sortPriority, MAvatar, memberInitials,
  HBADGE, HBADGE_ORDER, honoraryBadges, badgePriority, HMedals,
});
