// Fristenrechner overhaul — shared result view (design §4).
//
// Given a locked trigger event + a trigger date, this module renders
// the result surface: a sticky trigger card on top, then four priority
// groups (mandatory / recommended / optional / conditional) of follow-up
// rules with computed dates, then a write-back footer that calls the
// existing POST /api/projects/{id}/deadlines/bulk.
//
// The two future entry paths (Mode A "Direkt suchen" in S3, Mode B
// wizard in S4) both land here once they've identified a trigger
// procedural_event. S2 mounts the surface under `?overhaul=1` and is
// deep-linkable on its own via `?overhaul=1&event=<code>&trigger_date=…`.

import { escAttr, escHtml } from "./views/verfahrensablauf-core";
import { getLang, t, tDyn } from "./i18n";

// Wire shape from GET /api/tools/fristenrechner/follow-ups. Mirrors
// services.FollowUpsResponse server-side.
export interface FollowUpRule {
  rule_id: string;
  event_code: string;
  title_de: string;
  title_en: string;
  priority: string;
  primary_party?: string;
  duration_value?: number;
  duration_unit?: string;
  timing?: string;
  due_date?: string;
  original_due_date?: string;
  was_adjusted?: boolean;
  is_court_set: boolean;
  is_spawn: boolean;
  is_bilateral: boolean;
  has_condition: boolean;
  rule_code?: string;
  legal_source?: string;
  legal_source_display?: string;
  legal_source_url?: string;
  notes_de?: string;
  notes_en?: string;
  spawn_label?: string;
  spawn_proceeding_code?: string;
  concept_id?: string;
}

export interface FollowUpsResponse {
  trigger: {
    id: string;
    code: string;
    name_de: string;
    name_en: string;
    event_kind?: string;
    proceeding_type: {
      id: number;
      code: string;
      name_de: string;
      name_en: string;
      jurisdiction?: string;
    };
    anchor_rule_id: string;
  };
  trigger_date: string;
  party?: string;
  follow_ups: FollowUpRule[];
}

// Per-rule UI state — checkbox, optional date override.
interface RuleSelection {
  checked: boolean;
  override?: string;
}

// Module-local state. Single result view at a time; the surface
// re-renders in place when the user changes the trigger date or
// re-locks a different event.
let currentResponse: FollowUpsResponse | null = null;
const selections = new Map<string, RuleSelection>();
let currentProjectId: string | null = null;

// Public API ----------------------------------------------------------

// isOverhaulMode reports whether the page is in overhaul mode.
// After Slice S5 (t-paliad-323), overhaul is the default; the legacy
// wizard / row-stack / cascade is only reachable via `?legacy=1` for
// a two-week deprecation window. The `?overhaul=1` deep links from
// S2-S4 still work — they're now redundant with the default but kept
// alive so bookmarks don't 302 / lose state.
export function isOverhaulMode(): boolean {
  return new URLSearchParams(window.location.search).get("legacy") !== "1";
}

// resolveProjectId reads the active Akte from the URL query string.
// Returns null when in kontextfrei mode (no project picked).
function resolveProjectId(): string | null {
  const p = new URLSearchParams(window.location.search).get("project");
  return p && p.length > 0 ? p : null;
}

// MODE_TAB_KEYS — the two entry-mode tabs landed by S3 + S4. S2's deep
// link path bypasses these (jumps straight to the result view via
// ?event=); the tabs appear when no event is locked yet.
export type ModeTab = "search" | "wizard";

