// Bug editor — the write half of the two-way Jira sync.
//
// Opens on `ns:edit-bug` (detail = issue key) or `ns:new-bug`. Every field here
// writes straight through to Jira; there is no local copy to reconcile.
//
// Vocabulary is Jira's: priorities are the project's real names (P0..P3), and
// status is moved with the project's real transitions ("to in progress", "Done"),
// because `status` is not on Jira's edit screen and can't be PUT.
//
// "Close" transitions the issue to a Done status. It never deletes it: the bug,
// its comments and its history are the team's record. In the AD workflow there
// is no direct Created -> Done edge, so the server walks the graph.
const { useState: useStateBE, useEffect: useEffectBE } = React;

const BE_SEVS = ["SEV1", "SEV2", "SEV3", "SEV4"];

function Field({ label, hint, children }) {
  return (
    <label className="be-field">
      <span className="be-label">{label}{hint && <em>{hint}</em>}</span>
      {children}
    </label>
  );
}

function BugEditor() {
  const [open, setOpen] = useStateBE(false);
  const [mode, setMode] = useStateBE("edit");     // "edit" | "create"
  const [key, setKey] = useStateBE("");
  const [busy, setBusy] = useStateBE(false);
  const [err, setErr] = useStateBE("");
  const [data, setData] = useStateBE(null);        // { bug, transitions, assignees }
  const [form, setForm] = useStateBE({});
  const [confirmClose, setConfirmClose] = useStateBE(false);

  useEffectBE(() => {
    const onEdit = (e) => openEdit(e.detail);
    const onNew = () => openCreate();
    window.addEventListener("ns:edit-bug", onEdit);
    window.addEventListener("ns:new-bug", onNew);
    return () => { window.removeEventListener("ns:edit-bug", onEdit); window.removeEventListener("ns:new-bug", onNew); };
  }, []);

  async function openEdit(k) {
    setOpen(true); setMode("edit"); setKey(k); setErr(""); setData(null); setConfirmClose(false);
    const r = await window.NS.admin.bugs.get(k);
    if (!r.ok) { setErr(errText(r)); return; }
    setData(r);
    setForm({ title: r.bug.title, description: r.bug.description, severity: r.bug.severity,
              assigneeAccountId: r.bug.assignee ? r.bug.assignee.accountId : "" });
  }

  async function openCreate() {
    setOpen(true); setMode("create"); setKey(""); setErr(""); setConfirmClose(false);
    setForm({ title: "", description: "", severity: "SEV3", assigneeAccountId: "", destination: "" });
    setData({ bug: null, transitions: [], assignees: [] });
    // Project-scoped assignees — there's no issue key to hang them off yet.
    const m = await window.NS.admin.bugs.meta();
    if (!m.ok) return setErr(errText(m));
    setData({ bug: null, transitions: [], assignees: m.assignees || [] });
  }

  function close() { setOpen(false); setData(null); setErr(""); }
  const set = (k, v) => setForm((f) => Object.assign({}, f, { [k]: v }));

  async function save() {
    setBusy(true); setErr("");
    let res;
    if (mode === "create") {
      res = await window.NS.admin.bugs.create({
        title: form.title, description: form.description, actual: form.description,
        severity: form.severity, assigneeAccountId: form.assigneeAccountId || undefined,
        destination: form.destination || undefined, createdBy: "admin",
      });
    } else {
      res = await window.NS.admin.bugs.update(key, {
        title: form.title, description: form.description, severity: form.severity,
        assigneeAccountId: form.assigneeAccountId || null,
      });
    }
    setBusy(false);
    if (!res.ok) return setErr(errText(res));
    if (window.fireToast) window.fireToast(mode === "create" ? `${res.key} created in Jira` : `${key} updated in Jira`);
    window.dispatchEvent(new CustomEvent("ns:bugs-changed"));
    close();
  }

  async function applyTransition(to) {
    setBusy(true); setErr("");
    const res = await window.NS.admin.bugs.transition(key, to);
    setBusy(false);
    if (!res.ok) return setErr(errText(res));
    setData((d) => Object.assign({}, d, { bug: res.bug }));
    if (window.fireToast) window.fireToast(`${key} → ${res.bug.status}`);
    window.dispatchEvent(new CustomEvent("ns:bugs-changed"));
    // Transitions change which transitions are available next — refetch.
    const fresh = await window.NS.admin.bugs.get(key);
    if (fresh.ok) setData(fresh);
  }

  async function doClose() {
    setBusy(true); setErr("");
    const res = await window.NS.admin.bugs.close(key);
    setBusy(false);
    if (!res.ok) return setErr(errText(res));
    if (window.fireToast) window.fireToast(`${key} closed — ${res.bug.status}`);
    window.dispatchEvent(new CustomEvent("ns:bugs-changed"));
    close();
  }

  if (!open) return null;

  const bug = data && data.bug;
  const assignees = (data && data.assignees) || [];
  const transitions = (data && data.transitions) || [];
  const priority = window.NS.priorityFor(form.severity || "SEV3");

  return (
    <div className="be-backdrop" onClick={(e) => { if (e.target === e.currentTarget && !busy) close(); }}>
      <div className="be-modal card" role="dialog" aria-modal="true">
        <div className="be-head">
          <div>
            <div className="eyebrow">{mode === "create" ? "New bug" : bug ? bug.key : key}</div>
            <h2 className="be-title">{mode === "create" ? "Create a bug in Jira" : "Edit in Jira"}</h2>
          </div>
          {bug && bug.url && <a className="btn btn-ghost btn-sm" href={bug.url} target="_blank" rel="noreferrer">Open in Jira ↗</a>}
          <button className="icon-btn" onClick={close} aria-label="Close" disabled={busy}>✕</button>
        </div>

        {err && <div className="gate-err be-err">{err}</div>}

        {mode === "edit" && !data && !err && <div className="pq-empty">Loading from Jira…</div>}

        {(mode === "create" || data) && (
          <div className="be-body">
            <Field label="Summary">
              <input value={form.title || ""} onChange={(e) => set("title", e.target.value)}
                placeholder="What's broken?" disabled={busy} />
            </Field>

            <Field label="Description" hint="plain text · lines starting with ## become headings">
              <textarea rows={7} value={form.description || ""} onChange={(e) => set("description", e.target.value)}
                placeholder={"## Steps to reproduce\nOpen the Forecast page…"} disabled={busy} />
            </Field>

            <div className="be-grid">
              <Field label="Priority" hint={`Jira ${priority}`}>
                <select value={form.severity || "SEV3"} onChange={(e) => set("severity", e.target.value)} disabled={busy}>
                  {BE_SEVS.map((s) => (
                    <option key={s} value={s}>{window.NS.priorityFor(s)} · {window.NS.sev(s).label}</option>
                  ))}
                </select>
              </Field>

              <Field label="Assignee">
                <select value={form.assigneeAccountId || ""} onChange={(e) => set("assigneeAccountId", e.target.value)} disabled={busy}>
                  <option value="">Unassigned</option>
                  {assignees.map((u) => <option key={u.accountId} value={u.accountId}>{u.name}</option>)}
                </select>
              </Field>

              {mode === "create" && (
                <Field label="Destination" hint="defaults to the severity rule">
                  <select value={form.destination || ""} onChange={(e) => set("destination", e.target.value)} disabled={busy}>
                    <option value="">Use the rule</option>
                    <option value="sprint">Current sprint</option>
                    <option value="backlog">Backlog</option>
                  </select>
                </Field>
              )}
            </div>

            {mode === "edit" && bug && (
              <div className="be-status">
                <div className="be-label">Status <em>moves through the real Jira workflow</em></div>
                <div className="be-status-row">
                  <span className={"status-pill " + window.NS.statusClass(window.NS.categoryOfStatus(bug.status, bug.statusCategory))}>
                    <span className="sev-dot" />{bug.status}
                  </span>
                  <span className="be-arrow">→</span>
                  {transitions.length
                    ? transitions.map((t) => (
                        <button key={t.id} className="btn btn-ghost btn-sm" disabled={busy}
                          onClick={() => applyTransition(t.id)} title={`Moves to "${t.to}"`}>
                          {t.name}
                        </button>
                      ))
                    : <span className="rs-dim">no transitions available</span>}
                </div>
              </div>
            )}
          </div>
        )}

        <div className="be-foot">
          {mode === "edit" && bug && (
            confirmClose
              ? (
                <div className="be-confirm">
                  <span>Close <strong>{bug.key}</strong> as Done? The issue stays in Jira.</span>
                  <button className="btn btn-danger btn-sm" onClick={doClose} disabled={busy}>Yes, close it</button>
                  <button className="btn btn-ghost btn-sm" onClick={() => setConfirmClose(false)} disabled={busy}>Cancel</button>
                </div>
              )
              : <button className="btn btn-ghost btn-sm" onClick={() => setConfirmClose(true)} disabled={busy}>Close as Done</button>
          )}
          <div className="be-foot-right">
            <button className="btn btn-ghost btn-sm" onClick={close} disabled={busy}>Cancel</button>
            <button className="btn btn-primary" onClick={save} disabled={busy || !(form.title || "").trim()}>
              {busy ? "Saving…" : mode === "create" ? "Create in Jira" : "Save to Jira"}
            </button>
          </div>
        </div>
      </div>
    </div>
  );
}

function errText(res) {
  if (!res) return "Something went wrong.";
  switch (res.error) {
    case "jira_not_configured": return `Jira isn't configured — set ${(res.missing || []).join(", ")}.`;
    case "not_a_bug": return `That issue is a ${res.issuetype}, not a Bug. This portal only manages bugs.`;
    case "cannot_close": return `Couldn't close it: ${res.reason}.`;
    case "transition_unavailable": return `That status isn't reachable from here. Available: ${(res.available || []).map((a) => a.name).join(", ")}.`;
    case "admin_required": return "Your admin session expired. Sign in again.";
    case "not_found": return "That issue no longer exists in Jira.";
    default: return res.error || "Something went wrong.";
  }
}

window.BugEditor = BugEditor;
