/* Votebox — feedback board.
   Buildless: compiled in-browser by Babel.
   Talks to the Pages Functions API under /api. */

const BASE = "/feedback";

const STATUS_META = {
  open:         { label: "Open",         cls: "vb-st-open" },
  under_review: { label: "Under Review", cls: "vb-st-under_review" },
  planned:      { label: "Planned",      cls: "vb-st-planned" },
  in_progress:  { label: "In Progress",  cls: "vb-st-in_progress" },
  complete:     { label: "Complete",     cls: "vb-st-complete" },
  closed:       { label: "Closed",       cls: "vb-st-closed" },
};
const STATUS_ORDER = ["open", "under_review", "planned", "in_progress", "complete", "closed"];

const SORTS = [
  ["top",      "Most voted"],
  ["newest",   "Newest"],
  ["activity", "Recent activity"],
];

/* ---------- API helper ---------- */

async function api(path, opts = {}) {
  const res = await fetch(path, {
    credentials: "same-origin",
    headers: opts.body ? { "Content-Type": "application/json" } : undefined,
    ...opts,
  });
  let data = null;
  try { data = await res.json(); } catch {}
  if (!res.ok) throw new Error((data && data.error) || `Request failed (${res.status})`);
  return data;
}

/* ---------- Utilities ---------- */

function timeAgo(unixSec) {
  const s = Math.max(0, Math.floor(Date.now() / 1000) - unixSec);
  const units = [["y", 31536000], ["mo", 2592000], ["w", 604800], ["d", 86400], ["h", 3600], ["m", 60]];
  for (const [label, secs] of units) {
    const n = Math.floor(s / secs);
    if (n >= 1) return `${n}${label} ago`;
  }
  return "just now";
}

function StatusBadge({ status }) {
  const meta = STATUS_META[status] || STATUS_META.open;
  return (
    <span className={`vb-badge ${meta.cls}`}>
      <span className="vb-dot" />{meta.label}
    </span>
  );
}

/* ---------- Avatar (initials) ---------- */

const AV_COLORS = ["#7c5cff","#e08a2b","#16a34a","#0ea5e9","#ef4444","#a855f7","#5b4ba1"];
function initials(str) {
  return (str || "?").split(/[\s@._-]+/).filter(Boolean).slice(0, 2).map(s => s[0].toUpperCase()).join("");
}
function avColor(str) {
  return AV_COLORS[(str || "").charCodeAt(0) % AV_COLORS.length];
}
function Avatar({ label }) {
  const init = initials(label);
  return (
    <span className="vb-av" style={{ background: avColor(label) }}>{init}</span>
  );
}

/* ---------- Vote button ---------- */

function VoteBox({ idea, authed, onRequireLogin, onChange, size = "md" }) {
  const [busy, setBusy] = React.useState(false);

  const toggle = async (e) => {
    e.stopPropagation();
    if (!authed) { onRequireLogin(); return; }
    if (busy) return;
    setBusy(true);
    const wantVote = !idea.voted;
    try {
      const data = await api(`/api/ideas/${idea.id}/vote`, { method: wantVote ? "POST" : "DELETE" });
      onChange({ voted: data.voted, vote_count: data.vote_count });
    } catch (err) {
      onChange(null, err.message);
    } finally {
      setBusy(false);
    }
  };

  return (
    <button
      className={`vb-votebox${size === "lg" ? " vb-votebox-lg" : ""}${idea.voted ? " voted" : ""}`}
      onClick={toggle}
      disabled={busy}
      aria-pressed={!!idea.voted}
      title={authed ? (idea.voted ? "Remove your vote" : "Upvote") : "Sign in to vote"}
    >
      <span className="vb-car">▲</span>
      <span>{(idea.vote_count || 0).toLocaleString()}</span>
    </button>
  );
}

/* ---------- Search icon ---------- */

