package handlers

import (
	"encoding/json"
	"errors"
	"net/http"

	"github.com/google/uuid"

	"mgit.msbls.de/m/paliad/internal/services"
)

// t-paliad-340 / m/paliad#153 B0 — REST endpoints over the new normalised
// scenario builder shape (paliad.scenarios with owner_id, +
// paliad.scenario_proceedings / scenario_events / scenario_shares).
//
// Endpoints live under /api/builder/scenarios/* to avoid clashing with
// the legacy /api/scenarios/* endpoints from m/paliad#124 Slice D. The
// B6 cleanup slice retires the legacy surface; until then both shapes
// coexist on the same paliad.scenarios table (the legacy paths require
// project_id IS NOT NULL OR an abstract created_by = caller; the builder
// paths require owner_id = caller).
//
// All handlers gate by requireScenarioBuilderService — 503 when the
// service is nil (DATABASE_URL unset). Auth is checked via requireUser;
// per-row visibility is enforced inside the service.

func requireScenarioBuilderService(w http.ResponseWriter) bool {
	if dbSvc == nil || dbSvc.scenarioBuilder == nil {
		writeJSON(w, http.StatusServiceUnavailable, map[string]string{
			"error": "Litigation-Builder ist vorübergehend nicht verfügbar (keine Datenbank).",
		})
		return false
	}
	return true
}

// scenarioBuilderErrorToStatus maps service errors to HTTP statuses.
func scenarioBuilderErrorToStatus(err error) (int, string) {
	switch {
	case errors.Is(err, services.ErrScenarioBuilderNotVisible),
		errors.Is(err, services.ErrNotVisible):
		return http.StatusNotFound, "Szenario nicht gefunden"
	case errors.Is(err, services.ErrInvalidInput):
		return http.StatusBadRequest, err.Error()
	}
	return http.StatusInternalServerError, err.Error()
}

func writeBuilderError(w http.ResponseWriter, err error) {
	status, msg := scenarioBuilderErrorToStatus(err)
	writeJSON(w, status, map[string]string{"error": msg})
}

// ---------------------------------------------------------------------------
// Akte mode (B4, t-paliad-347)
// ---------------------------------------------------------------------------

// handleBuilderScenarioFromProject — POST /api/builder/scenarios/from-project
//
// Body: {"project_id": "<uuid>"}
//
// Creates a fresh project-backed scenario by snapshotting the project's
// proceeding_type_id + our_side + scenario_flags into one top-level
// triplet, and seeds scenario_events from every existing
// paliad.deadlines row tied to a sequencing_rule. The new scenario's
// origin_project_id pins the Akte link so subsequent edits dual-write
// through to paliad.deadlines + paliad.projects.scenario_flags (PRD §2.3).
//
// Visibility: caller must be able to see the project. Bad input
// (missing proceeding_type_id, invisible project) returns 400 / 404
// via the standard service-error mapping.
func handleBuilderScenarioFromProject(w http.ResponseWriter, r *http.Request) {
	if !requireScenarioBuilderService(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	var body struct {
		ProjectID uuid.UUID `json:"project_id"`
	}
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "ungültige Anfrage"})
		return
	}
	if body.ProjectID == uuid.Nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "project_id ist erforderlich"})
		return
	}
	out, err := dbSvc.scenarioBuilder.CreateScenarioFromProject(r.Context(), uid, body.ProjectID)
	if err != nil {
		writeBuilderError(w, err)
		return
	}
	writeJSON(w, http.StatusCreated, out)
}

// ---------------------------------------------------------------------------
// Scenario CRUD
// ---------------------------------------------------------------------------

// handleBuilderScenariosList — GET /api/builder/scenarios?status=<active|archived|promoted|all>
func handleBuilderScenariosList(w http.ResponseWriter, r *http.Request) {
	if !requireScenarioBuilderService(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	status := r.URL.Query().Get("status")
	out, err := dbSvc.scenarioBuilder.ListMyScenarios(r.Context(), uid, status)
	if err != nil {
		writeBuilderError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, out)
}

// handleBuilderScenarioCreate — POST /api/builder/scenarios
func handleBuilderScenarioCreate(w http.ResponseWriter, r *http.Request) {
	if !requireScenarioBuilderService(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	var input services.CreateBuilderScenarioInput
	if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "ungültige Anfrage"})
		return
	}
	out, err := dbSvc.scenarioBuilder.CreateScenario(r.Context(), uid, input)
	if err != nil {
		writeBuilderError(w, err)
		return
	}
	writeJSON(w, http.StatusCreated, out)
}

