import { initI18n, onLangChange, t, tDyn, getLang } from "./i18n";
import { initSidebar } from "./sidebar";

// admin-rules-edit.ts — /admin/procedural-events/{id}/edit. Loads a single rule
// row, drives every form field, the preview widget, the audit-log
// timeline and the lifecycle action bar. Every write is gated behind
// a reason modal — the ≥10-char rule is enforced client-side per
// Slice 11a edge case #4.

interface Rule {
  id: string;
  proceeding_type_id?: number | null;
  parent_id?: string | null;
  // submission_code is the proceeding-prefixed identifier of this rule
  // within its proceeding (e.g. `upc.inf.cfi.soc`), distinct from
  // rule_code (legal citation, e.g. `RoP.013.1`).
  submission_code?: string | null;
  rule_code?: string | null;
  name: string;
  name_en: string;
  description?: string | null;
  primary_party?: string | null;
  event_type?: string | null;
  duration_value: number;
  duration_unit: string;
  timing?: string | null;
  alt_duration_value?: number | null;
  alt_duration_unit?: string | null;
  alt_rule_code?: string | null;
  anchor_alt?: string | null;
  combine_op?: string | null;
  legal_source?: string | null;
  deadline_notes?: string | null;
  deadline_notes_en?: string | null;
  priority: string;
  is_court_set: boolean;
  is_spawn: boolean;
  spawn_label?: string | null;
  spawn_proceeding_type_id?: number | null;
  trigger_event_id?: number | null;
  condition_expr?: unknown;
  sequence_order: number;
  concept_id?: string | null;
  lifecycle_state: string;
  draft_of?: string | null;
  published_at?: string | null;
  updated_at: string;
  created_at: string;
}

interface ProceedingType {
  id: number;
  code: string;
  // `name` is the German display name on the wire; the Go `ProceedingType`
  // model serialises `db:"name"` as JSON key `name`. Don't reach for
  // `name_de` — that field does not exist in this payload (m/paliad#113).
  name: string;
  name_en: string;
}

interface TriggerEvent {
  id: number;
  code: string;
  name: string;
  name_de: string;
}

interface AuditEntry {
  id: string;
  rule_id: string;
  changed_by?: string | null;
  changed_by_display_name?: string | null;
  changed_at: string;
  action: string;
  before_json?: unknown;
  after_json?: unknown;
  reason: string;
  migration_exported: boolean;
}

let ruleId = "";
let rule: Rule | null = null;
let proceedings: ProceedingType[] = [];
let triggers: TriggerEvent[] = [];
let auditEntries: AuditEntry[] = [];
let auditOffset = 0;
const AUDIT_PAGE = 20;
let auditHasMore = false;
let previewDebounce: number | undefined;

function esc(s: string | null | undefined): string {
  const d = document.createElement("div");
  d.textContent = s ?? "";
  return d.innerHTML;
}

function fmtDateTime(iso: string): string {
  if (!iso) return "";
  const d = new Date(iso);
  if (Number.isNaN(d.getTime())) return iso;
  const locale = getLang() === "de" ? "de-DE" : "en-GB";
  return d.toLocaleString(locale, {
    year: "numeric", month: "2-digit", day: "2-digit",
    hour: "2-digit", minute: "2-digit",
  });
}

function parseRuleIDFromPath(): string {
  // /admin/procedural-events/{uuid}/edit (canonical, post Slice B.6 rename)
  // /admin/rules/{uuid}/edit (legacy, 301-redirected by the backend but
  //   still matched here in case a stale tab or bookmark hits it).
  const m = /^\/admin\/(?:procedural-events|rules)\/([^\/]+)\/edit\/?$/.exec(window.location.pathname);
  return m ? decodeURIComponent(m[1]) : "";
}

function showFeedback(msg: string, isError: boolean) {
  const el = document.getElementById("rules-edit-feedback") as HTMLElement | null;
  if (!el) return;
  el.textContent = msg;
  el.className = "form-msg " + (isError ? "form-msg-error" : "form-msg-success");
  el.style.display = "block";
  if (!isError) {
    setTimeout(() => { el.style.display = "none"; }, 4000);
  }
}