function SearchIcon() {
  return (
    <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4">
      <circle cx="7" cy="7" r="4.5"/>
      <path d="M10.5 10.5l3 3" strokeLinecap="round"/>
    </svg>
  );
}

/* ---------- Login modal ---------- */

function LoginModal({ onClose, onAuthed }) {
  const [email, setEmail] = React.useState("");
  const [sent, setSent] = React.useState(false);
  const [busy, setBusy] = React.useState(false);
  const [err, setErr] = React.useState("");

  const submit = async (e) => {
    e.preventDefault();
    setErr(""); setBusy(true);
    try {
      await api("/api/auth/request-link", { method: "POST", body: JSON.stringify({ email }) });
      setSent(true);
    } catch (e2) {
      setErr(e2.message);
    } finally {
      setBusy(false);
    }
  };

  return (
    <div className="vb-overlay" onClick={onClose}>
      <div className="vb-modal" onClick={(e) => e.stopPropagation()}>
        {sent ? (
          <div className="vb-modal-sent">
            <span className="vb-modal-sent-icon">✉️</span>
            <h2>Check your email</h2>
            <p>We sent a sign-in link to <strong>{email}</strong>. It expires in 15 minutes and works once.</p>
            <div className="vb-modal-actions" style={{ justifyContent: "center", marginTop: 8 }}>
              <button className="vb-btn" onClick={onClose}>Done</button>
            </div>
          </div>
        ) : (
          <form onSubmit={submit}>
            <h2>Sign in to continue</h2>
            <p>No password — we'll email you a one-time link.</p>
            <div className="vb-field" style={{ marginTop: 4 }}>
              <label htmlFor="vb-email">Email address</label>
              <input
                id="vb-email" className="vb-input" type="email" required autoFocus
                placeholder="you@example.com" value={email}
                onChange={(e) => setEmail(e.target.value)}
              />
            </div>
            {err && <div className="vb-error">{err}</div>}
            <div className="vb-modal-actions">
              <button type="submit" className="vb-btn vb-btn-primary" disabled={busy}>
                {busy ? "Sending…" : "Email me a link"}
              </button>
              <button type="button" className="vb-btn" onClick={onClose}>Cancel</button>
            </div>
          </form>
        )}
      </div>
    </div>
  );
}

/* ---------- Submit idea modal ---------- */

function SubmitModal({ onClose, onCreated }) {
  const [title, setTitle] = React.useState("");
  const [description, setDescription] = React.useState("");
  const [busy, setBusy] = React.useState(false);
  const [err, setErr] = React.useState("");

  const submit = async (e) => {
    e.preventDefault();
    setErr(""); setBusy(true);
    try {
      const { id } = await api("/api/ideas", { method: "POST", body: JSON.stringify({ title, description }) });
      onCreated(id);
    } catch (e2) {
      setErr(e2.message);
    } finally {
      setBusy(false);
    }
  };

  return (
    <div className="vb-overlay" onClick={onClose}>
      <div className="vb-modal" onClick={(e) => e.stopPropagation()}>
        <form onSubmit={submit}>
          <h2>Suggest an idea</h2>
          <p>Keep it specific. Others can vote and comment.</p>
          <div className="vb-field" style={{ marginTop: 4 }}>
            <label htmlFor="vb-title">Title</label>
            <input id="vb-title" className="vb-input" required autoFocus maxLength={140}
              placeholder="e.g. Gapless playback" value={title} onChange={(e) => setTitle(e.target.value)} />
          </div>
          <div className="vb-field">
            <label htmlFor="vb-desc">
              Description <span className="vb-muted">(optional)</span>
            </label>
            <textarea id="vb-desc" className="vb-textarea" rows={4} maxLength={8000}
              placeholder="What problem does this solve?" value={description}
              onChange={(e) => setDescription(e.target.value)} />
          </div>
          {err && <div className="vb-error">{err}</div>}
          <div className="vb-modal-actions">
            <button type="submit" className="vb-btn vb-btn-primary" disabled={busy}>
              {busy ? "Posting…" : "Post idea"}
            </button>
            <button type="button" className="vb-btn" onClick={onClose}>Cancel</button>
          </div>
        </form>
      </div>
    </div>
  );
}