// handleBuilderScenarioGet — GET /api/builder/scenarios/{id}
func handleBuilderScenarioGet(w http.ResponseWriter, r *http.Request) {
	if !requireScenarioBuilderService(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	id, err := uuid.Parse(r.PathValue("id"))
	if err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "ungültige ID"})
		return
	}
	out, err := dbSvc.scenarioBuilder.GetScenarioDeep(r.Context(), uid, id)
	if err != nil {
		writeBuilderError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, out)
}

// handleBuilderScenarioPatch — PATCH /api/builder/scenarios/{id}
func handleBuilderScenarioPatch(w http.ResponseWriter, r *http.Request) {
	if !requireScenarioBuilderService(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	id, err := uuid.Parse(r.PathValue("id"))
	if err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "ungültige ID"})
		return
	}
	var input services.PatchBuilderScenarioInput
	if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "ungültige Anfrage"})
		return
	}
	out, err := dbSvc.scenarioBuilder.PatchScenario(r.Context(), uid, id, input)
	if err != nil {
		writeBuilderError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, out)
}

// ---------------------------------------------------------------------------
// Proceedings
// ---------------------------------------------------------------------------

// handleBuilderProceedingCreate — POST /api/builder/scenarios/{id}/proceedings
func handleBuilderProceedingCreate(w http.ResponseWriter, r *http.Request) {
	if !requireScenarioBuilderService(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	sid, err := uuid.Parse(r.PathValue("id"))
	if err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "ungültige Szenario-ID"})
		return
	}
	var input services.AddProceedingInput
	if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "ungültige Anfrage"})
		return
	}
	out, err := dbSvc.scenarioBuilder.AddProceeding(r.Context(), uid, sid, input)
	if err != nil {
		writeBuilderError(w, err)
		return
	}
	writeJSON(w, http.StatusCreated, out)
}

// handleBuilderProceedingPatch — PATCH /api/builder/scenarios/{id}/proceedings/{pid}
func handleBuilderProceedingPatch(w http.ResponseWriter, r *http.Request) {
	if !requireScenarioBuilderService(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	sid, err := uuid.Parse(r.PathValue("id"))
	if err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "ungültige Szenario-ID"})
		return
	}
	pid, err := uuid.Parse(r.PathValue("pid"))
	if err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "ungültige Proceeding-ID"})
		return
	}
	var input services.PatchProceedingInput
	if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "ungültige Anfrage"})
		return
	}
	out, err := dbSvc.scenarioBuilder.PatchProceeding(r.Context(), uid, sid, pid, input)
	if err != nil {
		writeBuilderError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, out)
}

// handleBuilderProceedingDelete — DELETE /api/builder/scenarios/{id}/proceedings/{pid}
func handleBuilderProceedingDelete(w http.ResponseWriter, r *http.Request) {
	if !requireScenarioBuilderService(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	sid, err := uuid.Parse(r.PathValue("id"))
	if err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "ungültige Szenario-ID"})
		return
	}
	pid, err := uuid.Parse(r.PathValue("pid"))
	if err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "ungültige Proceeding-ID"})
		return
	}
	if err := dbSvc.scenarioBuilder.DeleteProceeding(r.Context(), uid, sid, pid); err != nil {
		writeBuilderError(w, err)
		return
	}
	w.WriteHeader(http.StatusNoContent)
}

// ---------------------------------------------------------------------------
// Events
// ---------------------------------------------------------------------------

// handleBuilderEventCreate — POST /api/builder/scenarios/{id}/proceedings/{pid}/events
func handleBuilderEventCreate(w http.ResponseWriter, r *http.Request) {
	if !requireScenarioBuilderService(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	sid, err := uuid.Parse(r.PathValue("id"))
	if err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "ungültige Szenario-ID"})
		return
	}
	pid, err := uuid.Parse(r.PathValue("pid"))
	if err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "ungültige Proceeding-ID"})
		return
	}
	var input services.AddEventInput
	if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "ungültige Anfrage"})
		return
	}
	out, err := dbSvc.scenarioBuilder.AddEvent(r.Context(), uid, sid, pid, input)
	if err != nil {
		writeBuilderError(w, err)
		return
	}
	writeJSON(w, http.StatusCreated, out)
}

// handleBuilderEventPatch — PATCH /api/builder/scenarios/{id}/events/{eid}
func handleBuilderEventPatch(w http.ResponseWriter, r *http.Request) {
	if !requireScenarioBuilderService(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	sid, err := uuid.Parse(r.PathValue("id"))
	if err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "ungültige Szenario-ID"})
		return
	}
	eid, err := uuid.Parse(r.PathValue("eid"))
	if err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "ungültige Event-ID"})
		return
	}
	var input services.PatchEventInput
	if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "ungültige Anfrage"})
		return
	}
	out, err := dbSvc.scenarioBuilder.PatchEvent(r.Context(), uid, sid, eid, input)
	if err != nil {
		writeBuilderError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, out)
}