function lifecycleLabel(state: string): string {
  return tDyn(`admin.rules.lifecycle.${state}`) || state;
}

function lifecycleClass(state: string): string {
  switch (state) {
    case "draft": return "admin-rules-pill admin-rules-pill-draft";
    case "published": return "admin-rules-pill admin-rules-pill-published";
    case "archived": return "admin-rules-pill admin-rules-pill-archived";
    default: return "admin-rules-pill";
  }
}

// --------------------------------------------------------------------
// Loaders.
// --------------------------------------------------------------------
async function loadProceedings(): Promise<void> {
  const resp = await fetch("/api/proceeding-types-db?category=fristenrechner");
  if (!resp.ok) return;
  proceedings = (await resp.json()) as ProceedingType[];
  fillProceedingSelect("f-proceeding", proceedings);
  fillProceedingSelect("f-spawn-proceeding", proceedings);
}

async function loadTriggers(): Promise<void> {
  const resp = await fetch("/api/tools/trigger-events");
  if (!resp.ok) return;
  triggers = (await resp.json()) as TriggerEvent[];
  const sel = document.getElementById("f-trigger") as HTMLSelectElement | null;
  if (!sel) return;
  const placeholder = sel.querySelector('option[value=""]');
  sel.innerHTML = "";
  if (placeholder) sel.appendChild(placeholder);
  for (const te of triggers) {
    const opt = document.createElement("option");
    opt.value = String(te.id);
    opt.textContent = `${te.code} · ${getLang() === "en" ? te.name : te.name_de}`;
    sel.appendChild(opt);
  }
}

function fillProceedingSelect(selectId: string, list: ProceedingType[]) {
  const sel = document.getElementById(selectId) as HTMLSelectElement | null;
  if (!sel) return;
  const placeholder = sel.querySelector('option[value=""]');
  sel.innerHTML = "";
  if (placeholder) sel.appendChild(placeholder);
  for (const pt of list) {
    const opt = document.createElement("option");
    opt.value = String(pt.id);
    const name = getLang() === "en" ? pt.name_en : pt.name;
    opt.textContent = name ? `${pt.code} · ${name}` : pt.code;
    sel.appendChild(opt);
  }
}

async function loadRule(): Promise<void> {
  const resp = await fetch(`/admin/api/procedural-events/${encodeURIComponent(ruleId)}`);
  if (!resp.ok) {
    if (resp.status === 404) {
      showFeedback(t("admin.rules.edit.error.not_found") || "Regel nicht gefunden.", true);
    } else {
      showFeedback(t("admin.rules.edit.error.load") || "Konnte Regel nicht laden.", true);
    }
    return;
  }
  rule = await resp.json() as Rule;
  populateForm();
  updateLifecycleUI();
}

async function loadAudit(reset: boolean = true): Promise<void> {
  if (reset) {
    auditEntries = [];
    auditOffset = 0;
  }
  const resp = await fetch(`/admin/api/procedural-events/${encodeURIComponent(ruleId)}/audit?offset=${auditOffset}&limit=${AUDIT_PAGE}`);
  if (!resp.ok) return;
  const body = await resp.json();
  const rows = Array.isArray(body) ? body as AuditEntry[] : [];
  auditEntries.push(...rows);
  auditOffset += rows.length;
  auditHasMore = rows.length === AUDIT_PAGE;
  renderAudit();
}

// --------------------------------------------------------------------
// Form binding.
// --------------------------------------------------------------------
function setInput(id: string, val: unknown) {
  const el = document.getElementById(id) as HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | null;
  if (!el) return;
  if (val == null) {
    el.value = "";
    return;
  }
  el.value = String(val);
}

function setCheckbox(id: string, val: boolean) {
  const el = document.getElementById(id) as HTMLInputElement | null;
  if (!el) return;
  el.checked = !!val;
}

function getInput(id: string): string {
  const el = document.getElementById(id) as HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | null;
  return el ? el.value.trim() : "";
}