// mountModeShell renders the mode-tab pair under the page header and
// hosts whichever mode panel is currently active. Called from the boot
// path when no `?event=` is present. S3 wires Mode A; S4 will add
// Mode B and the actual tab switching.
export async function mountModeShell(activeTab: ModeTab): Promise<void> {
  const root = document.getElementById("fristen-overhaul-root");
  if (!root) return;
  root.hidden = false;
  // Defer to the per-mode module to render into the root. The tab
  // strip itself is a small header above the mode panel — for S3 we
  // render the shell + Mode A in one shot.
  // S4 will replace this with a real tab switcher.
  const tabs = `
    <nav class="fristen-mode-tabs" role="tablist" aria-label="${escAttr(t("deadlines.overhaul.modes.label"))}">
      <button type="button" class="fristen-mode-tab${activeTab === "search" ? " is-active" : ""}" role="tab"
              aria-selected="${activeTab === "search"}" data-tab="search">
        <span class="fristen-mode-tab-icon" aria-hidden="true">&#9889;</span>
        <span class="fristen-mode-tab-label">${escHtml(t("deadlines.overhaul.modes.search"))}</span>
      </button>
      <button type="button" class="fristen-mode-tab${activeTab === "wizard" ? " is-active" : ""}" role="tab"
              aria-selected="${activeTab === "wizard"}" data-tab="wizard">
        <span class="fristen-mode-tab-icon" aria-hidden="true">&#129517;</span>
        <span class="fristen-mode-tab-label">${escHtml(t("deadlines.overhaul.modes.wizard"))}</span>
      </button>
    </nav>
    <div id="fristen-overhaul-mode-host"></div>
  `;
  root.innerHTML = tabs;

  // Wire tab switching. S3 only has Mode A wired; Mode B is a
  // placeholder until S4.
  root.querySelectorAll<HTMLButtonElement>(".fristen-mode-tab").forEach((btn) => {
    btn.addEventListener("click", () => {
      const tab = (btn.dataset.tab || "search") as ModeTab;
      void mountModeShell(tab);
    });
  });

  // Mount the active mode panel into the host. S3 only routes "search";
  // "wizard" renders a placeholder until S4 lands.
  const host = document.getElementById("fristen-overhaul-mode-host");
  if (!host) return;
  if (activeTab === "search") {
    // Lazy import to keep the bundle layered and avoid a circular ref
    // between fristenrechner-result.ts ↔ fristenrechner-mode-a.ts.
    const mod = await import("./fristenrechner-mode-a");
    await mod.mountModeA();
  } else {
    const mod = await import("./fristenrechner-wizard");
    await mod.mountWizard();
  }
}

// MountOptions configures the surface entry. Both entry-mode paths
// (Mode A in S3, Mode B in S4) call mount() with the event reference
// that the user committed.
export interface MountOptions {
  // eventRef is the procedural_event code OR its uuid OR the anchor
  // sequencing_rule id. Resolved server-side; the wire returns the
  // canonical code so the URL bookmark is stable.
  eventRef: string;
  // triggerDate is YYYY-MM-DD. Defaults to today when omitted.
  triggerDate?: string;
  // party is "claimant" | "defendant"; mode A may pass "both" or
  // "court". When omitted, follow-ups are returned without party
  // narrowing.
  party?: string;
  // courtId selects the holiday calendar for the per-rule date
  // adjustment. Optional.
  courtId?: string;
}

// mountResultView fetches /follow-ups and renders the result surface
// into the host container. Re-callable: replaces previous state.
export async function mountResultView(opts: MountOptions): Promise<void> {
  const root = document.getElementById("fristen-overhaul-root");
  if (!root) return;
  root.hidden = false;

  const triggerDate = opts.triggerDate || todayIso();
  currentProjectId = resolveProjectId();

  // Show a quick "loading…" placeholder so the user sees something
  // immediately, even on a cold fetch.
  root.innerHTML = `<div class="fristen-overhaul-loading">${escHtml(t("deadlines.overhaul.loading"))}</div>`;

  const url = new URL("/api/tools/fristenrechner/follow-ups", window.location.origin);
  url.searchParams.set("event", opts.eventRef);
  url.searchParams.set("trigger_date", triggerDate);
  if (opts.party) url.searchParams.set("party", opts.party);
  if (opts.courtId) url.searchParams.set("court_id", opts.courtId);

  let data: FollowUpsResponse;
  try {
    const resp = await fetch(url.toString(), { headers: { Accept: "application/json" } });
    if (!resp.ok) {
      const body = await resp.json().catch(() => ({}) as { error?: string });
      root.innerHTML = `<div class="fristen-overhaul-error">${escHtml(body.error || t("deadlines.overhaul.load_error"))}</div>`;
      return;
    }
    data = (await resp.json()) as FollowUpsResponse;
  } catch (err) {
    root.innerHTML = `<div class="fristen-overhaul-error">${escHtml(t("deadlines.overhaul.load_error"))}</div>`;
    return;
  }

  currentResponse = data;
  selections.clear();
  for (const r of data.follow_ups) {
    selections.set(r.rule_id, { checked: defaultChecked(r) });
  }

  renderSurface();
  // Reflect the canonical event code + trigger date in the URL so the
  // deep-link survives a reload.
  syncUrlState(data.trigger.code, data.trigger_date);
}