/* ---------- Board sidebar ---------- */

function BoardSidebar({ filter, onFilter, sort, onSort, ideas }) {
  const counts = React.useMemo(() => {
    if (!ideas) return {};
    const m = { all: ideas.length };
    ideas.forEach(i => { m[i.status] = (m[i.status] || 0) + 1; });
    return m;
  }, [ideas]);

  const statusGroups = [
    { id: "all",         label: "All ideas" },
    { id: "open",        label: "Open" },
    { id: "under_review",label: "Under Review" },
    { id: "planned",     label: "Planned" },
    { id: "in_progress", label: "In Progress" },
    { id: "complete",    label: "Complete" },
    { id: "closed",      label: "Closed" },
  ];

  const sortOpts = [
    { id: "top",      label: "Most voted" },
    { id: "newest",   label: "Newest" },
    { id: "activity", label: "Recent activity" },
  ];

  return (
    <aside className="vb-side">
      <div className="vb-side-group">
        <div className="vb-side-head">Status</div>
        {statusGroups.map(g => (
          <button
            key={g.id}
            className={`vb-side-item${filter === g.id ? " is-active" : ""}`}
            onClick={() => onFilter(g.id)}
          >
            {g.label}
            {counts[g.id] > 0 && <span className="vb-side-item-n">{counts[g.id]}</span>}
          </button>
        ))}
      </div>
      <div className="vb-side-group">
        <div className="vb-side-head">Sort</div>
        {sortOpts.map(s => (
          <button
            key={s.id}
            className={`vb-side-item${sort === s.id ? " is-active" : ""}`}
            onClick={() => onSort(s.id)}
          >
            {s.label}
          </button>
        ))}
      </div>
    </aside>
  );
}

/* ---------- Board page ---------- */