function getCheckbox(id: string): boolean {
  const el = document.getElementById(id) as HTMLInputElement | null;
  return el ? el.checked : false;
}

function getOptionalInt(id: string): number | null {
  const v = getInput(id);
  if (!v) return null;
  const n = parseInt(v, 10);
  return Number.isFinite(n) ? n : null;
}

function getOptionalString(id: string): string | null {
  const v = getInput(id);
  return v ? v : null;
}

function populateForm() {
  if (!rule) return;
  const heading = document.getElementById("rules-edit-heading") as HTMLElement;
  const idEl = document.getElementById("rules-edit-id") as HTMLElement;
  const lifecycleEl = document.getElementById("rules-edit-lifecycle") as HTMLElement;
  heading.textContent = (getLang() === "en" ? rule.name_en : rule.name) || rule.name;
  idEl.textContent = rule.id;
  lifecycleEl.className = lifecycleClass(rule.lifecycle_state);
  lifecycleEl.textContent = lifecycleLabel(rule.lifecycle_state);

  setInput("f-name", rule.name);
  setInput("f-name-en", rule.name_en);
  setInput("f-description", rule.description ?? "");
  setInput("f-submission-code", rule.submission_code ?? "");
  setInput("f-rule-code", rule.rule_code ?? "");
  setInput("f-legal-source", rule.legal_source ?? "");
  setInput("f-proceeding", rule.proceeding_type_id ?? "");
  setInput("f-trigger", rule.trigger_event_id ?? "");
  setInput("f-parent", rule.parent_id ?? "");
  setInput("f-concept", rule.concept_id ?? "");
  setInput("f-sequence", rule.sequence_order);
  setInput("f-duration", rule.duration_value);
  setInput("f-duration-unit", rule.duration_unit);
  setInput("f-timing", rule.timing ?? "");
  setInput("f-combine-op", rule.combine_op ?? "");
  setInput("f-alt-duration", rule.alt_duration_value ?? "");
  setInput("f-alt-duration-unit", rule.alt_duration_unit ?? "");
  setInput("f-alt-rule-code", rule.alt_rule_code ?? "");
  setInput("f-anchor-alt", rule.anchor_alt ?? "");
  setInput("f-primary-party", rule.primary_party ?? "");
  setInput("f-event-type", rule.event_type ?? "");
  setInput("f-notes", rule.deadline_notes ?? "");
  setInput("f-notes-en", rule.deadline_notes_en ?? "");
  setInput("f-priority", rule.priority);
  setCheckbox("f-is-court-set", rule.is_court_set);
  setCheckbox("f-is-spawn", rule.is_spawn);
  setInput("f-spawn-label", rule.spawn_label ?? "");
  setInput("f-spawn-proceeding", rule.spawn_proceeding_type_id ?? "");
  toggleSpawnRow();
  setInput("f-condition-expr", rule.condition_expr ? JSON.stringify(rule.condition_expr, null, 2) : "");
}

function toggleSpawnRow() {
  const row = document.getElementById("f-spawn-row") as HTMLElement | null;
  if (!row) return;
  row.style.display = getCheckbox("f-is-spawn") ? "" : "none";
}

function updateLifecycleUI() {
  const draftOnly = (id: string, show: boolean) => {
    const el = document.getElementById(id) as HTMLElement | null;
    if (el) el.style.display = show ? "" : "none";
  };
  if (!rule) return;
  const isDraft = rule.lifecycle_state === "draft";
  const isPublished = rule.lifecycle_state === "published";
  const isArchived = rule.lifecycle_state === "archived";

  draftOnly("action-save-draft", isDraft);
  draftOnly("action-publish", isDraft);
  draftOnly("action-clone", isPublished || isArchived);
  draftOnly("action-archive", isDraft || isPublished);
  draftOnly("action-restore", isArchived);

  // Lock form fields when not editable (i.e. not draft). Published /
  // archived rules show the form read-only so editors can confirm
  // they're about to clone the right row.
  const readOnly = !isDraft;
  document.querySelectorAll<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>(
    "#rules-edit-form input, #rules-edit-form select, #rules-edit-form textarea",
  ).forEach((el) => {
    el.disabled = readOnly;
  });
}