// Render --------------------------------------------------------------

function renderSurface(): void {
  const root = document.getElementById("fristen-overhaul-root");
  if (!root || !currentResponse) return;

  const lang = getLang();
  const trig = currentResponse.trigger;
  const triggerName = lang === "en" ? trig.name_en || trig.name_de : trig.name_de;
  const ptName = lang === "en" ? trig.proceeding_type.name_en || trig.proceeding_type.name_de : trig.proceeding_type.name_de;
  const juris = trig.proceeding_type.jurisdiction || "";
  const kindIcon = eventKindIcon(trig.event_kind);

  const triggerCard = `
    <section class="fristen-overhaul-trigger" aria-label="${escAttr(t("deadlines.overhaul.trigger.label"))}">
      <header class="fristen-overhaul-trigger-header">
        <span class="fristen-overhaul-kind-icon" aria-hidden="true">${kindIcon}</span>
        <h2 class="fristen-overhaul-trigger-title">${escHtml(triggerName)}</h2>
      </header>
      <div class="fristen-overhaul-trigger-meta">
        <span class="fristen-overhaul-trigger-code">${escHtml(trig.code)}</span>
        <span class="fristen-overhaul-trigger-pt">${escHtml(ptName)}</span>
        ${juris ? `<span class="fristen-overhaul-trigger-juris">${escHtml(juris)}</span>` : ""}
      </div>
      <div class="fristen-overhaul-trigger-date">
        <label for="fristen-overhaul-trigger-date" class="fristen-overhaul-trigger-date-label">
          ${escHtml(t("deadlines.overhaul.trigger.date"))}
        </label>
        <input type="date" id="fristen-overhaul-trigger-date" class="fristen-overhaul-trigger-date-input"
               value="${escAttr(currentResponse.trigger_date)}" />
      </div>
    </section>
  `;

  const groups = groupFollowUps(currentResponse.follow_ups);
  const groupHtml = renderGroups(groups, lang);

  const nudge = currentProjectId
    ? ""
    : `<div class="fristen-overhaul-nudge">${escHtml(t("deadlines.overhaul.nudge.no_project"))}</div>`;

  const footer = currentProjectId
    ? renderFooter()
    : "";

  root.innerHTML = `
    ${triggerCard}
    ${nudge}
    <section class="fristen-overhaul-groups" aria-label="${escAttr(t("deadlines.overhaul.followups.label"))}">
      ${groupHtml}
    </section>
    ${footer}
    <div class="fristen-overhaul-msg" id="fristen-overhaul-msg" role="status" aria-live="polite"></div>
  `;

  wireSurfaceEvents();
}

export interface GroupedFollowUps {
  mandatory: FollowUpRule[];
  recommended: FollowUpRule[];
  optional: FollowUpRule[];
  conditional: FollowUpRule[];
}

