// Monette Farms — status helpers + shared voting primitives.
//
// Vote tallies are the source of truth; they live server-side in Supabase
// (public.votes -> vote_tallies view) and are hydrated into window.SUPA_TALLIES
// at page load. A couple of small things stay in localStorage as visitor-local
// memory: this person's own pick, and their own spotted-events timeline.
const { useState, useMemo, useEffect, useRef } = React;
const D = window.MONETTE_DATA;
const Q = window.MONETTE_QUARTERS;
const OWN = D.ownership, LIST = D.listing, SEA = D.season;

const fmt = (n) => (n || 0).toLocaleString("en-CA");
const fmtM = (n) => n >= 1000000 ? "$" + (n / 1000000).toFixed(1) + "M" : "$" + fmt(n);
const now = () => new Date().toLocaleString("en-CA", { month: "short", day: "numeric", hour: "numeric", minute: "2-digit" });

// Initial per-quarter display guess: until the community tells us otherwise,
// every quarter is assumed still owned or rented by Monette Farms. No pre-
// filled sold / returned / unknown — those statuses only appear when real
// votes land.
function seedQuarter(propId, q, i) {
  const rng = (propId + q.loc).split("").reduce((a, c) => a + c.charCodeAt(0), 0);
  const ownershipKeys = ["owned-monette", "owned-monette", "owned-monette", "owned-monette", "rented-monette"];
  return {
    ownership: ownershipKeys[rng % ownershipKeys.length],
    listing: "not-listed",
    seeded: false,
    seededAt: null,
    harvested: false,
    harvestedAt: null,
    sprays: [],
  };
}

// Highest-count value wins. Used to promote the community's top vote into
// the single-status pills (OwnershipPill, ListingPill) and the rollup bar.
function topValue(counts, fallback) {
  if (!counts) return fallback;
  let best = fallback, bestN = 0;
  for (const [k, n] of Object.entries(counts)) { if (n > bestN) { best = k; bestN = n; } }
  return best;
}

// Merge server-side vote tallies into the base state. Winner of each category
// becomes the displayed ownership/listing; raw counts drive the vote bars.
function mergeTallies(propId, qloc, base) {
  const k = `${propId}:${qloc}`;
  const t = (window.SUPA_TALLIES && window.SUPA_TALLIES[k]) || {};
  return {
    ...base,
    ownership:      topValue(t.ownership, base.ownership),
    listing:        topValue(t.listing,   base.listing),
    ownershipVotes: { ...(t.ownership || {}) },
    listingVotes:   { ...(t.listing   || {}) },
    seasonVotes:    { ...(t.season    || {}) },
  };
}

// Visitor-local memory: their own spotted-events (seeded/harvested/sprayed
// timeline). Community-wide counts are computed from SUPA_TALLIES at render.
// Bumped namespace to 'v2' on Apr 22 to reset visitors whose browsers still
// held the pre-launch synthetic state. Old monette.q.* keys are simply ignored.
const LOCAL_NS = "monette.q.v2";
function loadVisitorLocal(propId, q, i) {
  const key = `${LOCAL_NS}.${propId}:${q.loc}`;
  try { const s = localStorage.getItem(key); if (s) return JSON.parse(s); } catch (e) {}
  return seedQuarter(propId, q, i);
}
function saveVisitorLocal(propId, qloc, st) {
  const { ownership, listing, seeded, seededAt, harvested, harvestedAt, sprays } = st;
  try { localStorage.setItem(`${LOCAL_NS}.${propId}:${qloc}`, JSON.stringify({ ownership, listing, seeded, seededAt, harvested, harvestedAt, sprays })); } catch (e) {}
}

// Synchronous read used by rollupProperty and the list/map views. Merges
// visitor-local pick with server tallies so the rollup reflects community data.
function loadQState(propId, q, i) {
  return mergeTallies(propId, q.loc, loadVisitorLocal(propId, q, i));
}
function saveQState(propId, qloc, st) { saveVisitorLocal(propId, qloc, st); }