function renderAudit() {
  const list = document.getElementById("rules-edit-audit") as HTMLElement | null;
  const more = document.getElementById("audit-loadmore") as HTMLElement | null;
  if (!list) return;
  if (auditEntries.length === 0) {
    list.innerHTML = `<li class="admin-rules-audit-empty">${esc(t("admin.rules.edit.audit.empty") || "Keine Audit-Eintr&auml;ge.")}</li>`;
  } else {
    list.innerHTML = auditEntries.map((e) => {
      const actor = e.changed_by_display_name || (e.changed_by ? e.changed_by.slice(0, 8) : (t("admin.rules.edit.audit.actor.system") || "System"));
      const actionLabel = tDyn(`admin.rules.edit.audit.action.${e.action}`) || e.action;
      const exported = e.migration_exported
        ? `<span class="admin-rules-audit-badge">${esc(t("admin.rules.edit.audit.exported") || "exported")}</span>`
        : "";
      return `
        <li class="admin-rules-audit-entry admin-rules-audit-action-${esc(e.action)}">
          <div class="admin-rules-audit-head">
            <span class="admin-rules-audit-action">${esc(actionLabel)}</span>
            <span class="admin-rules-audit-time">${esc(fmtDateTime(e.changed_at))}</span>
            ${exported}
          </div>
          <div class="admin-rules-audit-actor">${esc(actor)}</div>
          ${e.reason ? `<div class="admin-rules-audit-reason">${esc(e.reason)}</div>` : ""}
        </li>
      `;
    }).join("");
  }
  if (more) more.style.display = auditHasMore ? "" : "none";
}

// --------------------------------------------------------------------
// Validation helpers.
// --------------------------------------------------------------------
function validateConditionExpr(): { ok: boolean; value: unknown | undefined; msg: string } {
  const raw = getInput("f-condition-expr");
  const msgEl = document.getElementById("f-condition-msg") as HTMLElement | null;
  if (!raw) {
    if (msgEl) {
      msgEl.textContent = "";
      msgEl.className = "admin-rules-hint";
    }
    return { ok: true, value: undefined, msg: "" };
  }
  try {
    const parsed = JSON.parse(raw);
    if (msgEl) {
      msgEl.textContent = "✓ " + (t("admin.rules.edit.field.condition.valid") || "JSON gültig.");
      msgEl.className = "admin-rules-hint admin-rules-hint-ok";
    }
    return { ok: true, value: parsed, msg: "" };
  } catch (err) {
    const m = err instanceof Error ? err.message : String(err);
    if (msgEl) {
      msgEl.textContent = "⚠ " + m;
      msgEl.className = "admin-rules-hint admin-rules-hint-error";
    }
    return { ok: false, value: undefined, msg: m };
  }
}

// --------------------------------------------------------------------
// Action modal (reason + lifecycle handler).
// --------------------------------------------------------------------
type Action = "save-draft" | "publish" | "clone" | "archive" | "restore";

let pendingAction: Action | null = null;