// groupFollowUps splits the wire list into the four visible groups per
// design §4.2. Conditional (sr.condition_expr IS NOT NULL) takes
// precedence over the priority bucket so a "nur wenn CCR" mandatory
// rule renders under Conditional with the gating language visible.
export function groupFollowUps(rows: FollowUpRule[]): GroupedFollowUps {
  const out: GroupedFollowUps = { mandatory: [], recommended: [], optional: [], conditional: [] };
  for (const r of rows) {
    if (r.has_condition) {
      out.conditional.push(r);
      continue;
    }
    switch (r.priority) {
      case "mandatory":
        out.mandatory.push(r);
        break;
      case "recommended":
        out.recommended.push(r);
        break;
      case "optional":
        out.optional.push(r);
        break;
      default:
        // unknown / informational — fold into optional so the row is at
        // least visible. Future Phase 2 'informational' tier gets a
        // dedicated bucket once seeded.
        out.optional.push(r);
    }
  }
  return out;
}

function renderGroups(groups: GroupedFollowUps, lang: "de" | "en"): string {
  const blocks: string[] = [];
  if (groups.mandatory.length > 0) {
    blocks.push(renderGroup("mandatory", t("deadlines.overhaul.group.mandatory"), groups.mandatory, lang));
  }
  if (groups.recommended.length > 0) {
    blocks.push(renderGroup("recommended", t("deadlines.overhaul.group.recommended"), groups.recommended, lang));
  }
  if (groups.optional.length > 0) {
    blocks.push(renderGroup("optional", t("deadlines.overhaul.group.optional"), groups.optional, lang));
  }
  if (groups.conditional.length > 0) {
    blocks.push(renderGroup("conditional", t("deadlines.overhaul.group.conditional"), groups.conditional, lang));
  }
  if (blocks.length === 0) {
    return `<div class="fristen-overhaul-empty">${escHtml(t("deadlines.overhaul.empty"))}</div>`;
  }
  return blocks.join("");
}

function renderGroup(slug: string, label: string, rows: FollowUpRule[], lang: "de" | "en"): string {
  const items = rows.map((r) => renderRule(r, lang)).join("");
  return `
    <div class="fristen-overhaul-group fristen-overhaul-group--${escAttr(slug)}">
      <h3 class="fristen-overhaul-group-title">${escHtml(label)}</h3>
      <ul class="fristen-overhaul-rule-list">
        ${items}
      </ul>
    </div>
  `;
}