function BoardPage({ me, navigate, requireLogin, openSubmit }) {
  const [ideas, setIdeas] = React.useState(null);
  const [sort, setSort] = React.useState("top");
  const [filter, setFilter] = React.useState("all");
  const [q, setQ] = React.useState("");
  const [err, setErr] = React.useState("");

  const authed = !!(me && me.user);

  const load = React.useCallback(async () => {
    setErr("");
    try {
      const qs = new URLSearchParams({ sort });
      const { ideas } = await api(`/api/ideas?${qs}`);
      setIdeas(ideas);
    } catch (e) { setErr(e.message); setIdeas([]); }
  }, [sort]);

  React.useEffect(() => { load(); }, [load]);

  const onVoteChange = (id, patch, errMsg) => {
    if (errMsg) { requireLogin(errMsg); return; }
    setIdeas((list) => list.map((it) => (it.id === id ? { ...it, ...patch } : it)));
  };

  const filtered = React.useMemo(() => {
    if (!ideas) return [];
    return ideas
      .filter(i => filter === "all" || i.status === filter)
      .filter(i => {
        if (!q) return true;
        const s = q.toLowerCase();
        return i.title.toLowerCase().includes(s) || (i.description || "").toLowerCase().includes(s);
      });
  }, [ideas, filter, q]);

  return (
    <main className="vb-wrap">
      <div className="container">

        {/* Page header */}
        <div className="vb-page-hd">
          <div>
            <h1 className="vb-page-title">Feedback</h1>
            <p className="vb-page-sub">Vote on ideas, suggest your own, and follow what's planned and shipping.</p>
          </div>
          <div className="vb-page-hd-right">
            <div className="vb-search">
              <SearchIcon />
              <input
                type="text"
                placeholder="Search ideas"
                value={q}
                onChange={(e) => setQ(e.target.value)}
              />
            </div>
            <button className="vb-btn vb-btn-primary vb-btn-sm" onClick={() => authed ? openSubmit() : requireLogin()}>
              + New idea
            </button>
          </div>
        </div>

        {/* Sidebar + list */}
        <div className="vb-layout">
          <BoardSidebar
            filter={filter} onFilter={setFilter}
            sort={sort} onSort={setSort}
            ideas={ideas}
          />
          <div>
            {err && <div style={{ color: "var(--text-3)", marginBottom: 16, fontSize: 14 }}>{err}</div>}
            {ideas === null ? (
              <div className="vb-loading">Loading…</div>
            ) : filtered.length === 0 ? (
              <div className="vb-list">
                <div className="vb-empty">
                  <div className="vb-empty-icon">
                    <SearchIcon />
                  </div>
                  <h3 className="vb-empty-title">
                    {q ? `No ideas match "${q}"` : "No ideas yet"}
                  </h3>
                  <p className="vb-empty-sub">
                    {q ? "Try a different search — or post it as a new idea." : "Be the first to suggest one."}
                  </p>
                  <button className="vb-btn vb-btn-sm" style={{ marginTop: 12 }} onClick={() => authed ? openSubmit() : requireLogin()}>
                    + Submit an idea
                  </button>
                </div>
              </div>
            ) : (
              <div className="vb-list">
                {filtered.map((idea) => (
                  <div
                    className="vb-row"
                    key={idea.id}
                    onClick={(e) => { if (!e.target.closest('.vb-votebox')) navigate(`${BASE}/${idea.id}`); }}
                  >
                    <VoteBox
                      idea={idea}
                      authed={authed}
                      onRequireLogin={() => requireLogin()}
                      onChange={(patch, e) => onVoteChange(idea.id, patch, e)}
                    />
                    <div className="vb-row-body">
                      <h3 className="vb-row-title">{idea.title}</h3>
                      <div className="vb-row-meta">
                        {idea.comment_count > 0 && (
                          <>
                            <span className="vb-row-meta-item">
                              <svg width="12" height="12" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4"><path d="M14 9.5a2 2 0 01-2 2H5l-3 2.5V4a2 2 0 012-2h8a2 2 0 012 2v5.5z" strokeLinejoin="round"/></svg>
                              {idea.comment_count}
                            </span>
                            <span className="vb-row-meta-sep">·</span>
                          </>
                        )}
                        <span className="vb-row-meta-item">{timeAgo(idea.created_at)}</span>
                      </div>
                    </div>
                  </div>
                ))}
              </div>
            )}
          </div>
        </div>

      </div>
    </main>
  );
}

/* ---------- Detail page ---------- */