function openActionModal(action: Action) {
  pendingAction = action;
  const modal = document.getElementById("rules-action-modal") as HTMLElement;
  const title = document.getElementById("rules-action-modal-title") as HTMLElement;
  const body = document.getElementById("rules-action-modal-body") as HTMLElement;
  const msg = document.getElementById("rules-action-modal-msg") as HTMLElement;
  const reasonInput = document.getElementById("rules-action-modal-reason") as HTMLTextAreaElement;
  msg.style.display = "none";
  reasonInput.value = "";
  switch (action) {
    case "save-draft":
      title.textContent = t("admin.rules.edit.modal.save_draft.title") || "Draft speichern";
      body.textContent = t("admin.rules.edit.modal.save_draft.body") || "Bitte einen Grund für die Änderung angeben (mind. 10 Zeichen). Wird ins Audit-Log geschrieben.";
      break;
    case "publish":
      title.textContent = t("admin.rules.edit.modal.publish.title") || "Publish";
      body.textContent = t("admin.rules.edit.modal.publish.body") || "Diese Draft-Regel wird live geschaltet. Bestehende publizierte Variante wird archiviert.";
      break;
    case "clone":
      title.textContent = t("admin.rules.edit.modal.clone.title") || "Als Draft klonen";
      body.textContent = t("admin.rules.edit.modal.clone.body") || "Eine neue Draft-Kopie dieser Regel wird angelegt. Sie werden auf die neue Draft-Seite weitergeleitet.";
      break;
    case "archive":
      title.textContent = t("admin.rules.edit.modal.archive.title") || "Archivieren";
      body.textContent = t("admin.rules.edit.modal.archive.body") || "Regel wird archiviert. Calculator nutzt sie nicht mehr.";
      break;
    case "restore":
      title.textContent = t("admin.rules.edit.modal.restore.title") || "Wiederherstellen";
      body.textContent = t("admin.rules.edit.modal.restore.body") || "Regel wird wiederhergestellt (archived → published).";
      break;
  }
  modal.style.display = "flex";
  reasonInput.focus();
}

function closeActionModal() {
  (document.getElementById("rules-action-modal") as HTMLElement).style.display = "none";
  pendingAction = null;
}

async function submitActionModal(ev: Event) {
  ev.preventDefault();
  if (!pendingAction || !rule) return;
  const reasonInput = document.getElementById("rules-action-modal-reason") as HTMLTextAreaElement;
  const msg = document.getElementById("rules-action-modal-msg") as HTMLElement;
  const submit = document.getElementById("rules-action-modal-submit") as HTMLButtonElement;
  const reason = reasonInput.value.trim();
  if (reason.length < 10) {
    msg.textContent = t("admin.rules.modal.reason.too_short") || "Grund muss mindestens 10 Zeichen enthalten.";
    msg.className = "form-msg form-msg-error";
    msg.style.display = "block";
    return;
  }
  submit.disabled = true;
  try {
    if (pendingAction === "save-draft") {
      await doSaveDraft(reason);
    } else if (pendingAction === "publish") {
      await doLifecycle("publish", reason);
    } else if (pendingAction === "clone") {
      await doClone(reason);
    } else if (pendingAction === "archive") {
      await doLifecycle("archive", reason);
    } else if (pendingAction === "restore") {
      await doLifecycle("restore", reason);
    }
  } finally {
    submit.disabled = false;
  }
}

function buildPatchPayload(): Record<string, unknown> {
  const validation = validateConditionExpr();
  if (!validation.ok) throw new Error(validation.msg);
  const payload: Record<string, unknown> = {
    name: getInput("f-name"),
    name_en: getInput("f-name-en"),
    description: getInput("f-description"),
    primary_party: getInput("f-primary-party"),
    event_type: getInput("f-event-type"),
    duration_value: getOptionalInt("f-duration") ?? 0,
    duration_unit: getInput("f-duration-unit"),
    timing: getOptionalString("f-timing"),
    alt_duration_value: getOptionalInt("f-alt-duration"),
    alt_duration_unit: getOptionalString("f-alt-duration-unit"),
    alt_rule_code: getOptionalString("f-alt-rule-code"),
    anchor_alt: getOptionalString("f-anchor-alt"),
    combine_op: getOptionalString("f-combine-op"),
    rule_code: getOptionalString("f-rule-code"),
    legal_source: getOptionalString("f-legal-source"),
    deadline_notes: getInput("f-notes"),
    deadline_notes_en: getInput("f-notes-en"),
    priority: getInput("f-priority"),
    is_court_set: getCheckbox("f-is-court-set"),
    is_spawn: getCheckbox("f-is-spawn"),
    spawn_label: getOptionalString("f-spawn-label"),
    spawn_proceeding_type_id: getOptionalInt("f-spawn-proceeding"),
    trigger_event_id: getOptionalInt("f-trigger"),
    sequence_order: getOptionalInt("f-sequence") ?? 0,
  };
  if (validation.value !== undefined) {
    payload.condition_expr = validation.value;
  }
  return payload;
}