// handleBuilderEventDelete — DELETE /api/builder/scenarios/{id}/events/{eid}
func handleBuilderEventDelete(w http.ResponseWriter, r *http.Request) {
	if !requireScenarioBuilderService(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	sid, err := uuid.Parse(r.PathValue("id"))
	if err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "ungültige Szenario-ID"})
		return
	}
	eid, err := uuid.Parse(r.PathValue("eid"))
	if err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "ungültige Event-ID"})
		return
	}
	if err := dbSvc.scenarioBuilder.DeleteEvent(r.Context(), uid, sid, eid); err != nil {
		writeBuilderError(w, err)
		return
	}
	w.WriteHeader(http.StatusNoContent)
}

// ---------------------------------------------------------------------------
// Shares
// ---------------------------------------------------------------------------

// handleBuilderShareCreate — POST /api/builder/scenarios/{id}/shares
// Body: {"shared_with_user_id": "<uuid>"}
func handleBuilderShareCreate(w http.ResponseWriter, r *http.Request) {
	if !requireScenarioBuilderService(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	sid, err := uuid.Parse(r.PathValue("id"))
	if err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "ungültige Szenario-ID"})
		return
	}
	var body struct {
		SharedWithUserID uuid.UUID `json:"shared_with_user_id"`
	}
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "ungültige Anfrage"})
		return
	}
	out, err := dbSvc.scenarioBuilder.AddShare(r.Context(), uid, sid, body.SharedWithUserID)
	if err != nil {
		writeBuilderError(w, err)
		return
	}
	writeJSON(w, http.StatusCreated, out)
}

// handleBuilderShareDelete — DELETE /api/builder/scenarios/{id}/shares/{sid}
func handleBuilderShareDelete(w http.ResponseWriter, r *http.Request) {
	if !requireScenarioBuilderService(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	scid, err := uuid.Parse(r.PathValue("id"))
	if err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "ungültige Szenario-ID"})
		return
	}
	shid, err := uuid.Parse(r.PathValue("sid"))
	if err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "ungültige Share-ID"})
		return
	}
	if err := dbSvc.scenarioBuilder.DeleteShare(r.Context(), uid, scid, shid); err != nil {
		writeBuilderError(w, err)
		return
	}
	w.WriteHeader(http.StatusNoContent)
}

// ---------------------------------------------------------------------------
// Shared-with-me + Promote (B5, m/paliad#153)
// ---------------------------------------------------------------------------

// handleBuilderScenariosShared — GET /api/builder/scenarios/shared
//
// Lists scenarios shared read-only with the caller (the "Geteilt mit mir"
// side-panel bucket, PRD §2.5). The caller's own scenarios are excluded.
func handleBuilderScenariosShared(w http.ResponseWriter, r *http.Request) {
	if !requireScenarioBuilderService(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	out, err := dbSvc.scenarioBuilder.ListSharedWithMe(r.Context(), uid)
	if err != nil {
		writeBuilderError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, out)
}

// handleBuilderScenarioPromote — POST /api/builder/scenarios/{id}/promote
//
// Body: PromoteScenarioInput (wizard steps 2 + 3). Promotes the scenario
// into a real paliad.projects 'case' row transactionally (PRD §10 — no
// partial promotions) and returns PromoteResult with the new project id
// the wizard navigates to (/projects/{project_id}).
func handleBuilderScenarioPromote(w http.ResponseWriter, r *http.Request) {
	if !requireScenarioBuilderService(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	sid, err := uuid.Parse(r.PathValue("id"))
	if err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "ungültige Szenario-ID"})
		return
	}
	var input services.PromoteScenarioInput
	if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "ungültige Anfrage"})
		return
	}
	out, err := dbSvc.scenarioBuilder.PromoteScenario(r.Context(), uid, sid, input)
	if err != nil {
		writeBuilderError(w, err)
		return
	}
	writeJSON(w, http.StatusCreated, out)
}

// ---------------------------------------------------------------------------
// Scenario flag catalog passthrough (m/paliad#153 B2)
// ---------------------------------------------------------------------------