function renderRule(r: FollowUpRule, lang: "de" | "en"): string {
  const title = lang === "en" ? r.title_en || r.title_de : r.title_de;
  const notes = lang === "en" ? r.notes_en || r.notes_de : r.notes_de;
  const sel = selections.get(r.rule_id);
  const checked = sel ? sel.checked : defaultChecked(r);
  const dateOverride = sel?.override;
  const computedDate = r.due_date || "";
  const effectiveDate = dateOverride || computedDate;
  const disabled = r.is_court_set || (r.is_spawn && !r.due_date);

  // Duration phrase: "3 Monate" / "14 Tage" — language-aware.
  const durationPhrase = formatDurationPhrase(r, lang);
  const dateCell = r.is_court_set
    ? `<span class="fristen-overhaul-rule-court-set">${escHtml(t("deadlines.court.set"))}</span>`
    : effectiveDate
      ? `<span class="fristen-overhaul-rule-date" data-rule-id="${escAttr(r.rule_id)}">${escHtml(formatDateForLang(effectiveDate, lang))}</span>`
      : `<span class="fristen-overhaul-rule-date fristen-overhaul-rule-date--unknown">&mdash;</span>`;

  const partyBadge = r.primary_party
    ? `<span class="fristen-overhaul-rule-party fristen-overhaul-rule-party--${escAttr(r.primary_party)}">${escHtml(t(`deadlines.party.${r.primary_party}` as never))}</span>`
    : "";

  const sourceBadge = r.legal_source_display
    ? r.legal_source_url
      ? `<a class="fristen-overhaul-rule-source" href="${escAttr(r.legal_source_url)}" target="_blank" rel="noreferrer">${escHtml(r.legal_source_display)}</a>`
      : `<span class="fristen-overhaul-rule-source">${escHtml(r.legal_source_display)}</span>`
    : r.rule_code
      ? `<span class="fristen-overhaul-rule-source">${escHtml(r.rule_code)}</span>`
      : "";

  const spawnBadge = r.is_spawn
    ? `<span class="fristen-overhaul-rule-spawn" title="${escAttr(t("deadlines.overhaul.spawn.tooltip"))}">${escHtml(t("deadlines.overhaul.spawn.badge"))}${r.spawn_proceeding_code ? ` · ${escHtml(r.spawn_proceeding_code)}` : ""}</span>`
    : "";

  const condBadge = r.has_condition
    ? `<span class="fristen-overhaul-rule-cond">${escHtml(t("deadlines.overhaul.condition.badge"))}</span>`
    : "";

  const notesHtml = notes
    ? `<details class="fristen-overhaul-rule-notes"><summary>${escHtml(t("deadlines.overhaul.notes.summary"))}</summary><p>${escHtml(notes)}</p></details>`
    : "";

  const editBtn = r.is_court_set || r.is_spawn || !computedDate
    ? ""
    : `<button type="button" class="fristen-overhaul-rule-edit-date" data-rule-id="${escAttr(r.rule_id)}" title="${escAttr(t("deadlines.overhaul.edit_date.title"))}" aria-label="${escAttr(t("deadlines.overhaul.edit_date.title"))}">${escHtml(t("deadlines.overhaul.edit_date.label"))}</button>`;

  return `
    <li class="fristen-overhaul-rule${disabled ? " is-disabled" : ""}" data-rule-id="${escAttr(r.rule_id)}">
      <label class="fristen-overhaul-rule-check">
        <input type="checkbox" data-rule-id="${escAttr(r.rule_id)}"
               ${checked ? "checked" : ""} ${disabled ? "disabled" : ""} />
        <span class="visually-hidden">${escHtml(t("deadlines.overhaul.select_rule"))}</span>
      </label>
      <div class="fristen-overhaul-rule-body">
        <div class="fristen-overhaul-rule-title-row">
          <span class="fristen-overhaul-rule-title">${escHtml(title)}</span>
          ${spawnBadge}
          ${condBadge}
        </div>
        <div class="fristen-overhaul-rule-meta-row">
          ${durationPhrase ? `<span class="fristen-overhaul-rule-duration">${escHtml(durationPhrase)}</span>` : ""}
          ${partyBadge}
          ${sourceBadge}
        </div>
        ${notesHtml}
      </div>
      <div class="fristen-overhaul-rule-date-cell">
        ${dateCell}
        ${editBtn}
      </div>
    </li>
  `;
}

function renderFooter(): string {
  const selectedCount = countSelected();
  return `
    <footer class="fristen-overhaul-footer" id="fristen-overhaul-footer">
      <span class="fristen-overhaul-footer-count" id="fristen-overhaul-footer-count">
        ${escHtml(tDyn("deadlines.overhaul.footer.count").replace("{n}", String(selectedCount)))}
      </span>
      <button type="button" class="fristen-overhaul-footer-cta btn-primary btn-cta-lime"
              id="fristen-overhaul-write-back"
              ${selectedCount === 0 ? "disabled" : ""}>
        ${escHtml(t("deadlines.overhaul.footer.cta"))}
      </button>
    </footer>
  `;
}

// Event wiring --------------------------------------------------------

