// Session Notes — persisted to Cloudflare D1. File attachments stored in R2.
const {
  useState: useStateS,
  useEffect: useEffectS,
  useMemo: useMemoS,
  useRef: useRefS,
} = React;

function tokS() { return sessionStorage.getItem("kol_tok") || ""; }
function jsonHeaders() {
  return { "Authorization": "Bearer " + tokS(), "Content-Type": "application/json" };
}

function todayISO() { return new Date().toISOString().slice(0, 10); }

function nextN(notes) {
  return (notes.reduce((m, n) => Math.max(m, n.n || 0), 0) || 0) + 1;
}

function fmtBytes(b) {
  if (!b) return "";
  if (b < 1024) return b + " B";
  if (b < 1024 * 1024) return (b / 1024).toFixed(1) + " KB";
  return (b / (1024 * 1024)).toFixed(1) + " MB";
}

// ── File attachment row ───────────────────────────────────────────────────────

function FileRow({ file, onDelete }) {
  const [removing, setRemoving] = useStateS(false);

  async function remove() {
    setRemoving(true);
    try {
      await fetch("/api/files/" + file.id, {
        method: "DELETE",
        headers: jsonHeaders(),
      });
      onDelete(file.id);
    } catch (e) {
      setRemoving(false);
    }
  }

  return (
    <div style={{ display: "flex", alignItems: "center", gap: 8, padding: "5px 10px", border: "1px solid var(--rule)", borderRadius: "var(--radius)", fontSize: 13 }}>
      <svg width="13" height="13" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" style={{ flexShrink: 0, color: "var(--ink-3)" }}>
        <path d="M9 2H4a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V6L9 2z" />
        <polyline points="9 2 9 6 13 6" />
      </svg>
      <a href={"/api/files/" + file.id} target="_blank" rel="noopener" style={{ color: "var(--ink-2)", textDecoration: "none" }}>
        {file.filename}
      </a>
      {file.size_bytes ? <span style={{ color: "var(--ink-3)", fontSize: 11 }}>{fmtBytes(file.size_bytes)}</span> : null}
      <button
        onClick={remove}
        disabled={removing}
        title="Remove file"
        style={{ marginLeft: "auto", color: "var(--ink-3)", fontSize: 16, lineHeight: 1, padding: "0 2px", opacity: removing ? 0.4 : 1 }}
      >×</button>
    </div>
  );
}

// ── Main Sessions component ───────────────────────────────────────────────────

function ChevronIcon({ open }) {
  return (
    <svg
      width="14" height="14" viewBox="0 0 16 16"
      fill="none" stroke="currentColor" strokeWidth="1.8"
      style={{ flexShrink: 0, color: "var(--ink-4)", transition: "transform 200ms", transform: open ? "rotate(180deg)" : "rotate(0deg)", marginLeft: "auto" }}
    >
      <polyline points="4 6 8 10 12 6" />
    </svg>
  );
}