// Per-quarter state hook. Votes are fire-and-forget inserts to Supabase;
// SUPA_TALLIES is bumped optimistically so the UI moves instantly. Visitor-
// local flags (seeded spotted / sprays timeline) stay in localStorage.
function useQuarter(propId, q, i) {
  const [local, setLocal] = useState(() => loadVisitorLocal(propId, q, i));
  const [, force] = useState(0);
  useEffect(() => {
    if (!window.onTalliesChange) return;
    return window.onTalliesChange(() => force((n) => n + 1));
  }, []);

  const st = mergeTallies(propId, q.loc, local);
  const update = (patch) => setLocal((prev) => {
    const n = { ...prev, ...patch };
    saveVisitorLocal(propId, q.loc, n);
    return n;
  });

  const insert = (cat, val, note) => {
    if (window.monetteInsertVote) window.monetteInsertVote(propId, q.loc, cat, val, note);
  };

  const voteOwn  = (k) => { update({ ownership: k }); insert("ownership", k); };
  const voteList = (k) => { update({ listing: k });   insert("listing",   k); };
  const toggleSeed = () => {
    const seeded = !local.seeded;
    update({ seeded, seededAt: seeded ? now() : null });
    if (seeded) insert("season", "seeded");
  };
  const toggleHarvest = () => {
    const harvested = !local.harvested;
    update({ harvested, harvestedAt: harvested ? now() : null });
    if (harvested) insert("season", "harvested");
  };
  const addSpray = (note) => {
    update({ sprays: [{ by: "you", at: now(), note: note || "Sprayer spotted" }, ...local.sprays] });
    insert("season", "sprayed", note || null);
  };
  return [st, { voteOwn, voteList, toggleSeed, toggleHarvest, addSpray, update }];
}

// Fold all quarters of one property into portfolio-level counts used by
// the list, editorial, and map views.
function rollupProperty(propId) {
  const quarters = ((Q && Q[propId]) || []).map((q, i) => ({ q, i, st: loadQState(propId, q, i) }));
  const rollup = { total: quarters.length, owned: 0, sold: 0, rented: 0, returned: 0, unknown: 0, forSale: 0, forRent: 0, seeded: 0, sprayed: 0, harvested: 0 };
  quarters.forEach(({ st }) => {
    if (st.ownership === "owned-monette")  rollup.owned++;
    if (st.ownership === "sold")            rollup.sold++;
    if (st.ownership === "rented-monette") rollup.rented++;
    if (st.ownership === "returned-to-ll") rollup.returned++;
    if (st.ownership === "unknown")         rollup.unknown++;
    if (st.listing === "listed-for-sale")   rollup.forSale++;
    if (st.listing === "listed-for-rent")   rollup.forRent++;
    const sv = st.seasonVotes || {};
    if ((sv.seeded    || 0) > 0 || st.seeded)               rollup.seeded++;
    if ((sv.sprayed   || 0) > 0 || (st.sprays && st.sprays.length)) rollup.sprayed++;
    if ((sv.harvested || 0) > 0 || st.harvested)            rollup.harvested++;
  });
  return { quarters, rollup };
}

// Headline ticker store. Community submissions persist alongside the
// starter feed in data.js. Local-only for now; future Supabase tips table
// will hydrate the same list via window.monetteSubmitTip.
function useHeadlines() {
  const [heads, setHeads] = useState(() => {
    try { const s = localStorage.getItem("monette.headlines"); if (s) return JSON.parse(s); } catch (e) {}
    return D.headlines;
  });
  const add = (text, author) => setHeads((prev) => {
    const n = [{ id: Date.now(), text, author: author || "@anon", when: "just now" }, ...prev];
    try { localStorage.setItem("monette.headlines", JSON.stringify(n)); } catch (e) {}
    return n;
  });
  return [heads, add];
}