// handleBuilderFlagCatalog — GET /api/builder/scenario-flag-catalog
//
// Returns every row of paliad.scenario_flag_catalog so the Litigation
// Builder can render per-triplet flag toggles without a per-project
// round-trip. The catalog itself is global (no jurisdiction or
// proceeding scope baked into the table); which flags actually apply
// to a given proceeding type is decided by the calc engine via
// condition_expr at calculation time. The client renders every catalog
// flag and lets the user toggle them — flags with no effect on the
// active proceeding's rules simply have no condition_expr referencing
// them, so toggling is a no-op.
//
// 503 when ScenarioFlagsService is nil (DATABASE_URL unset); per-row
// visibility checks aren't needed because the catalog is global.
func handleBuilderFlagCatalog(w http.ResponseWriter, r *http.Request) {
	if dbSvc == nil || dbSvc.scenarioFlags == nil {
		writeJSON(w, http.StatusServiceUnavailable, map[string]string{
			"error": "Flag-Katalog vorübergehend nicht verfügbar (keine Datenbank).",
		})
		return
	}
	if _, ok := requireUser(w, r); !ok {
		return
	}
	out, err := dbSvc.scenarioFlags.ListCatalog(r.Context())
	if err != nil {
		writeJSON(w, http.StatusInternalServerError, map[string]string{
			"error": "Flag-Katalog konnte nicht geladen werden",
		})
		return
	}
	writeJSON(w, http.StatusOK, out)
}

// ---------------------------------------------------------------------------
// Dev-only test route
// ---------------------------------------------------------------------------

// handleBuilderDevTestPage — GET /dev/scenario-builder
//
// Gated to services.PaliadinOwnerEmail (the same single-owner gate the
// /paliadin route uses). Every other authenticated user gets 404. Pure
// HTML — no JS bundle — so the page works even before B1 wires the real
// builder shell. Renders curl-equivalent forms for the B0 surface so the
// schema can be exercised end-to-end without Postman / shell scripts.
//
// This is the "dev-only test route" the head's task spec asked for. It
// disappears in B6 cleanup once the production builder UI ships at
// /tools/procedures.
func handleBuilderDevTestPage(w http.ResponseWriter, r *http.Request) {
	if !requirePaliadinOwner(w, r) {
		return
	}
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	w.Header().Set("Cache-Control", "no-store")
	_, _ = w.Write([]byte(builderDevTestHTML))
}