async function doSaveDraft(reason: string) {
  const msg = document.getElementById("rules-action-modal-msg") as HTMLElement;
  let payload: Record<string, unknown>;
  try {
    payload = buildPatchPayload();
  } catch (e) {
    msg.textContent = e instanceof Error ? e.message : String(e);
    msg.className = "form-msg form-msg-error";
    msg.style.display = "block";
    return;
  }
  payload.reason = reason;
  const resp = await fetch(`/admin/api/procedural-events/${encodeURIComponent(ruleId)}`, {
    method: "PATCH",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(payload),
  });
  if (!resp.ok) {
    const body = await resp.json().catch(() => ({ error: resp.statusText }));
    msg.textContent = body.error || (t("admin.rules.edit.action.save_draft.error") || "Speichern fehlgeschlagen.");
    msg.className = "form-msg form-msg-error";
    msg.style.display = "block";
    return;
  }
  rule = await resp.json() as Rule;
  closeActionModal();
  populateForm();
  updateLifecycleUI();
  await loadAudit(true);
  showFeedback(t("admin.rules.edit.action.save_draft.ok") || "Draft gespeichert.", false);
}

async function doLifecycle(op: "publish" | "archive" | "restore", reason: string) {
  const msg = document.getElementById("rules-action-modal-msg") as HTMLElement;
  const resp = await fetch(`/admin/api/procedural-events/${encodeURIComponent(ruleId)}/${op}`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ reason }),
  });
  if (!resp.ok) {
    const body = await resp.json().catch(() => ({ error: resp.statusText }));
    msg.textContent = body.error || (tDyn(`admin.rules.edit.action.${op}.error`) || "Aktion fehlgeschlagen.");
    msg.className = "form-msg form-msg-error";
    msg.style.display = "block";
    return;
  }
  rule = await resp.json() as Rule;
  closeActionModal();
  populateForm();
  updateLifecycleUI();
  await loadAudit(true);
  showFeedback(tDyn(`admin.rules.edit.action.${op}.ok`) || (t("admin.rules.edit.action.ok") || "Erledigt."), false);
}

async function doClone(reason: string) {
  const msg = document.getElementById("rules-action-modal-msg") as HTMLElement;
  const resp = await fetch(`/admin/api/procedural-events/${encodeURIComponent(ruleId)}/clone-as-draft`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ reason }),
  });
  if (!resp.ok) {
    const body = await resp.json().catch(() => ({ error: resp.statusText }));
    msg.textContent = body.error || (t("admin.rules.edit.action.clone.error") || "Klonen fehlgeschlagen.");
    msg.className = "form-msg form-msg-error";
    msg.style.display = "block";
    return;
  }
  const newRule = await resp.json() as Rule;
  window.location.href = `/admin/procedural-events/${encodeURIComponent(newRule.id)}/edit`;
}

// --------------------------------------------------------------------
// Preview.
// --------------------------------------------------------------------
async function runPreview() {
  const out = document.getElementById("preview-result") as HTMLElement;
  if (!rule) return;
  if (rule.lifecycle_state !== "draft") {
    out.innerHTML = `<p class="admin-rules-hint admin-rules-hint-error">${esc(t("admin.rules.edit.preview.only_drafts") || "Preview ist nur für Drafts verfügbar.")}</p>`;
    out.style.display = "";
    return;
  }
  const triggerDate = getInput("preview-trigger-date");
  if (!triggerDate) {
    out.innerHTML = `<p class="admin-rules-hint admin-rules-hint-error">${esc(t("admin.rules.edit.preview.trigger_required") || "Bitte Trigger-Datum angeben.")}</p>`;
    out.style.display = "";
    return;
  }
  const flagsRaw = getInput("preview-flags");
  const qs = new URLSearchParams();
  qs.set("trigger_date", triggerDate);
  if (flagsRaw) qs.set("flags", flagsRaw);
  out.innerHTML = `<p class="admin-rules-loading">${esc(t("admin.rules.edit.preview.running") || "Berechne...")}</p>`;
  out.style.display = "";
  const resp = await fetch(`/admin/api/procedural-events/${encodeURIComponent(ruleId)}/preview?${qs.toString()}`);
  if (!resp.ok) {
    const body = await resp.json().catch(() => ({ error: resp.statusText }));
    out.innerHTML = `<p class="admin-rules-hint admin-rules-hint-error">${esc(body.error || (t("admin.rules.edit.preview.error") || "Preview fehlgeschlagen."))}</p>`;
    return;
  }
  const body = await resp.json();
  renderPreview(body);
}