function DetailPage({ id, me, navigate, requireLogin, showToast }) {
  const [idea, setIdea] = React.useState(null);
  const [comments, setComments] = React.useState([]);
  const [err, setErr] = React.useState("");
  const [body, setBody] = React.useState("");
  const [posting, setPosting] = React.useState(false);

  const authed = !!(me && me.user);
  const isAdmin = !!(me && me.isAdmin);

  const load = React.useCallback(async () => {
    setErr("");
    try {
      const [{ idea }, { comments }] = await Promise.all([
        api(`/api/ideas/${id}`),
        api(`/api/ideas/${id}/comments`),
      ]);
      setIdea(idea);
      setComments(comments);
      document.title = `${idea.title} — Moonlight Feedback`;
    } catch (e) { setErr(e.message); }
  }, [id]);

  React.useEffect(() => { load(); }, [load]);

  const onVoteChange = (patch, errMsg) => {
    if (errMsg) { requireLogin(errMsg); return; }
    setIdea((cur) => ({ ...cur, ...patch }));
  };

  const postComment = async (e) => {
    e.preventDefault();
    if (!authed) { requireLogin(); return; }
    setPosting(true);
    try {
      const { comment } = await api(`/api/ideas/${id}/comments`, { method: "POST", body: JSON.stringify({ body }) });
      setComments((c) => [...c, comment]);
      setIdea((cur) => cur ? { ...cur, comment_count: (cur.comment_count || 0) + 1 } : cur);
      setBody("");
    } catch (e2) { showToast(e2.message, true); }
    finally { setPosting(false); }
  };

  const toggleFollow = async () => {
    if (!authed) { requireLogin(); return; }
    try {
      const method = idea.following ? "DELETE" : "POST";
      const data = await api(`/api/ideas/${id}/follow`, { method });
      setIdea((cur) => ({ ...cur, following: data.following }));
      showToast(data.following ? "Following this idea." : "Unfollowed.");
    } catch (e) { showToast(e.message, true); }
  };

  const changeStatus = async (status) => {
    try {
      await api(`/api/admin/ideas/${id}/status`, { method: "PATCH", body: JSON.stringify({ status }) });
      setIdea((cur) => ({ ...cur, status }));
      showToast("Status updated");
    } catch (e) { showToast(e.message, true); }
  };

  const deleteComment = async (cid) => {
    try {
      await api(`/api/admin/comments/${cid}`, { method: "DELETE" });
      setComments((c) => c.filter((x) => x.id !== cid));
      setIdea((cur) => cur ? { ...cur, comment_count: Math.max(0, (cur.comment_count || 1) - 1) } : cur);
    } catch (e) { showToast(e.message, true); }
  };

  return (
    <main className="vb-wrap">
      <div className="container">

        <button className="vb-back" onClick={() => navigate(BASE)}>
          <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4">
            <path d="M13 8H3M7 4L3 8l4 4" strokeLinecap="round" strokeLinejoin="round"/>
          </svg>
          All ideas
        </button>

        {err && <div style={{ color: "var(--text-3)", marginBottom: 16, fontSize: 14 }}>{err}</div>}

        {!idea ? (
          <div className="vb-loading">Loading…</div>
        ) : (
          <div style={{ maxWidth: 720, margin: "0 auto" }}>

            <div className="vb-detail">
              <VoteBox
                idea={idea}
                authed={authed}
                size="lg"
                onRequireLogin={() => requireLogin()}
                onChange={onVoteChange}
              />
              <div className="vb-detail-body">
                <h1>{idea.title}</h1>
                <div className="vb-detail-meta">
                  <StatusBadge status={idea.status} />
                  {idea.author_label && (
                    <span>by {idea.author_label}</span>
                  )}
                  <span>{timeAgo(idea.created_at)}</span>
                  <button
                    className={`vb-follow-btn${idea.following ? " is-following" : ""}`}
                    onClick={toggleFollow}
                  >
                    {idea.following ? "✓ Following" : "Follow"}
                  </button>
                </div>

                {isAdmin && (
                  <div className="vb-admin-bar">
                    <label htmlFor="vb-admin-status">Admin · set status</label>
                    <select id="vb-admin-status" className="vb-select" value={idea.status}
                      onChange={(e) => changeStatus(e.target.value)}>
                      {STATUS_ORDER.map((s) => (
                        <option key={s} value={s}>{STATUS_META[s].label}</option>
                      ))}
                    </select>
                  </div>
                )}

                {idea.description && (
                  <p className="vb-detail-desc">{idea.description}</p>
                )}
              </div>
            </div>

            {/* Comments */}
            <div style={{ borderTop: "1px solid rgba(255,255,255,0.07)", paddingTop: 24, marginTop: 8 }}>
              <p className="vb-section-title">
                Comments · {idea.comment_count || 0}
              </p>

              <div className="vb-comments">
                {comments.length === 0 && (
                  <p style={{ color: "var(--text-4)", fontSize: 14, padding: "8px 0" }}>
                    No comments yet — be the first.
                  </p>
                )}
                {comments.map((c) => (
                  <div className="vb-comment" key={c.id}>
                    <Avatar label={c.author_label || "user"} />
                    <div className="vb-comment-body">
                      <div className="vb-comment-head">
                        <span className="vb-comment-author">{c.author_label || "user"}</span>
                        <span className="vb-comment-time">· {timeAgo(c.created_at)}</span>
                        {isAdmin && (
                          <button className="vb-comment-del" onClick={() => deleteComment(c.id)}>Delete</button>
                        )}
                      </div>
                      <p className="vb-comment-text">{c.body}</p>
                    </div>
                  </div>
                ))}
              </div>

              <div className="vb-composer">
                <textarea
                  placeholder={authed ? "Add a comment…" : "Sign in to comment"}
                  value={body}
                  onChange={(e) => setBody(e.target.value)}
                  onFocus={() => { if (!authed) requireLogin(); }}
                />
                <button
                  className="vb-btn vb-btn-primary vb-btn-sm"
                  onClick={postComment}
                  disabled={posting || !body.trim()}
                  style={{ opacity: !body.trim() ? 0.5 : 1, flexShrink: 0 }}
                >
                  {posting ? "Posting…" : "Comment"}
                </button>
              </div>
            </div>

          </div>
        )}

      </div>
    </main>
  );
}