function Sessions() {
  const [notes, setNotes] = useStateS([]);
  const [loading, setLoading] = useStateS(true);
  const [editing, setEditing] = useStateS(null); // null | "new" | n (number)
  const [draft, setDraft] = useStateS({ n: 0, title: "", date: todayISO(), tags: "", body: "" });
  const [uploadingFor, setUploadingFor] = useStateS(null); // session n currently uploading
  const [openSessions, setOpenSessions] = useStateS(new Set());
  const fileInputRef = useRefS(null);

  function toggleSession(n) {
    setOpenSessions(prev => {
      const next = new Set(prev);
      if (next.has(n)) next.delete(n); else next.add(n);
      return next;
    });
  }

  useEffectS(() => { loadFromApi(); }, []);

  // Keep global sessions in sync so the home page and search always reflect live data.
  useEffectS(() => {
    window.KOL_SESSIONS = notes;
    window.KOL_SESSION_COUNT = notes.length;
    window.dispatchEvent(new CustomEvent("kol:sessions-count", { detail: notes.length }));
    window.dispatchEvent(new CustomEvent("kol:sessions", { detail: notes }));
  }, [notes]);

  async function loadFromApi() {
    setLoading(true);
    try {
      const resp = await fetch("/api/sessions");
      if (!resp.ok) throw new Error(resp.status);
      const { sessions, seeded } = await resp.json();

      if (!seeded) {
        // Seed default sessions from data.js into D1
        const defaults = window.SESSION_NOTES_DEFAULT || [];
        for (const note of defaults) {
          await fetch("/api/sessions", {
            method: "POST",
            headers: jsonHeaders(),
            body: JSON.stringify({ session: note }),
          });
        }
        // Re-fetch after seeding
        const r2 = await fetch("/api/sessions");
        if (r2.ok) {
          const data = await r2.json();
          setNotes(data.sessions || []);
        }
      } else {
        setNotes(sessions);
      }
    } catch (e) {
      // Fall back to hardcoded defaults if API is unreachable
      setNotes((window.SESSION_NOTES_DEFAULT || []).map(n => ({ ...n, files: [] })));
    }
    setLoading(false);
  }

  const sorted = useMemoS(
    () => [...notes].sort((a, b) => (b.date || "").localeCompare(a.date || "")),
    [notes]
  );

  // ── Editor helpers ──────────────────────────────────────────────────────────

  function startNew() {
    setDraft({ n: nextN(notes), title: "", date: todayISO(), tags: "", body: "" });
    setEditing("new");
  }

  function startEdit(note) {
    setDraft({ ...note, tags: (note.tags || []).join(", ") });
    setEditing(note.n);
  }

  function cancel() { setEditing(null); }

  async function save() {
    if (!draft.title.trim() || !draft.body.trim()) return;
    const cleaned = {
      n: Number(draft.n) || nextN(notes),
      title: draft.title.trim(),
      date: draft.date || todayISO(),
      tags: draft.tags.split(",").map(s => s.trim()).filter(Boolean),
      body: draft.body,
    };
    // Optimistic update
    if (editing === "new") {
      setNotes(prev => [{ ...cleaned, files: [] }, ...prev]);
    } else {
      setNotes(prev => prev.map(n => n.n === editing ? { ...n, ...cleaned } : n));
    }
    setEditing(null);
    // Persist
    fetch("/api/sessions", {
      method: "POST",
      headers: jsonHeaders(),
      body: JSON.stringify({ session: cleaned }),
    }).catch(console.error);
  }

  async function remove(n) {
    const note = notes.find(x => x.n === n);
    const ok = await window.requestDelete(
      `session ${String(n).padStart(2, "0")}${note ? ` “${note.title}”` : ""}`
    );
    if (!ok) return;
    setNotes(prev => prev.filter(x => x.n !== n));
    fetch("/api/sessions/" + n, {
      method: "DELETE",
      headers: jsonHeaders(),
    }).catch(console.error);
  }

  // ── File upload ─────────────────────────────────────────────────────────────

  function triggerUpload(sessionN) {
    setUploadingFor(sessionN);
    fileInputRef.current?.click();
  }

  async function handleFileChange(e) {
    const file = e.target.files[0];
    if (!file || uploadingFor === null) return;
    const sessionN = uploadingFor;

    // Show uploading state
    setNotes(prev => prev.map(n =>
      n.n === sessionN ? { ...n, _uploading: true } : n
    ));

    try {
      const fd = new FormData();
      fd.append("file", file);
      fd.append("session_n", String(sessionN));
      const resp = await fetch("/api/upload", {
        method: "POST",
        headers: { "Authorization": "Bearer " + tokS() },
        body: fd,
      });
      if (!resp.ok) throw new Error(await resp.text());
      const { id, filename, size_bytes } = await resp.json();
      setNotes(prev => prev.map(n =>
        n.n === sessionN
          ? { ...n, _uploading: false, files: [...(n.files || []), { id, filename, size_bytes }] }
          : n
      ));
    } catch (err) {
      console.error("Upload failed:", err);
      setNotes(prev => prev.map(n =>
        n.n === sessionN ? { ...n, _uploading: false, _uploadErr: err.message } : n
      ));
    }

    e.target.value = "";
    setUploadingFor(null);
  }

  function removeFile(sessionN, fileId) {
    setNotes(prev => prev.map(n =>
      n.n === sessionN ? { ...n, files: (n.files || []).filter(f => f.id !== fileId) } : n
    ));
  }

  // ── Render ──────────────────────────────────────────────────────────────────

  return (
    <>
      <input
        ref={fileInputRef}
        type="file"
        style={{ display: "none" }}
        accept=".pdf,.pptx,.docx,.xlsx,.zip,.png,.jpg,.jpeg"
        onChange={handleFileChange}
      />

      <div className="crumbs">
        <a href="#/">Home</a><span>/</span>
        <span style={{ color: "var(--ink)" }}>Session Notes</span>
      </div>

      <header className="tools-head">
        <h1>Session Notes</h1>
        <p>Write-ups from each meeting, newest first. Attach slide PDFs or other files to any session for members to download.</p>
      </header>

      <div className="session-actions" style={{ marginBottom: 22 }}>
        {editing === null && (
          <button className="btn" onClick={startNew}>+ New session note</button>
        )}
      </div>

      {/* ── Editor ── */}
      {editing !== null && (
        <div className="session-editor" style={{ marginBottom: 28 }}>
          <h3>{editing === "new" ? "New session" : `Editing session ${editing}`}</h3>
          <div style={{ display: "grid", gridTemplateColumns: "80px 140px 1fr", gap: 10 }}>
            <div className="field">
              <label>Number</label>
              <input type="number" value={draft.n} onChange={e => setDraft({ ...draft, n: e.target.value })} />
            </div>
            <div className="field">
              <label>Date</label>
              <input type="date" value={draft.date} onChange={e => setDraft({ ...draft, date: e.target.value })} />
            </div>
            <div className="field">
              <label>Tags (comma-separated)</label>
              <input value={draft.tags} onChange={e => setDraft({ ...draft, tags: e.target.value })} placeholder="e.g. vm, opsec, geolocation" />
            </div>
          </div>
          <div className="field">
            <label>Title</label>
            <input value={draft.title} onChange={e => setDraft({ ...draft, title: e.target.value })} placeholder="Tuesday: …" />
          </div>
          <div className="field">
            <label>Body</label>
            <textarea value={draft.body} onChange={e => setDraft({ ...draft, body: e.target.value })} placeholder="What did the group cover? What's the homework?" />
          </div>
          <div className="row">
            <button className="btn" onClick={save}>Save</button>
            <button className="btn ghost" onClick={cancel}>Cancel</button>
          </div>
        </div>
      )}

      {/* ── List ── */}
      {loading ? (
        <div style={{ color: "var(--ink-3)", padding: "32px 0", fontSize: 14 }}>Loading sessions…</div>
      ) : sorted.length === 0 ? (
        <div className="empty">No session notes yet</div>
      ) : sorted.map(note => {
        const isOpen = openSessions.has(note.n);
        return (
          <div key={note.n} className="session">
            <div
              className="session-head session-head-toggle"
              onClick={() => toggleSession(note.n)}
            >
              <span className="session-num">SESSION {String(note.n).padStart(2, "0")}</span>
              <h3>{note.title}</h3>
              <span className="session-date">{fmtDate(note.date)}</span>
              <ChevronIcon open={isOpen} />
            </div>

            <div className={`session-collapse${isOpen ? " open" : ""}`}>
              <div className="session-collapse-inner">
                <div
                  className="session-body"
                  dangerouslySetInnerHTML={{ __html: marked.parse(note.body || "") }}
                />

                {note.tags?.length ? (
                  <div className="session-tags">
                    {note.tags.map(t => <span key={t} className="tool-tag">{t}</span>)}
                  </div>
                ) : null}

                {/* Files */}
                {(note.files?.length > 0 || note._uploading || note._uploadErr) && (
                  <div style={{ marginTop: 12, display: "flex", flexDirection: "column", gap: 6 }}>
                    {note.files?.map(f => (
                      <FileRow key={f.id} file={f} onDelete={id => removeFile(note.n, id)} />
                    ))}
                    {note._uploading && (
                      <div style={{ fontSize: 13, color: "var(--ink-3)", padding: "5px 0" }}>Uploading…</div>
                    )}
                    {note._uploadErr && (
                      <div style={{ fontSize: 13, color: "var(--danger)" }}>Upload failed: {note._uploadErr}</div>
                    )}
                  </div>
                )}

                {/* Actions */}
                <div style={{ display: "flex", gap: 8, marginTop: 12, alignItems: "center" }}>
                  <button className="btn ghost" style={{ fontSize: 12, padding: "5px 10px" }} onClick={() => startEdit(note)}>Edit</button>
                  <button className="btn ghost" style={{ fontSize: 12, padding: "5px 10px" }} onClick={() => remove(note.n)}>Delete</button>
                  <button
                    className="btn ghost"
                    style={{ fontSize: 12, padding: "5px 10px" }}
                    onClick={() => triggerUpload(note.n)}
                    disabled={!!note._uploading}
                  >
                    {note._uploading ? "Uploading…" : "Attach file"}
                  </button>
                </div>
              </div>
            </div>
          </div>
        );
      })}
    </>
  );
}

window.KOL_PAGES.Sessions = Sessions;