function renderPreview(resp: unknown) {
  const out = document.getElementById("preview-result") as HTMLElement;
  type Result = { deadlines?: Array<{ name?: string; titleDE?: string; due_date?: string; dueDate?: string; ruleCode?: string; rule_code?: string }>; deadline?: Array<unknown> };
  const r = resp as Result;
  const list = (r && (r.deadlines || r.deadline)) as Array<Record<string, unknown>> | undefined;
  if (!list || list.length === 0) {
    out.innerHTML = `<p class="admin-rules-hint">${esc(t("admin.rules.edit.preview.empty") || "Keine Deadlines.")}</p>`;
    return;
  }
  out.innerHTML = `<ul class="admin-rules-preview-list">${list.map((d) => {
    const name = String(d.name || d.titleDE || d.title || "");
    const date = String(d.due_date || d.dueDate || "");
    const code = String(d.rule_code || d.ruleCode || "");
    return `<li>
      ${code ? `<code>${esc(code)}</code>` : ""}
      <span class="admin-rules-preview-name">${esc(name)}</span>
      <span class="admin-rules-preview-date">${esc(date)}</span>
    </li>`;
  }).join("")}</ul>`;
}

// --------------------------------------------------------------------
// Init.
// --------------------------------------------------------------------
async function init() {
  initI18n();
  initSidebar();
  ruleId = parseRuleIDFromPath();
  if (!ruleId) {
    showFeedback(t("admin.rules.edit.error.bad_id") || "Ungültige Regel-ID in der URL.", true);
    return;
  }

  (document.getElementById("rules-action-modal-close") as HTMLElement).addEventListener("click", closeActionModal);
  (document.getElementById("rules-action-modal-cancel") as HTMLElement).addEventListener("click", closeActionModal);
  (document.getElementById("rules-action-modal-form") as HTMLFormElement).addEventListener("submit", submitActionModal);

  (document.getElementById("action-save-draft") as HTMLElement).addEventListener("click", () => openActionModal("save-draft"));
  (document.getElementById("action-publish") as HTMLElement).addEventListener("click", () => openActionModal("publish"));
  (document.getElementById("action-clone") as HTMLElement).addEventListener("click", () => openActionModal("clone"));
  (document.getElementById("action-archive") as HTMLElement).addEventListener("click", () => openActionModal("archive"));
  (document.getElementById("action-restore") as HTMLElement).addEventListener("click", () => openActionModal("restore"));

  (document.getElementById("f-is-spawn") as HTMLInputElement).addEventListener("change", toggleSpawnRow);
  (document.getElementById("f-condition-expr") as HTMLTextAreaElement).addEventListener("input", () => {
    validateConditionExpr();
  });

  (document.getElementById("preview-run") as HTMLElement).addEventListener("click", () => {
    window.clearTimeout(previewDebounce);
    previewDebounce = window.setTimeout(runPreview, 100);
  });
  (document.getElementById("audit-loadmore") as HTMLElement).addEventListener("click", () => loadAudit(false));

  await Promise.all([loadProceedings(), loadTriggers()]);
  await loadRule();
  await loadAudit(true);

  onLangChange(() => {
    if (rule) {
      populateForm();
      updateLifecycleUI();
    }
    renderAudit();
  });
}

document.addEventListener("DOMContentLoaded", init);