// ------- UI PRIMITIVES -------
function StatusDot({ kind, size = 8 }) {
  const m = OWN[kind] || LIST[kind] || { color: "#6a6a6a" };
  return <span style={{ display: "inline-block", width: size, height: size, borderRadius: "50%", background: m.color, verticalAlign: "middle", marginRight: 6 }} />;
}

function OwnershipPill({ kind, compact }) {
  const m = OWN[kind] || OWN.unknown;
  return (
    <span style={{
      display: "inline-flex", alignItems: "center", gap: 5, fontFamily: '"JetBrains Mono", monospace',
      fontSize: compact ? 9 : 10, padding: compact ? "3px 6px" : "4px 8px",
      border: `1px solid ${m.color}`, color: m.color,
      letterSpacing: "0.06em", textTransform: "uppercase",
    }}>
      <span style={{ width: 5, height: 5, borderRadius: "50%", background: m.color }} />
      {m.short}
    </span>
  );
}

function ListingPill({ kind }) {
  if (kind === "not-listed") return null;
  const m = LIST[kind];
  return (
    <span style={{
      display: "inline-flex", alignItems: "center", gap: 5, fontFamily: '"JetBrains Mono", monospace',
      fontSize: 9, padding: "3px 6px", border: `1px dashed ${m.color}`, color: m.color,
      letterSpacing: "0.06em", textTransform: "uppercase",
    }}>◎ {m.label}</span>
  );
}

// Proportional bar showing ownership mix across a property's quarters.
function RollupBar({ rollup }) {
  const t = rollup.total || 1;
  const segs = [
    ["owned",    rollup.owned,    OWN["owned-monette"].color],
    ["rented",   rollup.rented,   OWN["rented-monette"].color],
    ["sold",     rollup.sold,     OWN.sold.color],
    ["returned", rollup.returned, OWN["returned-to-ll"].color],
    ["unknown",  rollup.unknown,  OWN.unknown.color],
  ];
  return (
    <div style={{ display: "flex", height: 6, background: "var(--rule)" }}>
      {segs.map(([k, v, c]) => v ? <div key={k} title={`${k}: ${v}`} style={{ width: `${(v / t) * 100}%`, background: c }} /> : null)}
    </div>
  );
}

// Shared vote-bars widget used for both ownership and listing votes.
function VoteBars({ entries, meta, active, onVote, dense }) {
  const total = Object.values(entries).reduce((a, b) => a + b, 0) || 1;
  return (
    <div style={{ display: "grid", gap: dense ? 3 : 4 }}>
      {Object.entries(meta).map(([k, m]) => {
        const cnt = entries[k] || 0;
        const pct = (cnt / total) * 100;
        const on = active === k;
        return (
          <button key={k} onClick={() => onVote(k)} style={{
            display: "grid", gridTemplateColumns: "auto 1fr auto", gap: 8, alignItems: "center",
            padding: dense ? "5px 7px" : "7px 9px",
            border: on ? `1.5px solid ${m.color}` : "1px solid var(--rule)",
            background: on ? "var(--paper-2)" : "transparent",
            cursor: "pointer", textAlign: "left", fontFamily: "inherit", color: "inherit",
          }}>
            <StatusDot kind={k} size={6} />
            <span style={{ display: "flex", alignItems: "center", gap: 8 }}>
              <span style={{ fontSize: 11 }}>{m.label || m.short}</span>
              <span style={{ flex: 1, height: 3, background: "var(--rule)", position: "relative", minWidth: 30 }}>
                <span style={{ position: "absolute", left: 0, top: 0, bottom: 0, width: `${pct}%`, background: m.color }} />
              </span>
            </span>
            <span style={{ fontFamily: '"JetBrains Mono", monospace', fontSize: 10, color: "var(--mute)", minWidth: 20, textAlign: "right" }}>{cnt}</span>
          </button>
        );
      })}
    </div>
  );
}

Object.assign(window, { D, Q, OWN, LIST, SEA, fmt, fmtM, now, useQuarter, rollupProperty, useHeadlines, StatusDot, OwnershipPill, ListingPill, RollupBar, VoteBars, seedQuarter, loadQState });