const builderDevTestHTML = `<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<title>Scenario Builder — Dev Test (B0)</title>
<style>
body { font-family: ui-monospace, Menlo, monospace; max-width: 880px; margin: 2em auto;
       padding: 0 1em; color: #222; background: #fafaf7; }
h1, h2 { font-family: ui-sans-serif, system-ui, sans-serif; }
h1 { border-bottom: 4px solid #c6f41c; padding-bottom: .2em; }
section { background: #fff; border: 1px solid #ddd; border-radius: 4px;
          padding: 1em 1.2em; margin: 1em 0; }
label { display: block; margin: .4em 0 .15em; font-size: .85em; color: #555; }
input, textarea, select, button { font: inherit; padding: .35em .5em; box-sizing: border-box; }
input[type="text"], input[type="number"], textarea { width: 100%; }
button { background: #c6f41c; border: 1px solid #9ec61f; cursor: pointer;
         padding: .4em 1em; border-radius: 3px; margin: .2em 0; }
button.secondary { background: #eee; border-color: #ccc; }
pre.out { background: #1e1e1e; color: #e6e6e6; padding: .8em 1em; border-radius: 4px;
          overflow: auto; max-height: 30em; font-size: .85em; }
.note { color: #777; font-size: .9em; }
.row { display: flex; gap: .5em; }
.row > * { flex: 1; }
</style>
</head>
<body>
<h1>Scenario Builder — Dev Test (B0)</h1>
<p class="note">t-paliad-340 / m/paliad#153 — DB-only slice. Exercises
paliad.scenarios (builder rows), scenario_proceedings, scenario_events,
scenario_shares via /api/builder/scenarios/*. Gated to PaliadinOwnerEmail.</p>

<section>
  <h2>1. Liste meine Szenarien</h2>
  <label>Status filter</label>
  <select id="list-status">
    <option value="">(default: alle)</option>
    <option value="active">active</option>
    <option value="archived">archived</option>
    <option value="promoted">promoted</option>
    <option value="all">all (explicit)</option>
  </select>
  <button onclick="listScenarios()">GET /api/builder/scenarios</button>
  <pre class="out" id="list-out"></pre>
</section>

<section>
  <h2>2. Szenario anlegen</h2>
  <label>Name</label>
  <input type="text" id="create-name" placeholder="(leer = Unbenanntes Szenario)">
  <label>Notes (optional)</label>
  <textarea id="create-notes" rows="2"></textarea>
  <button onclick="createScenario()">POST /api/builder/scenarios</button>
  <pre class="out" id="create-out"></pre>
</section>

<section>
  <h2>3. Szenario abrufen (deep)</h2>
  <label>Scenario ID</label>
  <input type="text" id="get-id">
  <button onclick="getScenario()">GET /api/builder/scenarios/{id}</button>
  <pre class="out" id="get-out"></pre>
</section>

<section>
  <h2>4. Verfahren hinzufügen</h2>
  <label>Scenario ID</label>
  <input type="text" id="proc-sid">
  <label>proceeding_type_id (integer)</label>
  <input type="number" id="proc-pt-id" placeholder="z.B. 7 für upc.inf.cfi">
  <label>primary_party</label>
  <select id="proc-party">
    <option value="">(none)</option>
    <option value="claimant">claimant</option>
    <option value="defendant">defendant</option>
  </select>
  <button onclick="addProceeding()">POST .../proceedings</button>
  <pre class="out" id="proc-out"></pre>
</section>

<section>
  <h2>5. Event-Karte hinzufügen</h2>
  <label>Scenario ID</label>
  <input type="text" id="ev-sid">
  <label>Proceeding ID</label>
  <input type="text" id="ev-pid">
  <label>custom_label (oder sequencing_rule_id / procedural_event_id)</label>
  <input type="text" id="ev-label" placeholder="freitext-Karte">
  <label>state</label>
  <select id="ev-state">
    <option value="planned">planned</option>
    <option value="filed">filed</option>
    <option value="skipped">skipped</option>
  </select>
  <button onclick="addEvent()">POST .../proceedings/{pid}/events</button>
  <pre class="out" id="ev-out"></pre>
</section>

<section>
  <h2>6. Status patchen (archive / restore)</h2>
  <label>Scenario ID</label>
  <input type="text" id="patch-sid">
  <label>new status</label>
  <select id="patch-status">
    <option value="active">active</option>
    <option value="archived">archived</option>
  </select>
  <button onclick="patchStatus()">PATCH /api/builder/scenarios/{id}</button>
  <pre class="out" id="patch-out"></pre>
</section>

<script>
const j = (id, payload) =>
  document.getElementById(id).textContent = JSON.stringify(payload, null, 2);

async function call(method, url, body) {
  const opts = { method, headers: { 'Content-Type': 'application/json' } };
  if (body !== undefined) opts.body = JSON.stringify(body);
  const r = await fetch(url, opts);
  const text = await r.text();
  let parsed = text;
  try { parsed = JSON.parse(text); } catch (_) {}
  return { status: r.status, body: parsed };
}

async function listScenarios() {
  const status = document.getElementById('list-status').value;
  const q = status ? '?status=' + encodeURIComponent(status) : '';
  j('list-out', await call('GET', '/api/builder/scenarios' + q));
}

async function createScenario() {
  const name = document.getElementById('create-name').value;
  const notes = document.getElementById('create-notes').value;
  const body = {};
  if (name) body.name = name;
  if (notes) body.notes = notes;
  j('create-out', await call('POST', '/api/builder/scenarios', body));
}

async function getScenario() {
  const id = document.getElementById('get-id').value.trim();
  if (!id) return j('get-out', { error: 'ID erforderlich' });
  j('get-out', await call('GET', '/api/builder/scenarios/' + id));
}

async function addProceeding() {
  const sid = document.getElementById('proc-sid').value.trim();
  const ptID = parseInt(document.getElementById('proc-pt-id').value, 10);
  const party = document.getElementById('proc-party').value;
  if (!sid || !ptID) return j('proc-out', { error: 'sid + proceeding_type_id erforderlich' });
  const body = { proceeding_type_id: ptID };
  if (party) body.primary_party = party;
  j('proc-out', await call('POST', '/api/builder/scenarios/' + sid + '/proceedings', body));
}

async function addEvent() {
  const sid = document.getElementById('ev-sid').value.trim();
  const pid = document.getElementById('ev-pid').value.trim();
  const label = document.getElementById('ev-label').value.trim();
  const state = document.getElementById('ev-state').value;
  if (!sid || !pid || !label) return j('ev-out', { error: 'sid + pid + custom_label erforderlich' });
  j('ev-out', await call('POST',
    '/api/builder/scenarios/' + sid + '/proceedings/' + pid + '/events',
    { custom_label: label, state }));
}

async function patchStatus() {
  const sid = document.getElementById('patch-sid').value.trim();
  const status = document.getElementById('patch-status').value;
  if (!sid) return j('patch-out', { error: 'sid erforderlich' });
  j('patch-out', await call('PATCH', '/api/builder/scenarios/' + sid, { status }));
}
</script>
</body>
</html>`
