/* iBD Coliseum — app shell, routing, tweaks */

function BrandLogo() {
  const [ok, setOk] = React.useState(true);
  return (
    <div className="brand">
      {ok
        ? <img className="brand-logo" src="lib/spigen-logo.png" alt="Spigen" onError={() => setOk(false)} />
        : <span className="spigen-logo" aria-label="Spigen">Spigen</span>}
      <span className="sep"></span>
      <span className="product">Coliseum</span>
    </div>
  );
}

const TWEAK_DEFAULTS = {
  content: "empty",
  accent: "#FF6A00",
  heroBg: "arena",
  density: "comfortable",
  layout: "cards",
  tone: "friendly",
};

function hexToRgb(hex) {
  const h = hex.replace("#", "");
  const n = parseInt(h.length === 3 ? h.split("").map(c => c + c).join("") : h, 16);
  return [(n >> 16) & 255, (n >> 8) & 255, n & 255];
}
function shade(hex, f) {
  const [r, g, b] = hexToRgb(hex);
  const m = (v) => Math.max(0, Math.min(255, Math.round(v * f)));
  return `#${[m(r), m(g), m(b)].map(v => v.toString(16).padStart(2, "0")).join("")}`;
}

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const { CARDS } = window.COLISEUM_DATA;
  const dbOn = window.COLISEUM_DB && window.COLISEUM_DB.isConfigured();

  const [session, setSession] = useState(null);
  const [profile, setProfile] = useState(null);
  const [authError, setAuthError] = useState("");
  const [dbLoading, setDbLoading] = useState(dbOn);

  const [screen, setScreen] = useState("gallery");
  const [cards, setCards] = useState(() => {
    if (dbOn || t.content !== "seeded") return [];
    const { seasonForDate } = window.COLISEUM_DATA;
    return CARDS.map((c, i) => {
      const created = new Date();
      if (i % 2 === 0) created.setMonth(created.getMonth() - 4);
      const s = seasonForDate(created);
      return { ...c, seasonKey: s.key, seasonLabel: s.label, createdAt: created.toISOString() };
    });
  });
  const [voted, setVoted] = useState(() => new Set());
  const [seasonVoteCount, setSeasonVoteCount] = useState(null);
  const [justVotedId, setJustVotedId] = useState(null);
  const [missionPoster, setMissionPoster] = useState(false);
  const [engagement, setEngagement] = useState(null);
  const [hofEntries, setHofEntries] = useState([]);

  const bumpEngagement = React.useCallback((name, field, delta) => {
    if (!name) return;
    setEngagement((prev) => {
      const base = prev || window.COLISEUM_DATA.ENGAGEMENT || {};
      const cur = base[name] || { displayName: name, team: "", upvotesGiven: 0, commentsMade: 0, submissions: 0 };
      const next = { ...base, [name]: { ...cur, [field]: Math.max(0, (cur[field] || 0) + delta) } };
      window.COLISEUM_DATA.ENGAGEMENT = next;
      return next;
    });
  }, []);

  const lastContent = React.useRef(t.content);
  React.useEffect(() => {
    if (dbOn) return;
    if (lastContent.current !== t.content) {
      lastContent.current = t.content;
      setCards(t.content === "seeded"
        ? CARDS.map((c, i) => {
          const created = new Date();
          if (i % 2 === 0) created.setMonth(created.getMonth() - 4);
          const s = window.COLISEUM_DATA.seasonForDate(created);
          return { ...c, seasonKey: s.key, seasonLabel: s.label, createdAt: created.toISOString() };
        })
        : []);
      setVoted(new Set());
      setSeasonVoteCount(null);
    }
  }, [t.content, dbOn]);

  const voteQuota = React.useMemo(() => {
    const { VOTES_PER_QUARTER, voteQuota: quotaFromVotes } = window.COLISEUM_DATA;
    if (dbOn && seasonVoteCount !== null) {
      return { used: seasonVoteCount, limit: VOTES_PER_QUARTER, remaining: Math.max(0, VOTES_PER_QUARTER - seasonVoteCount) };
    }
    return quotaFromVotes(voted, cards);
  }, [dbOn, seasonVoteCount, voted, cards]);

  const refreshFromDb = React.useCallback(async (userId) => {
    if (!dbOn || !userId) return;
    setDbLoading(true);
    try {
      if (window.COLISEUM_DB.ensureCalendarSeasons) {
        await window.COLISEUM_DB.ensureCalendarSeasons().catch((err) => {
          console.warn("ensureCalendarSeasons skipped:", err.message);
        });
      }
      const [entries, voteSet, seasonVotes, eng, hof, dbSeason] = await Promise.all([
        window.COLISEUM_DB.loadGalleryEntries(userId),
        window.COLISEUM_DB.loadUserUpvotes(userId),
        window.COLISEUM_DB.loadUserSeasonUpvoteCount(userId),
        window.COLISEUM_DB.loadEngagement(),
        window.COLISEUM_DB.loadHallOfFame(userId),
        window.COLISEUM_DB.loadCurrentSeason().catch((err) => {
          console.warn("loadCurrentSeason skipped:", err.message);
          return null;
        }),
      ]);
      setCards(entries);
      setVoted(voteSet);
      setSeasonVoteCount(seasonVotes);
      setHofEntries(hof);
      window.COLISEUM_DATA.DB_CURRENT_SEASON = dbSeason;
      const map = {};
      eng.forEach((e) => { map[e.displayName] = e; });
      window.COLISEUM_DATA.ENGAGEMENT = map;
      setEngagement(map);
    } catch (err) {
      console.error(err);
      setAuthError(err.message || "Could not load gallery from Supabase.");
    } finally {
      setDbLoading(false);
    }
  }, [dbOn]);

  React.useEffect(() => {
    if (!dbOn) return;
    const sb = window.COLISEUM_DB.getClient();

    const applySession = async (s) => {
      if (!s) {
        setSession(null);
        setProfile(null);
        setMissionPoster(false);
        setCards([]);
        setVoted(new Set());
        setSeasonVoteCount(null);
        setDbLoading(false);
        return;
      }
      const gate = await window.COLISEUM_DB.enforceAllowedSession(s);
      if (!gate.ok) {
        setSession(null);
        setProfile(null);
        setMissionPoster(false);
        setCards([]);
        setVoted(new Set());
        setSeasonVoteCount(null);
        setAuthError(gate.message);
        setDbLoading(false);
        return;
      }
      setSession(s);
      setAuthError("");
      try {
        const prof = await window.COLISEUM_DB.ensureProfile();
        setProfile(prof);
        const posted = await window.COLISEUM_DB.hasPostedMissions(s.user.id);
        setMissionPoster(posted);
      } catch (err) {
        console.error(err);
      }
      refreshFromDb(s.user.id);
    };

    sb.auth.getSession().then(({ data: { session: s } }) => applySession(s));
    const { data: { subscription } } = sb.auth.onAuthStateChange((_event, s) => {
      applySession(s);
    });
    return () => subscription.unsubscribe();
  }, [dbOn, refreshFromDb]);

  const [submitOpen, setSubmitOpen] = useState(false);
  const [editingCard, setEditingCard] = useState(null);
  const [profileAuthor, setProfileAuthor] = useState(null);
  const [detailCard, setDetailCard] = useState(null);
  const [avatarImg, setAvatarImg] = useState(() => {
    try { return localStorage.getItem("coliseum-avatar") || ""; } catch (e) { return ""; }
  });
  const fileRef = React.useRef(null);

  const displayName = profile?.display_name || "You";
  const isExecOrAdmin = profile?.role === "exec" || profile?.role === "admin";
  const isExecutive = isExecOrAdmin || missionPoster;
  const isAdmin = dbOn ? isExecutive : screen === "admin";
  const showCompetition = dbOn ? isExecutive : true;
  const userId = session?.user?.id || null;

  React.useEffect(() => {
    if (dbOn && !isExecutive && screen === "competition") setScreen("gallery");
  }, [dbOn, isExecutive, screen]);

  const openDetailFromCard = async (card) => {
    if (!card) return;
    if (dbOn && userId && card.id && window.COLISEUM_DB.loadEntryById) {
      try {
        const fresh = await window.COLISEUM_DB.loadEntryById(userId, card.id);
        if (fresh) {
          setDetailCard(fresh);
          return;
        }
      } catch (err) {
        console.error(err);
      }
    }
    setDetailCard(card);
  };

  const openCardDetail = (id) => {
    const c = cards.find((x) => x.id === id);
    if (c) openDetailFromCard(c);
  };

  const setMyTeam = async (team) => {
    if (dbOn && userId) {
      try {
        const updated = await window.COLISEUM_DB.updateTeam(userId, team);
        setProfile(updated);
      } catch (err) {
        console.error(err);
        setAuthError(err.message || "Could not update your team.");
      }
      return;
    }
    setProfile((p) => ({ ...(p || {}), team }));
  };

  const onAvatarPick = (e) => {
    const f = e.target.files && e.target.files[0];
    if (!f) return;
    const reader = new FileReader();
    reader.onload = () => {
      const url = reader.result;
      setAvatarImg(url);
      try { localStorage.setItem("coliseum-avatar", url); } catch (err) {}
    };
    reader.readAsDataURL(f);
    e.target.value = "";
  };

  const onVote = async (id) => {
    const card = cards.find((c) => c.id === id);
    if (card && window.COLISEUM_DATA.isOwnEntry(card, userId, displayName)) return;
    const isVoted = voted.has(id);
    const { VOTES_PER_QUARTER } = window.COLISEUM_DATA;
    const isCurrentSeason = card && window.COLISEUM_DATA.entryInCurrentSeason(card);
    if (!isVoted && isCurrentSeason && voteQuota.remaining <= 0) {
      setAuthError(`You've used all ${VOTES_PER_QUARTER} votes for this quarter.`);
      return;
    }
    if (dbOn && userId) {
      try {
        await window.COLISEUM_DB.toggleUpvote(userId, id, isVoted);
        setVoted(prev => {
          const next = new Set(prev);
          isVoted ? next.delete(id) : next.add(id);
          return next;
        });
        if (isCurrentSeason) {
          setSeasonVoteCount((n) => Math.max(0, (n ?? voteQuota.used) + (isVoted ? -1 : 1)));
        }
        setCards(cs => cs.map(c => c.id === id ? { ...c, votes: c.votes + (isVoted ? -1 : 1) } : c));
        bumpEngagement(displayName, "upvotesGiven", isVoted ? -1 : 1);
        if (!isVoted) { setJustVotedId(id); setTimeout(() => setJustVotedId(null), 360); }
      } catch (err) {
        console.error(err);
        setAuthError(err.message || "Could not save your vote.");
      }
      return;
    }
    setVoted(prev => {
      const next = new Set(prev);
      isVoted ? next.delete(id) : next.add(id);
      setCards(cs => cs.map(c => c.id === id ? { ...c, votes: c.votes + (isVoted ? -1 : 1) } : c));
      if (!isVoted) { setJustVotedId(id); setTimeout(() => setJustVotedId(null), 360); }
      return next;
    });
  };

  const onSubmit = async (data) => {
    if (dbOn && userId) {
      try {
        if (editingCard) {
          const updated = await window.COLISEUM_DB.updateEntry(userId, editingCard.id, data);
          setCards(cs => cs.map(c => c.id === editingCard.id ? updated : c));
        } else {
          const created = await window.COLISEUM_DB.createEntry(userId, data);
          setCards(cs => [created, ...cs]);
        }
      } catch (err) {
        console.error(err);
        setAuthError(err.message || "Could not save your submission.");
      }
      return;
    }
    if (editingCard) {
      setCards(cs => cs.map(c => c.id === editingCard.id
        ? {
          ...c,
          title: data.title,
          desc: data.desc,
          cat: data.cat,
          tools: data.tools,
          hasLink: !!data.skill,
          skill: data.skill || "",
          detail: { ...(c.detail || {}), prompt: data.prompt, outputImage: data.outputImage || "" },
        } : c));
      return;
    }
    const newCard = {
      id: "new-" + Date.now(), cat: data.cat, group: "C", title: data.title, desc: data.desc,
      author: displayName, team: profile?.team || "iBD · Pilot", tools: data.tools, votes: 0, when: "now",
      hasRepro: true, hasLink: !!data.skill, skill: data.skill || "",
      detail: { prompt: data.prompt, output: "", outputImage: data.outputImage || "" },
    };
    setCards(cs => [newCard, ...cs]);
  };

  const deleteCard = async (id) => {
    if (dbOn && userId) {
      try {
        await window.COLISEUM_DB.deleteEntry(id);
      } catch (err) {
        console.error(err);
        setAuthError(err.message || "Could not delete this card.");
        return;
      }
    }
    setCards(cs => cs.filter(c => c.id !== id));
    setVoted(prev => { const n = new Set(prev); n.delete(id); return n; });
  };

  const toggleHallOfFame = async (entry, value) => {
    if (dbOn && userId) {
      try {
        await window.COLISEUM_DB.setHallOfFame(entry.id, value);
        await refreshFromDb(userId);
      } catch (err) {
        console.error(err);
        setAuthError(err.message || "Could not update the Hall of Fame.");
      }
      return;
    }
    setHofEntries((prev) => {
      if (value) {
        if (prev.some((e) => e.id === entry.id)) return prev;
        return [{ ...entry, hallOfFame: true }, ...prev];
      }
      return prev.filter((e) => e.id !== entry.id);
    });
  };

  const archiveEntry = async (entry) => {
    if (!window.confirm("Archive this card? It disappears from the gallery, competitions, and Hall of Fame, but the submitter keeps the points they earned for it.")) return;
    if (dbOn && userId) {
      try {
        await window.COLISEUM_DB.setArchived(entry.id, true);
        await refreshFromDb(userId);
      } catch (err) {
        console.error(err);
        setAuthError(err.message || "Could not archive this card.");
      }
      return;
    }
    setCards((cs) => cs.filter((c) => c.id !== entry.id));
    setHofEntries((prev) => prev.filter((e) => e.id !== entry.id));
  };

  const onAddComment = async (entryId, text) => {
    if (!dbOn || !userId) return null;
    try {
      const added = await window.COLISEUM_DB.addComment(userId, entryId, text);
      bumpEngagement(displayName, "commentsMade", 1);
      return added;
    } catch (err) {
      console.error(err);
      setAuthError(err.message || "Could not post your comment.");
      return null;
    }
  };

  const openEditCard = (card) => { setEditingCard(card); setSubmitOpen(true); };
  const closeSubmit = () => { setSubmitOpen(false); setEditingCard(null); };

  const handleSignOut = async () => {
    if (dbOn) {
      await window.COLISEUM_DB.signOut();
      const next = encodeURIComponent(window.location.pathname + window.location.search);
      window.location.href = `/login.html?next=${next}`;
    }
  };

  React.useEffect(() => {
    if (!dbOn || session || dbLoading) return;
    const next = encodeURIComponent(window.location.pathname + window.location.search);
    window.location.replace(`/login.html?next=${next}`);
  }, [dbOn, session, dbLoading]);

  const [ar, ag, ab] = hexToRgb(t.accent);
  const appStyle = {
    "--accent": t.accent,
    "--accent-hover": shade(t.accent, 0.9),
    "--accent-pressed": shade(t.accent, 0.8),
    "--accent-wash": `rgba(${ar},${ag},${ab},0.08)`,
    "--accent-ring": `rgba(${ar},${ag},${ab},0.28)`,
  };

  const isGallery = screen === "gallery";
  const isSeason = screen === "season";
  const isComp = screen === "competition";
  const isHof = screen === "hof";
  const isGuide = screen === "guide";
  const isAdminScreen = screen === "admin";
  const hofIds = React.useMemo(() => new Set(hofEntries.map((e) => e.id)), [hofEntries]);
  const { authorStats, tierFor, initials } = window.COLISEUM_DATA;
  const youStats = authorStats(cards, displayName);
  const youTier = tierFor(youStats.points);
  const navInitials = isExecutive
    ? initials(displayName).slice(0, 2)
    : (displayName === "You" ? "YU" : initials(displayName));

  if (dbOn && !session) {
    return (
      <div className="app" style={appStyle}>
        <div className="auth-gate"><div className="auth-card"><p>Redirecting to sign in…</p></div></div>
      </div>
    );
  }

  if (dbOn && dbLoading && cards.length === 0) {
    return (
      <div className="app" style={appStyle}>
        <div className="auth-gate"><div className="auth-card"><p>Loading gallery…</p></div></div>
      </div>
    );
  }

  return (
    <div className="app" style={appStyle}>
      <div className="strip">
        Embedding AI into how we work<span className="dot">●</span>Share one thing you tried this week
        {dbOn && <button className="strip-signout" onClick={handleSignOut}>Sign out</button>}
      </div>

      {authError &&
        <div className="db-banner" role="alert">
          {authError}
          <button onClick={() => setAuthError("")}>Dismiss</button>
        </div>}

      <nav className="nav">
        <BrandLogo />
        <div className="nav-spacer"></div>
        <div className="seg">
          <button aria-pressed={isGuide} onClick={() => setScreen("guide")}>
            <Icon name="steps" className="ic" />Guide
          </button>
          <button aria-pressed={isGallery} onClick={() => setScreen("gallery")}>
            <Icon name="grid" className="ic" />Gallery
          </button>
          <button aria-pressed={isSeason} onClick={() => setScreen("season")}>
            <Icon name="trophy" className="ic" />Season
          </button>
          <button aria-pressed={isHof} onClick={() => setScreen("hof")}>
            <Icon name="person" className="ic" />Hall of Fame
          </button>
          {showCompetition &&
            <button aria-pressed={isComp} onClick={() => setScreen("competition")}>
              <Icon name="target" className="ic" />Competition
            </button>}
          {(isExecutive || !dbOn) &&
            <button aria-pressed={isAdminScreen} onClick={() => setScreen("admin")}>
              <Icon name="chart" className="ic" />Dashboard
            </button>}
        </div>
        <div className="who">
          <span
            className={"avatar" + (!isExecOrAdmin ? " up" : "")}
            onClick={!isExecOrAdmin ? () => fileRef.current && fileRef.current.click() : undefined}
            title={!isExecOrAdmin ? "Upload a profile photo" : undefined}
            style={(!isExecOrAdmin && avatarImg)
              ? { backgroundImage: `url("${avatarImg}")` }
              : { background: isExecOrAdmin ? "var(--ink)" : "var(--accent)" }}
          >
            {(!isExecOrAdmin && avatarImg) ? null : navInitials}
            {!isExecOrAdmin && <span className="cam"><Icon name="camera" className="ic" /></span>}
          </span>
          <input ref={fileRef} type="file" accept="image/*" onChange={onAvatarPick} style={{ display: "none" }} />
          <div className="meta">
            <div className="n">
              {isExecOrAdmin ? (
                <button className="nav-name" onClick={() => setProfileAuthor(displayName)} title="View your profile">
                  {displayName}<TierBadge points={youStats.points} size="sm" />
                </button>
              ) : (
                <button className="nav-name" onClick={() => setProfileAuthor(displayName)} title="View your profile">
                  {displayName}<TierBadge points={youStats.points} size="sm" />
                </button>
              )}
            </div>
            <div className="r">{isExecOrAdmin ? (profile?.team || "Executive") : (profile?.team || "Group C · viewer")}</div>
            {!isExecOrAdmin &&
              <button className="rank-tracker" onClick={() => setProfileAuthor(displayName)} title="View your rank progress">
                <div className="rt-top">
                  <span className="rt-label">Rank Tracker</span>
                  <span className="rt-goal">{youTier.next ? `${youTier.toNext.toLocaleString()} pts to ${youTier.next.name}` : "Top tier"}</span>
                </div>
                <div className="rt-bar"><i style={{ width: (youTier.progress * 100) + "%", background: youTier.next ? `linear-gradient(90deg, ${youTier.tier.color}, ${youTier.next.color})` : youTier.tier.color }}></i></div>
              </button>
            }
          </div>
        </div>
      </nav>

      {isGallery &&
        <Gallery cards={cards} voted={voted} onVote={onVote} justVotedId={justVotedId} voteQuota={voteQuota}
          openSubmit={() => setSubmitOpen(true)} openProfile={(a) => setProfileAuthor(a)}
          openDetail={openDetailFromCard} onEdit={openEditCard} onDelete={deleteCard} tweak={t}
          userId={userId} displayName={displayName} isExecOrAdmin={isExecutive} onAddComment={dbOn ? onAddComment : null}
          canHof={isExecutive} hofIds={hofIds} onHallOfFame={toggleHallOfFame} onArchive={archiveEntry} />}
      {isSeason &&
        <Season cards={cards} openCard={openCardDetail} hofIds={hofIds} />}
      {showCompetition && isComp && <Competition empty={!dbOn && t.content === "empty"} tone={t.tone} openDetail={openDetailFromCard} openCard={openCardDetail} dbOn={dbOn} userId={userId} displayName={displayName} isExecOrAdmin={isExecutive} voted={voted} onVote={onVote} onGalleryRefresh={() => userId && refreshFromDb(userId)} canHof={isExecutive} hofIds={hofIds} onHallOfFame={toggleHallOfFame} onArchive={archiveEntry} />}
      {isHof && <HallOfFame entries={hofEntries} canManage={isExecutive} onRemove={(e) => toggleHallOfFame(e, false)} onArchive={archiveEntry} openProfile={(a) => setProfileAuthor(a)} openDetail={openDetailFromCard} loading={dbOn && dbLoading} />}
      {isGuide && <GuidePage />}
      {isAdminScreen && <AdminDashboard cards={cards} empty={dbOn ? cards.length === 0 : t.content === "empty"} />}

      <footer className="foot">
        <div className="wrap">
          <div className="word"><img className="foot-logo" src="lib/spigen-logo.png" alt="Spigen" /><span style={{ fontStyle: "normal", fontWeight: 400, fontSize: 13, letterSpacing: "normal", marginLeft: 12, color: "rgba(255,255,255,0.45)" }}>iBD Coliseum — internal AI showcase</span></div>
          <div>Internal only · submissions retained as a learning asset</div>
        </div>
      </footer>

      {submitOpen && <SubmitModal onClose={closeSubmit} onSubmit={onSubmit} tone={t.tone} initial={editingCard} />}
      {profileAuthor && <ProfileModal stats={authorStats(cards, profileAuthor)} isYou={profileAuthor === displayName} avatarImg={avatarImg} myTeam={profile?.team || ""} onSetTeam={setMyTeam} onClose={() => setProfileAuthor(null)} />}
      {detailCard && <DetailModal card={detailCard} onClose={() => setDetailCard(null)} />}

      {!dbOn &&
        <TweaksPanel>
          <TweakSection label="Content" />
          <TweakRadio label="Gallery data" value={t.content} options={["empty", "seeded"]}
            onChange={(v) => setTweak("content", v)} />
          <TweakSection label="Theme" />
          <TweakColor label="Accent" value={t.accent}
            options={["#FF6A00", "#2A6FDB", "#1F8A5B", "#7A5AE0"]}
            onChange={(v) => setTweak("accent", v)} />
          <TweakRadio label="Hero backdrop" value={t.heroBg} options={["arena", "spotlight"]}
            onChange={(v) => setTweak("heroBg", v)} />
          <TweakSection label="Gallery layout" />
          <TweakRadio label="Card style" value={t.layout} options={["cards", "list"]}
            onChange={(v) => setTweak("layout", v)} />
          <TweakRadio label="Density" value={t.density} options={["comfortable", "compact"]}
            onChange={(v) => setTweak("density", v)} />
          <TweakSection label="Copy" />
          <TweakRadio label="Tone" value={t.tone} options={["friendly", "neutral"]}
            onChange={(v) => setTweak("tone", v)} />
        </TweaksPanel>}
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