function wireSurfaceEvents(): void {
  // Trigger-date change → re-fetch with new date.
  const dateInput = document.getElementById("fristen-overhaul-trigger-date") as HTMLInputElement | null;
  if (dateInput && currentResponse) {
    dateInput.addEventListener("change", () => {
      if (!currentResponse) return;
      const newDate = dateInput.value;
      if (!newDate) return;
      void mountResultView({
        eventRef: currentResponse.trigger.code,
        triggerDate: newDate,
        party: currentResponse.party,
      });
    });
  }

  // Checkbox toggles → update selections + footer count.
  const root = document.getElementById("fristen-overhaul-root");
  if (root) {
    root.querySelectorAll<HTMLInputElement>(".fristen-overhaul-rule-check input[type=checkbox]").forEach((cb) => {
      cb.addEventListener("change", () => {
        const id = cb.dataset.ruleId || "";
        const sel = selections.get(id) ?? { checked: cb.checked };
        sel.checked = cb.checked;
        selections.set(id, sel);
        refreshFooterCount();
      });
    });

    // Per-rule date override.
    root.querySelectorAll<HTMLButtonElement>(".fristen-overhaul-rule-edit-date").forEach((btn) => {
      btn.addEventListener("click", () => editRuleDate(btn));
    });
  }

  // Write-back CTA.
  const cta = document.getElementById("fristen-overhaul-write-back");
  if (cta) cta.addEventListener("click", () => void submitWriteBack());
}

function editRuleDate(btn: HTMLButtonElement): void {
  const ruleId = btn.dataset.ruleId || "";
  const rule = currentResponse?.follow_ups.find((r) => r.rule_id === ruleId);
  if (!rule) return;
  const sel = selections.get(ruleId) ?? { checked: defaultChecked(rule) };
  const current = sel.override || rule.due_date || todayIso();

  const dateCell = btn.parentElement;
  if (!dateCell) return;
  const dateSpan = dateCell.querySelector<HTMLSpanElement>(".fristen-overhaul-rule-date");
  if (!dateSpan) return;

  const input = document.createElement("input");
  input.type = "date";
  input.value = current;
  input.className = "fristen-overhaul-rule-date-input";
  dateSpan.replaceWith(input);
  btn.disabled = true;
  input.focus();

  const commit = () => {
    const newDate = input.value;
    if (newDate && newDate !== current) {
      sel.override = newDate;
      selections.set(ruleId, sel);
    }
    renderSurface();
  };
  input.addEventListener("blur", commit, { once: true });
  input.addEventListener("keydown", (e) => {
    if ((e as KeyboardEvent).key === "Enter") {
      e.preventDefault();
      input.blur();
    } else if ((e as KeyboardEvent).key === "Escape") {
      renderSurface();
    }
  });
}

function refreshFooterCount(): void {
  const countEl = document.getElementById("fristen-overhaul-footer-count");
  const cta = document.getElementById("fristen-overhaul-write-back") as HTMLButtonElement | null;
  const n = countSelected();
  if (countEl) {
    countEl.textContent = tDyn("deadlines.overhaul.footer.count").replace("{n}", String(n));
  }
  if (cta) cta.disabled = n === 0;
}

function countSelected(): number {
  let n = 0;
  if (!currentResponse) return 0;
  for (const r of currentResponse.follow_ups) {
    if (r.is_court_set) continue;
    const sel = selections.get(r.rule_id);
    if (sel?.checked) n++;
  }
  return n;
}

// Write-back ----------------------------------------------------------

