// RF Issue Logger — main app
const { useState, useMemo, useEffect, useRef, useCallback } = React;

const ISSUE_TYPES = window.RF_ISSUE_TYPES;
const SEVERITY = window.RF_SEVERITY;

// ─── API ─────────────────────────────────────────────────────────────────
const API_BASE = "https://rf-issues-api.dpjdewi.workers.dev";

const api = {
  async listIssues() {
    const r = await fetch(`${API_BASE}/issues`);
    if (!r.ok) throw new Error(`list failed (${r.status})`);
    return r.json();
  },
  async createIssue(issue) {
    const r = await fetch(`${API_BASE}/issues`, {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify(issue),
    });
    if (!r.ok) throw new Error(`create failed (${r.status})`);
    return r.json();
  },
  async updateIssue(id, patch) {
    const r = await fetch(`${API_BASE}/issues/${id}`, {
      method: "PATCH",
      headers: { "content-type": "application/json" },
      body: JSON.stringify(patch),
    });
    if (!r.ok) throw new Error(`update failed (${r.status})`);
    return r.json();
  },
  async listZoneNames() {
    const r = await fetch(`${API_BASE}/zone-names`);
    if (!r.ok) throw new Error(`zones failed (${r.status})`);
    return r.json();
  },
  async setZoneName(id, name) {
    const r = await fetch(`${API_BASE}/zone-names/${id}`, {
      method: "PUT",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({ name }),
    });
    if (!r.ok) throw new Error(`rename failed (${r.status})`);
    return r.json();
  },
};

// ─── zones context ──────────────────────────────────────────────────────
const ZonesCtx = React.createContext(null);
const useZones = () => React.useContext(ZonesCtx);

// ─── utils ──────────────────────────────────────────────────────────────
const fmtRelative = (iso) => {
  const NOW = new Date("2026-05-15T14:32:00Z");
  const d = new Date(iso);
  const mins = Math.round((NOW - d) / 60000);
  if (mins < 1) return "just now";
  if (mins < 60) return `${mins}m ago`;
  const hrs = Math.floor(mins / 60);
  if (hrs < 24) return `${hrs}h ${mins % 60}m ago`;
  return `${Math.floor(hrs/24)}d ago`;
};
const fmtTime = (iso) => {
  const d = new Date(iso);
  return d.toLocaleTimeString("en-GB", { hour:"2-digit", minute:"2-digit" });
};
const sevMeta = (id) => SEVERITY.find(s => s.id === id);
const typeMeta = (id) => ISSUE_TYPES.find(t => t.id === id);

// ─── top bar ────────────────────────────────────────────────────────────
function TopBar({ view, setView, openCount, role, status, onRetry, name, onChangeName }) {
  const [tick, setTick] = useState(0);
  const [shareOpen, setShareOpen] = useState(false);
  useEffect(() => { const i = setInterval(() => setTick(t=>t+1), 1000); return () => clearInterval(i); }, []);
  const now = new Date();
  return (
    <header className="topbar">
      <div className="topbar-left">
        <div className="brand">
          <span className={`brand-dot status-${status||"online"}`} />
          <span className="brand-mark">RF·OPS</span>
          <span className="brand-divider">/</span>
          <span className="brand-sub">ISSUE LOGGER</span>
        </div>
        <div className="venue">
          <span className="venue-label">VENUE</span>
          <span className="venue-name">CCD · Dublin</span>
          <span className="venue-dot">·</span>
          <span className="venue-name">Google GML</span>
        </div>
        {status === "offline" && (
          <button className="offline-pill" onClick={onRetry} title="Retry connection">
            <span className="offline-dot" /> OFFLINE · RETRY
          </button>
        )}
      </div>
      <div className="topbar-right">
        {name && (
          <button className="user-chip" onClick={onChangeName} title="Change name">
            <span className="user-icon">●</span>
            <span className="user-name">{name}</span>
          </button>
        )}
        <div className="clock">
          <span className="clock-time">{now.toLocaleTimeString("en-GB",{hour:"2-digit",minute:"2-digit",second:"2-digit"})}</span>
          <span className="clock-tz">UTC</span>
        </div>
        {role === "tech" ? (
          <div className="role-badge role-tech">
            <span className="rb-dot" />
            <span>TECH · LOG ONLY</span>
          </div>
        ) : (
          <>
            <button className="share-btn" onClick={() => setShareOpen(true)} title="Share a logger-only link with techs">
              <span className="share-icon">⇪</span> SHARE TECH LINK
            </button>
            <div className="view-toggle" role="tablist">
              <button role="tab" className={view==="log"?"active":""} onClick={()=>setView("log")}>
                <span className="vt-dot" /> LOG
              </button>
              <button role="tab" className={view==="review"?"active":""} onClick={()=>setView("review")}>
                REVIEW
                {openCount > 0 && <span className="vt-badge">{openCount}</span>}
              </button>
            </div>
          </>
        )}
      </div>
      {shareOpen && <ShareModal onClose={() => setShareOpen(false)} />}
    </header>
  );
}