/* ---------- Root app ---------- */

function VoteboxApp({ showHeader = true }) {
  const [route, setRoute] = React.useState(() => parseRoute());
  const [me, setMe] = React.useState(undefined);
  const [showLogin, setShowLogin] = React.useState(false);
  const [showSubmit, setShowSubmit] = React.useState(false);
  const [toast, setToast] = React.useState(null);

  function parseRoute() {
    const path = window.location.pathname.replace(/\/+$/, "");
    if (path === BASE || path === "") return { name: "board" };
    if (path.startsWith(BASE + "/")) return { name: "detail", id: path.slice(BASE.length + 1) };
    return { name: "board" };
  }

  const navigate = React.useCallback((to) => {
    window.history.pushState({}, "", to);
    const next = parseRoute();
    if (next.name === "board") document.title = "Moonlight — Feedback & feature requests";
    setRoute(next);
    window.scrollTo(0, 0);
  }, []);

  const showToast = React.useCallback((msg, err) => {
    setToast({ msg, err });
    setTimeout(() => setToast(null), 3200);
  }, []);

  const refreshMe = React.useCallback(async () => {
    try { setMe(await api("/api/me")); } catch { setMe({ user: null, isAdmin: false }); }
  }, []);

  React.useEffect(() => {
    refreshMe();
    const onPop = () => setRoute(parseRoute());
    window.addEventListener("popstate", onPop);

    const params = new URLSearchParams(window.location.search);
    const auth = params.get("auth");
    if (auth) {
      showToast(auth === "ok" ? "Signed in." : "That sign-in link was invalid or expired.", auth !== "ok");
      params.delete("auth");
      const q = params.toString();
      window.history.replaceState({}, "", window.location.pathname + (q ? "?" + q : ""));
    }
    return () => window.removeEventListener("popstate", onPop);
  }, [refreshMe, showToast]);

  const requireLogin = (msg) => {
    if (msg) showToast(msg, true);
    setShowLogin(true);
  };

  const logout = async () => {
    try { await api("/api/auth/logout", { method: "POST" }); } catch {}
    refreshMe();
    showToast("Signed out.");
  };

  return (
    <div>
      {route.name === "detail"
        ? <DetailPage id={route.id} me={me} navigate={navigate} requireLogin={requireLogin} showToast={showToast} />
        : <BoardPage me={me} navigate={navigate} requireLogin={requireLogin} openSubmit={() => setShowSubmit(true)} />
      }

      {showLogin && <LoginModal onClose={() => setShowLogin(false)} onAuthed={refreshMe} />}
      {showSubmit && (
        <SubmitModal
          onClose={() => setShowSubmit(false)}
          onCreated={(id) => { setShowSubmit(false); showToast("Idea posted."); navigate(`${BASE}/${id}`); }}
        />
      )}
      {toast && <div className={`vb-toast${toast.err ? " err" : ""}`}>{toast.msg}</div>}
    </div>
  );
}