async function submitWriteBack(): Promise<void> {
  if (!currentResponse) return;
  if (!currentProjectId) return;
  const msg = document.getElementById("fristen-overhaul-msg");
  const cta = document.getElementById("fristen-overhaul-write-back") as HTMLButtonElement | null;
  const lang = getLang();

  const deadlines: Array<Record<string, unknown>> = [];
  for (const r of currentResponse.follow_ups) {
    const sel = selections.get(r.rule_id);
    if (!sel?.checked) continue;
    if (r.is_court_set) continue;
    const dueDate = sel.override || r.due_date;
    if (!dueDate) continue;
    const title = lang === "en" ? r.title_en || r.title_de : r.title_de;
    const notes = lang === "en" ? r.notes_en || r.notes_de : r.notes_de;
    deadlines.push({
      title,
      rule_code: r.rule_code || undefined,
      due_date: dueDate,
      original_due_date: r.original_due_date || r.due_date || undefined,
      source: "fristenrechner",
      rule_id: r.rule_id,
      notes: notes || undefined,
      audit_reason: auditReason(),
    });
  }

  if (deadlines.length === 0 || !msg || !cta) return;
  cta.disabled = true;
  msg.textContent = "";
  msg.className = "fristen-overhaul-msg";

  try {
    const resp = await fetch(`/api/projects/${encodeURIComponent(currentProjectId)}/deadlines/bulk`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ deadlines }),
    });
    if (!resp.ok) {
      const body = await resp.json().catch(() => ({}) as { error?: string });
      msg.textContent = body.error || t("deadlines.save.error");
      msg.className = "fristen-overhaul-msg form-msg-error";
      cta.disabled = false;
      return;
    }
    msg.innerHTML = `${escHtml(t("deadlines.save.success"))} <a href="/deadlines?project_id=${encodeURIComponent(currentProjectId)}">${escHtml(t("deadlines.save.success.link"))}</a>`;
    msg.className = "fristen-overhaul-msg form-msg-ok";
    setTimeout(() => {
      if (cta) cta.disabled = false;
    }, 1500);
  } catch {
    msg.textContent = t("deadlines.save.error");
    msg.className = "fristen-overhaul-msg form-msg-error";
    cta.disabled = false;
  }
}

// audit reason per design §11.Q12: "Aus Fristenrechner — Trigger: {name} ({date})".
function auditReason(): string {
  if (!currentResponse) return "";
  const name = currentResponse.trigger.name_de;
  const date = currentResponse.trigger_date;
  return `Aus Fristenrechner — Trigger: ${name} (${date})`;
}

// Helpers -------------------------------------------------------------

export function defaultChecked(r: FollowUpRule): boolean {
  if (r.is_court_set) return false;
  if (r.is_spawn) return r.priority === "mandatory";
  if (r.has_condition) return false;
  return r.priority === "mandatory" || r.priority === "recommended";
}

function formatDurationPhrase(r: FollowUpRule, lang: "de" | "en"): string {
  if (!r.duration_value || !r.duration_unit) return "";
  const unitDE: Record<string, string> = {
    days: "Tage",
    months: "Monate",
    weeks: "Wochen",
    years: "Jahre",
  };
  const unitEN: Record<string, string> = {
    days: "days",
    months: "months",
    weeks: "weeks",
    years: "years",
  };
  const u = (lang === "en" ? unitEN : unitDE)[r.duration_unit] || r.duration_unit;
  return `${r.duration_value} ${u}`;
}

function formatDateForLang(iso: string, lang: "de" | "en"): string {
  // YYYY-MM-DD → DE: DD.MM.YYYY / EN: DD MMM YYYY (short).
  if (!iso || iso.length < 10) return iso;
  const [y, m, d] = iso.split("-");
  if (!y || !m || !d) return iso;
  if (lang === "en") {
    const months = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
    const idx = parseInt(m, 10) - 1;
    const mn = idx >= 0 && idx < months.length ? months[idx] : m;
    return `${parseInt(d, 10)} ${mn} ${y}`;
  }
  return `${d}.${m}.${y}`;
}

function eventKindIcon(kind?: string): string {
  switch (kind) {
    case "filing":   return "&#128229;"; // inbox/letter
    case "hearing":  return "&#127963;&#65039;"; // courthouse
    case "decision": return "&#9878;&#65039;"; // scales
    case "order":    return "&#128220;"; // page
    default:         return "&#128197;"; // calendar
  }
}

function todayIso(): string {
  return new Date().toISOString().slice(0, 10);
}

function syncUrlState(eventCode: string, triggerDate: string): void {
  const url = new URL(window.location.href);
  url.searchParams.set("overhaul", "1");
  url.searchParams.set("event", eventCode);
  url.searchParams.set("trigger_date", triggerDate);
  history.replaceState(null, "", url.pathname + url.search + url.hash);
}