function ShareModal({ onClose }) {
  // build tech URL — preserve current pathname/origin, just add role=tech
  const techUrl = useMemo(() => {
    const u = new URL(window.location.href);
    u.searchParams.set("role", "tech");
    u.hash = "";
    return u.toString();
  }, []);
  const [copied, setCopied] = useState(false);

  // generate QR client-side (no external service)
  const qrSvg = useMemo(() => {
    if (!window.qrcode) return null;
    try {
      const qr = window.qrcode(0, "M");
      qr.addData(techUrl);
      qr.make();
      const count = qr.getModuleCount();
      const size = 240;
      const pad = 16;
      const cell = (size - pad * 2) / count;
      let rects = "";
      for (let r = 0; r < count; r++) {
        for (let c = 0; c < count; c++) {
          if (qr.isDark(r, c)) {
            rects += `<rect x="${pad + c*cell}" y="${pad + r*cell}" width="${cell+0.5}" height="${cell+0.5}" />`;
          }
        }
      }
      return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${size} ${size}" width="${size}" height="${size}"><rect width="${size}" height="${size}" fill="#0f1418"/><g fill="#cfe9ef">${rects}</g></svg>`;
    } catch { return null; }
  }, [techUrl]);

  const copy = async () => {
    try {
      await navigator.clipboard.writeText(techUrl);
      setCopied(true);
      setTimeout(() => setCopied(false), 1800);
    } catch {
      // fallback
      const el = document.createElement("textarea");
      el.value = techUrl;
      document.body.appendChild(el);
      el.select();
      document.execCommand("copy");
      el.remove();
      setCopied(true);
      setTimeout(() => setCopied(false), 1800);
    }
  };

  return (
    <div className="modal-scrim" onClick={onClose}>
      <div className="modal" onClick={e=>e.stopPropagation()}>
        <div className="modal-head">
          <div>
            <div className="modal-kicker">SHARE</div>
            <h3>Tech logger link</h3>
          </div>
          <button className="modal-close" onClick={onClose}>✕</button>
        </div>
        <p className="modal-desc">Techs opening this link can only log issues — the review portal is hidden. Share by URL, or have them scan the QR with their phone.</p>
        <div className="share-grid">
          <div className="qr-frame">
            {qrSvg
              ? <div className="qr-img" dangerouslySetInnerHTML={{ __html: qrSvg }} />
              : <div className="qr-fallback mono">QR unavailable</div>
            }
            <div className="qr-caption mono">SCAN TO OPEN</div>
          </div>
          <div className="share-actions">
            <div className="share-url-wrap">
              <div className="share-url-label mono">URL</div>
              <div className="share-url mono">{techUrl}</div>
            </div>
            <button className={`btn-primary ${copied?"is-copied":""}`} onClick={copy}>
              <span className="btn-glyph">{copied ? "✓" : "⧉"}</span>
              {copied ? "Copied" : "Copy link"}
            </button>
            <div className="share-tip">
              <span className="mono">TIP</span> Bookmark on each tech's phone home-screen — opens like an app.
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

// ─── log issue view ─────────────────────────────────────────────────────
function LogView({ onSubmit, role, name }) {
  const { zones, renameZone } = useZones();
  const [zone, setZone] = useState(null);
  const [mhz, setMhz] = useState(null);
  const [types, setTypes] = useState([]);
  const [severity, setSeverity] = useState(null);
  const [equipment, setEquipment] = useState("");
  const [otherText, setOtherText] = useState("");
  const [notes, setNotes] = useState("");
  const [submitted, setSubmitted] = useState(null);
  const [editZones, setEditZones] = useState(false);

  const reporter = name;

  const zoneObj = useMemo(() => zones.find(z => z.id === zone), [zones, zone]);
  const canSubmit = zone && mhz && types.length > 0 && severity && notes.trim().length > 3;

  const toggleType = (id) => setTypes(prev => prev.includes(id) ? prev.filter(x=>x!==id) : [...prev,id]);

  const reset = () => {
    setZone(null); setMhz(null); setTypes([]); setSeverity(null);
    setEquipment(""); setNotes(""); setOtherText("");
  };

  const submit = () => {
    if (!canSubmit) return;
    const issue = {
      id: `RF-${String(Math.floor(Math.random()*900)+143).padStart(4,"0")}`,
      zone, mhz, types, severity, equipment, reporter,
      notes: types.includes("other") && otherText ? `[${otherText}] ${notes}` : notes,
      status: "open",
      created: new Date().toISOString(),
      history: [{ t: new Date().toISOString(), who: reporter, what: "Logged" }],
    };
    onSubmit(issue);
    setSubmitted(issue.id);
    setTimeout(() => { setSubmitted(null); reset(); }, 2400);
  };

  return (
    <div className="log-view">
      <div className="log-panel">
        <div className="panel-head">
          <div className="panel-head-l">
            <span className="kicker">NEW REPORT</span>
            <h1>Log an RF issue</h1>
          </div>
          <div className="panel-head-r">
            <div className="reporter-display">
              <span className="rd-label">REPORTER</span>
              <span className="rd-name mono">{reporter}</span>
            </div>
          </div>
        </div>

        {/* zone */}
        <Field n="01" label="Zone" hint={zone ? zoneObj.short : "select where the issue is happening"}
          extra={role !== "tech" ? (
            <button className={`edit-toggle ${editZones?"on":""}`} onClick={()=>setEditZones(!editZones)}>
              {editZones ? "✓ DONE" : "✎ EDIT NAMES"}
            </button>
          ) : null}>
          {editZones ? (
            <div className="zone-edit">
              <div className="zone-edit-hint mono">Rename any zone — these labels persist on this device and update everywhere.</div>
              <div className="zone-edit-list">
                {zones.map(z => (
                  <div key={z.id} className="zone-edit-row">
                    <span className="zone-edit-code mono">{z.short}</span>
                    <input
                      className="zone-edit-input"
                      value={z.name}
                      onChange={e => renameZone(z.id, e.target.value)}
                      placeholder="zone name"
                    />
                    <span className="zone-edit-count mono">{z.freqs.length} freq{z.freqs.length===1?"":"s"}</span>
                  </div>
                ))}
              </div>
            </div>
          ) : (
            <div className="zone-grid">
              {zones.map(z => (
                <button key={z.id} className={`zone-chip ${zone===z.id?"on":""}`} onClick={()=>{setZone(z.id); setMhz(null);}}>
                  <span className="zone-chip-name">{z.name}</span>
                  <span className="zone-chip-count">{z.freqs.length} freq{z.freqs.length===1?"":"s"}</span>
                </button>
              ))}
            </div>
          )}
        </Field>

        {/* freq */}
        <Field n="02" label="Frequency" hint={mhz ? `${mhz} MHz` : (zone?"pick from the zone's coordinated list":"pick a zone first")} disabled={!zone}>
          {!zone ? (
            <div className="empty-row">— select a zone above —</div>
          ) : (
            <div className="freq-grid">
              {zoneObj.freqs.map(f => (
                <button key={f.mhz} className={`freq-chip ${mhz===f.mhz?"on":""} ${f.role==="backup"?"backup":""}`} onClick={()=>setMhz(f.mhz)}>
                  <span className="freq-mhz">{f.mhz}</span>
                  <span className="freq-meta">{f.type} · {f.band}{f.role==="backup"?" · BKP":""}</span>
                </button>
              ))}
            </div>
          )}
        </Field>

        {/* types */}
        <Field n="03" label="Issue type" hint={types.length ? `${types.length} selected` : "pick all that apply"}>
          <div className="type-grid">
            {ISSUE_TYPES.map(t => (
              <button key={t.id} className={`type-chip ${types.includes(t.id)?"on":""}`} onClick={()=>toggleType(t.id)}>
                <span className="type-code">{t.code}</span>
                <span className="type-label">{t.label}</span>
                <span className="type-check">{types.includes(t.id)?"✓":""}</span>
              </button>
            ))}
          </div>
          {types.includes("other") && (
            <input className="other-input" placeholder="describe the 'other' issue type…" value={otherText} onChange={e=>setOtherText(e.target.value)} />
          )}
        </Field>

        {/* severity */}
        <Field n="04" label="Severity" hint={severity ? sevMeta(severity).label.toUpperCase() : "how bad is it"}>
          <div className="sev-row">
            {SEVERITY.map(s => (
              <button key={s.id} className={`sev-btn sev-${s.id} ${severity===s.id?"on":""}`} onClick={()=>setSeverity(s.id)}>
                <span className="sev-code">{s.code}</span>
                <span className="sev-label">{s.label}</span>
              </button>
            ))}
          </div>
        </Field>

        {/* equipment */}
        <Field n="05" label="Equipment / channel" hint="e.g. Lav 4, HH 2, IEM Pack — MD">
          <input className="text-input mono" placeholder="Lav 4" value={equipment} onChange={e=>setEquipment(e.target.value)} />
        </Field>

        {/* notes */}
        <Field n="06" label="Notes" hint="what's happening, what you've tried, when it started" required>
          <textarea className="text-input" rows={5} placeholder="Describe the symptom, when it started, anything you've tried…" value={notes} onChange={e=>setNotes(e.target.value)} />
          <div className="char-count">{notes.length} chars</div>
        </Field>

        <div className="submit-row">
          <button className="btn-secondary" onClick={reset}>Clear</button>
          <button className={`btn-primary ${canSubmit?"":"disabled"}`} disabled={!canSubmit} onClick={submit}>
            <span className="btn-glyph">▸</span> Submit issue
          </button>
        </div>

        {submitted && (
          <div className="toast">
            <span className="toast-dot" />
            <span>Logged as <b className="mono">{submitted}</b> — routed to review queue.</span>
          </div>
        )}
      </div>

      <aside className="log-sidecar">
        <div className="sc-card">
          <div className="sc-head">CURRENT SELECTION</div>
          <dl className="sc-list">
            <div><dt>Zone</dt><dd className="mono">{zoneObj?.short || "—"}</dd></div>
            <div><dt>Freq</dt><dd className="mono">{mhz ? `${mhz} MHz` : "—"}</dd></div>
            <div><dt>Type</dt><dd>{types.length ? types.map(t=>typeMeta(t).code).join(" · ") : "—"}</dd></div>
            <div><dt>Severity</dt><dd>{severity ? <span className={`pill sev-${severity}`}>{sevMeta(severity).code}</span> : "—"}</dd></div>
            <div><dt>Equip</dt><dd className="mono">{equipment || "—"}</dd></div>
          </dl>
        </div>
      </aside>
    </div>
  );
}

function Field({ n, label, hint, children, disabled, required, extra }) {
  return (
    <div className={`field ${disabled?"disabled":""}`}>
      <div className="field-head">
        <span className="field-n">{n}</span>
        <span className="field-label">{label}{required && <span className="req">*</span>}</span>
        <span className="field-hint">{hint}</span>
        {extra && <span className="field-extra">{extra}</span>}
      </div>
      <div className="field-body">{children}</div>
    </div>
  );
}

// ─── review view ────────────────────────────────────────────────────────
function ReviewView({ issues, patchIssue }) {
  const { zones } = useZones();
  const zoneMeta = (id) => zones.find(z => z.id === id);
  const [filter, setFilter] = useState("open");
  const [sevFilter, setSevFilter] = useState("all");
  const [zoneFilter, setZoneFilter] = useState("all");
  const [query, setQuery] = useState("");
  const [selectedId, setSelectedId] = useState(issues[0]?.id);
  const [mobileDetail, setMobileDetail] = useState(false);

  const counts = useMemo(() => ({
    all: issues.length,
    open: issues.filter(i=>i.status==="open").length,
    acknowledged: issues.filter(i=>i.status==="acknowledged").length,
    "signed-off": issues.filter(i=>i.status==="signed-off").length,
  }), [issues]);

  const filtered = useMemo(() => {
    return issues
      .filter(i => filter==="all" ? true : i.status===filter)
      .filter(i => sevFilter==="all" ? true : i.severity===sevFilter)
      .filter(i => zoneFilter==="all" ? true : i.zone===zoneFilter)
      .filter(i => {
        if (!query.trim()) return true;
        const q = query.toLowerCase();
        return (i.id+" "+i.mhz+" "+i.equipment+" "+i.notes+" "+zoneMeta(i.zone)?.name).toLowerCase().includes(q);
      })
      .sort((a,b) => {
        // critical first within status, then newest first
        const sevOrder = { critical:0, high:1, medium:2, low:3 };
        if (a.status !== b.status) {
          const o = { open:0, acknowledged:1, "signed-off":2 };
          return o[a.status]-o[b.status];
        }
        if (a.severity !== b.severity) return sevOrder[a.severity] - sevOrder[b.severity];
        return new Date(b.created) - new Date(a.created);
      });
  }, [issues, filter, sevFilter, zoneFilter, query]);

  const selected = filtered.find(i=>i.id===selectedId) || filtered[0];

  return (
    <div className={`review-view ${mobileDetail?"show-detail":""}`}>
      <section className="inbox">
        <div className="inbox-head">
          <div className="filter-tabs">
            {[
              {id:"open", label:"OPEN"},
              {id:"acknowledged", label:"ACK"},
              {id:"signed-off", label:"SIGNED"},
              {id:"all", label:"ALL"},
            ].map(t => (
              <button key={t.id} className={filter===t.id?"on":""} onClick={()=>setFilter(t.id)}>
                {t.label}<span className="ft-count">{counts[t.id]}</span>
              </button>
            ))}
          </div>
          <div className="filter-row">
            <input className="search" placeholder="search issues, freqs, equip…" value={query} onChange={e=>setQuery(e.target.value)} />
            <select className="select" value={sevFilter} onChange={e=>setSevFilter(e.target.value)}>
              <option value="all">All severity</option>
              {SEVERITY.map(s => <option key={s.id} value={s.id}>{s.label}</option>)}
            </select>
            <select className="select" value={zoneFilter} onChange={e=>setZoneFilter(e.target.value)}>
              <option value="all">All zones</option>
              {zones.map(z => <option key={z.id} value={z.id}>{z.short}</option>)}
            </select>
          </div>
        </div>

        <div className="inbox-list">
          {filtered.length === 0 && <div className="inbox-empty">No issues match.</div>}
          {filtered.map(i => (
            <IssueRow key={i.id} issue={i} selected={selected?.id===i.id}
              onClick={() => { setSelectedId(i.id); setMobileDetail(true); }} />
          ))}
        </div>
      </section>

      <section className="detail">
        {selected ? (
          <IssueDetail
            issue={selected}
            onBack={() => setMobileDetail(false)}
            onAcknowledge={(note) => patchIssue(selected.id, {
              status: "acknowledged",
              assignee: "D. Jones",
              history: [...selected.history, { t: new Date().toISOString(), who: "D. Jones", what: note ? `Acknowledged — ${note}` : "Acknowledged" }],
            })}
            onSignOff={(resolution) => patchIssue(selected.id, {
              status: "signed-off",
              resolved: new Date().toISOString(),
              resolution,
              history: [...selected.history, { t: new Date().toISOString(), who: "D. Jones", what: `Signed off — ${resolution}` }],
            })}
            onReopen={() => patchIssue(selected.id, {
              status: "open",
              resolved: null, resolution: null,
              history: [...selected.history, { t: new Date().toISOString(), who: "D. Jones", what: "Re-opened" }],
            })}
          />
        ) : (
          <div className="detail-empty">No issue selected.</div>
        )}
      </section>
    </div>
  );
}

function IssueRow({ issue, selected, onClick }) {
  const { zones } = useZones();
  const z = zones.find(x => x.id === issue.zone);
  return (
    <button className={`row ${selected?"on":""} status-${issue.status}`} onClick={onClick}>
      <div className="row-l">
        <span className={`sev-bar sev-${issue.severity}`} />
        <div className="row-main">
          <div className="row-line1">
            <span className="row-zone mono">{z?.short}</span>
            <span className="row-mhz mono">{issue.mhz}</span>
            <span className="row-dot">·</span>
            <span className="row-equip">{issue.equipment || "—"}</span>
          </div>
          <div className="row-line2">
            {issue.types.map(t => <span key={t} className="tag">{typeMeta(t).code}</span>)}
            <span className="row-notes">{issue.notes}</span>
          </div>
        </div>
      </div>
      <div className="row-r">
        <span className={`status-pill status-${issue.status}`}>
          {issue.status==="open" && "OPEN"}
          {issue.status==="acknowledged" && "ACK"}
          {issue.status==="signed-off" && "✓ SIGNED"}
        </span>
        <span className="row-id mono">{issue.id}</span>
        <span className="row-time">{fmtRelative(issue.created)}</span>
      </div>
    </button>
  );
}

function IssueDetail({ issue, onBack, onAcknowledge, onSignOff, onReopen }) {
  const { zones } = useZones();
  const z = zones.find(x => x.id === issue.zone);
  const [resolution, setResolution] = useState("");
  const [ackNote, setAckNote] = useState("");
  const [confirmReopen, setConfirmReopen] = useState(false);

  // reset action fields when switching issues
  useEffect(() => { setResolution(""); setAckNote(""); setConfirmReopen(false); }, [issue.id]);

  return (
    <div className="detail-inner">
      <div className="detail-head">
        <button className="back-btn" onClick={onBack}>‹ Back</button>
        <div className="detail-head-main">
          <div className="dh-l1">
            <span className="dh-id mono">{issue.id}</span>
            <span className={`status-pill status-${issue.status}`}>
              {issue.status==="open" && "OPEN"}
              {issue.status==="acknowledged" && "ACKNOWLEDGED"}
              {issue.status==="signed-off" && "✓ SIGNED OFF"}
            </span>
            <span className={`sev-pill sev-${issue.severity}`}>{sevMeta(issue.severity).label.toUpperCase()}</span>
          </div>
          <h2>{z?.name} <span className="dh-mhz mono">· {issue.mhz} MHz</span></h2>
        </div>
      </div>

      <div className="detail-grid">
        <DetailStat label="Zone" value={<span className="mono">{z?.short}</span>} />
        <DetailStat label="Frequency" value={<span className="mono">{issue.mhz} MHz</span>} />
        <DetailStat label="Equipment" value={<span className="mono">{issue.equipment || "—"}</span>} />
        <DetailStat label="Reporter" value={issue.reporter} />
        <DetailStat label="Created" value={fmtTime(issue.created)} sub={fmtRelative(issue.created)} />
        <DetailStat label="Assignee" value={issue.assignee || "—"} />
      </div>

      <div className="detail-section">
        <div className="ds-head">ISSUE TYPES</div>
        <div className="ds-tags">
          {issue.types.map(t => (
            <span key={t} className="tag-lg"><span className="tag-code mono">{typeMeta(t).code}</span>{typeMeta(t).label}</span>
          ))}
        </div>
      </div>

      <div className="detail-section">
        <div className="ds-head">REPORTER NOTES</div>
        <p className="notes-body">{issue.notes}</p>
      </div>

      {issue.resolution && (
        <div className="detail-section resolved-block">
          <div className="ds-head">RESOLUTION</div>
          <p className="notes-body">{issue.resolution}</p>
        </div>
      )}

      <div className="detail-section">
        <div className="ds-head">HISTORY</div>
        <ul className="history">
          {issue.history.map((h, idx) => (
            <li key={idx}>
              <span className="hist-time mono">{fmtTime(h.t)}</span>
              <span className="hist-who">{h.who}</span>
              <span className="hist-what">{h.what}</span>
            </li>
          ))}
        </ul>
      </div>

      <div className="action-bar">
        {issue.status === "open" && (
          <div className="action-block">
            <div className="ab-head">ACKNOWLEDGE</div>
            <input className="text-input" placeholder="optional note — assigning, eta, etc." value={ackNote} onChange={e=>setAckNote(e.target.value)} />
            <button className="btn-primary" onClick={()=>onAcknowledge(ackNote)}>
              <span className="btn-glyph">▸</span> Acknowledge
            </button>
          </div>
        )}
        {issue.status === "acknowledged" && (
          <div className="action-block">
            <div className="ab-head">SIGN OFF</div>
            <textarea className="text-input" rows={3} placeholder="what did you do? required — this becomes the resolution record." value={resolution} onChange={e=>setResolution(e.target.value)} />
            <button className={`btn-primary ${resolution.trim().length<4?"disabled":""}`} disabled={resolution.trim().length<4} onClick={()=>onSignOff(resolution)}>
              <span className="btn-glyph">✓</span> Sign off & resolve
            </button>
          </div>
        )}
        {issue.status === "signed-off" && (
          <div className="action-block resolved-cta">
            <div className="ab-head">RESOLVED</div>
            <p>Signed off {issue.resolved ? fmtRelative(issue.resolved) : ""}. Reopen if it recurs.</p>
            {confirmReopen ? (
              <div className="reopen-confirm">
                <button className="btn-secondary" onClick={()=>setConfirmReopen(false)}>Cancel</button>
                <button className="btn-warn" onClick={()=>{ onReopen(); setConfirmReopen(false); }}>Confirm reopen</button>
              </div>
            ) : (
              <button className="btn-secondary" onClick={()=>setConfirmReopen(true)}>Reopen issue</button>
            )}
          </div>
        )}
      </div>
    </div>
  );
}

function DetailStat({ label, value, sub }) {
  return (
    <div className="dstat">
      <div className="dstat-label">{label}</div>
      <div className="dstat-value">{value}</div>
      {sub && <div className="dstat-sub">{sub}</div>}
    </div>
  );
}

// ─── root ───────────────────────────────────────────────────────────────
function App() {
  const role = useMemo(() => {
    const p = new URLSearchParams(window.location.search);
    return p.get("role") === "tech" ? "tech" : "admin";
  }, []);
  const [view, setView] = useState("log");
  const [issues, setIssues] = useState([]);
  const [zones, setZones] = useState(window.RF_ZONES);
  const [status, setStatus] = useState("loading"); // loading | online | offline
  const [name, setName] = useState(() => {
    try { return localStorage.getItem("rf-reporter-name") || ""; }
    catch { return ""; }
  });

  const saveName = (n) => {
    const trimmed = n.trim();
    setName(trimmed);
    try { localStorage.setItem("rf-reporter-name", trimmed); } catch {}
  };

  const refresh = useCallback(async () => {
    try {
      const [issuesData, zoneNames] = await Promise.all([
        api.listIssues(),
        api.listZoneNames(),
      ]);
      setIssues(issuesData);
      setZones(window.RF_ZONES.map(z =>
        zoneNames[z.id] ? { ...z, name: zoneNames[z.id] } : z
      ));
      setStatus("online");
    } catch (err) {
      console.error("API unreachable:", err);
      setStatus("offline");
    }
  }, []);

  useEffect(() => { refresh(); }, [refresh]);

  // poll while review view is open so new tech submissions show up
  useEffect(() => {
    if (view !== "review" || role === "tech") return;
    const i = setInterval(() => { refresh(); }, 8000);
    return () => clearInterval(i);
  }, [view, role, refresh]);

  const renameZone = async (id, name) => {
    setZones(prev => prev.map(z => z.id === id ? { ...z, name } : z));
    try { await api.setZoneName(id, name); } catch (e) { console.error(e); }
  };

  const addIssue = async (issue) => {
    setIssues(prev => [issue, ...prev]);
    try { await api.createIssue(issue); } catch (e) { console.error(e); }
  };

  const patchIssue = async (id, patch) => {
    setIssues(prev => prev.map(i => i.id === id ? { ...i, ...patch } : i));
    try { await api.updateIssue(id, patch); } catch (e) { console.error(e); }
  };

  const effectiveView = role === "tech" ? "log" : view;
  const openCount = issues.filter(i => i.status === "open").length;

  return (
    <ZonesCtx.Provider value={{ zones, renameZone }}>
      <div className={`app role-${role}`}>
        <TopBar view={effectiveView} setView={setView} openCount={openCount} role={role} status={status} onRetry={refresh} name={name} onChangeName={() => saveName("")} />
        <main className="main">
          {status === "loading" ? (
            <div className="app-loading">
              <span className="spinner" />
              <span className="loading-text mono">CONNECTING…</span>
            </div>
          ) : effectiveView === "log" ? (
            <LogView onSubmit={addIssue} role={role} name={name} />
          ) : (
            <ReviewView issues={issues} patchIssue={patchIssue} />
          )}
        </main>
        {!name && status !== "loading" && <NameGate role={role} onSubmit={saveName} />}
      </div>
    </ZonesCtx.Provider>
  );
}

function NameGate({ role, onSubmit }) {
  const [v, setV] = useState("");
  const ref = useRef(null);
  useEffect(() => { ref.current?.focus(); }, []);
  const submit = (e) => {
    e.preventDefault();
    if (v.trim().length < 2) return;
    onSubmit(v);
  };
  return (
    <div className="name-gate">
      <div className="name-gate-bg" />
      <form className="name-gate-card" onSubmit={submit}>
        <div className="ng-kicker">RF·OPS · ISSUE LOGGER</div>
        <h1 className="ng-title">Who's logging this?</h1>
        <p className="ng-sub">
          {role === "tech"
            ? "Your name goes on every issue you raise so the review team knows who to follow up with."
            : "We'll attach your name to anything you log or sign off."}
        </p>
        <input
          ref={ref}
          className="ng-input"
          placeholder="e.g. M. Doyle"
          value={v}
          onChange={(e) => setV(e.target.value)}
          autoComplete="name"
          autoCapitalize="words"
        />
        <button type="submit" className={`btn-primary ng-btn ${v.trim().length<2?"disabled":""}`} disabled={v.trim().length<2}>
          <span className="btn-glyph">▸</span> Start logging
        </button>
        <div className="ng-tip mono">SAVED ON THIS DEVICE · YOU WON'T BE ASKED AGAIN</div>
      </form>
    </div>
  );
}

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