// Fristenrechner overhaul Mode A — "Direkt suchen" (design §3.1).
//
// Power-user surface: a filter strip (Forum / Verfahren / Was passierte /
// Partei) over a free-text search box over a result list of
// procedural_events. Clicking a row locks the event as the trigger and
// transitions to the shared result view (S2). Inbox channel chip lives
// as a secondary "Erweitert" toggle per design §3.3 — picking CMS / beA
// / Postal auto-sets the Forum chip.
//
// Section-split visual hierarchy per m §11.Q3: filter strip on top
// ("Filter (eingrenzen)") with the four chip groups, search box and
// result list below — clicking a result row IS the qualifier action.

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

// Wire shape from GET /api/tools/fristenrechner/search?kind=events.
// Mirrors services.EventSearchResponse server-side.
interface EventSearchHit {
  id: string;
  code: string;
  name_de: string;
  name_en: string;
  event_kind?: string;
  description?: string;
  primary_party?: string;
  proceeding_type: {
    id: number;
    code: string;
    name_de: string;
    name_en: string;
    jurisdiction?: string;
  };
  anchor_rule_id: string;
  follow_up_count: number;
  concept_id?: string;
  score: number;
}

interface EventSearchResponse {
  query: string;
  events: EventSearchHit[];
  total: number;
}

interface ProceedingChip {
  code: string;
  name: string;
  nameEN: string;
  group: string;
}

// Module-local state — single Mode A surface at a time.
interface ModeAState {
  jurisdiction: string; // "" = Alle
  proc: string;         // proceeding_types.code, "" = Alle
  eventKind: string;    // "" = Alle
  party: string;        // "" = Alle (Mode A's filter semantics, §11.Q8)
  q: string;            // free-text query
  inbox: string;        // CMS / bea / postal / "" — secondary, design §3.3
  inboxOpen: boolean;
}

const state: ModeAState = {
  jurisdiction: "",
  proc: "",
  eventKind: "",
  party: "",
  q: "",
  inbox: "",
  inboxOpen: false,
};

// Debounce token for search input — avoid hammering the server on
// every keystroke.
let searchSeq = 0;
let searchTimer: ReturnType<typeof setTimeout> | null = null;

// Chip data — static. Forum and event-kind are closed-set per design;
// party is closed-set with "Beide" option (Mode A is filter mode,
// §11.Q8). Inbox secondary chip set per §3.3.
const FORUMS = ["UPC", "DE", "EPA", "DPMA"] as const;
const EVENT_KINDS = ["filing", "hearing", "decision", "order"] as const;
const PARTIES = ["claimant", "defendant", "both"] as const;

// Forum auto-derivation from inbox chip per §3.3: CMS → UPC, beA → DE,
// Postal → no narrowing (postal arrives at every jurisdiction).
const INBOX_TO_FORUM: Record<string, string> = {
  cms: "UPC",
  bea: "DE",
  postal: "",
};

// MODE_A_HOST_ID is the DOM id of the container Mode A renders into.
// The mode shell (fristenrechner-result.mountModeShell) creates this
// element under the overhaul root and hands it to Mode A; Mode A
// otherwise has no opinion about its placement on the page.
const MODE_A_HOST_ID = "fristen-overhaul-mode-host";

export function isModeASurfaceMounted(): boolean {
  return !!document.getElementById("fristen-mode-a-root");
}

// mountModeA renders the Mode A surface into the overhaul root. Reads
// initial state from URL params so deep links restore the previous
// filter / search state.
export async function mountModeA(): Promise<void> {
  const root = document.getElementById(MODE_A_HOST_ID);
  if (!root) return;

  // Hydrate state from URL.
  const params = new URLSearchParams(window.location.search);
  state.jurisdiction = (params.get("forum") || "").toUpperCase();
  state.proc = params.get("pt") || "";
  state.eventKind = params.get("kind") || "";
  state.party = params.get("party") || "";
  state.q = params.get("q") || "";

  renderShell();
  await loadProceedingChips();
  void runSearch();
}

// renderShell builds the Mode A markup. Idempotent re-call from the
// boot path; row-level rewrites use renderResults / renderFilterStrip
// for finer-grained updates.
function renderShell(): void {
  const root = document.getElementById("fristen-overhaul-root");
  if (!root) return;
  root.innerHTML = `
    <div id="fristen-mode-a-root" class="fristen-mode-a-root">
      <section class="fristen-mode-a-filters" aria-label="${escAttr(t("deadlines.overhaul.modea.filters.label"))}">
        <header class="fristen-mode-a-filters-header">
          <span class="fristen-mode-a-filters-title">${escHtml(t("deadlines.overhaul.modea.filters.heading"))}</span>
        </header>
        <div class="fristen-mode-a-chip-row" data-axis="forum">
          <span class="fristen-mode-a-axis-label">${escHtml(t("deadlines.overhaul.modea.axis.forum"))}</span>
          <div class="fristen-mode-a-chips" id="fristen-mode-a-chips-forum"></div>
        </div>
        <div class="fristen-mode-a-chip-row" data-axis="proc">
          <span class="fristen-mode-a-axis-label">${escHtml(t("deadlines.overhaul.modea.axis.proc"))}</span>
          <div class="fristen-mode-a-chips" id="fristen-mode-a-chips-proc"></div>
        </div>
        <div class="fristen-mode-a-chip-row" data-axis="kind">
          <span class="fristen-mode-a-axis-label">${escHtml(t("deadlines.overhaul.modea.axis.kind"))}</span>
          <div class="fristen-mode-a-chips" id="fristen-mode-a-chips-kind"></div>
        </div>
        <div class="fristen-mode-a-chip-row" data-axis="party">
          <span class="fristen-mode-a-axis-label">${escHtml(t("deadlines.overhaul.modea.axis.party"))}</span>
          <div class="fristen-mode-a-chips" id="fristen-mode-a-chips-party"></div>
        </div>
        <details class="fristen-mode-a-inbox" ${state.inboxOpen ? "open" : ""}>
          <summary class="fristen-mode-a-inbox-summary">${escHtml(t("deadlines.overhaul.modea.inbox.summary"))}</summary>
          <div class="fristen-mode-a-chip-row" data-axis="inbox">
            <span class="fristen-mode-a-axis-label">${escHtml(t("deadlines.overhaul.modea.axis.inbox"))}</span>
            <div class="fristen-mode-a-chips" id="fristen-mode-a-chips-inbox"></div>
          </div>
        </details>
      </section>

      <section class="fristen-mode-a-search" aria-label="${escAttr(t("deadlines.overhaul.modea.search.label"))}">
        <div class="fristen-mode-a-search-input-wrap">
          <svg class="fristen-mode-a-search-icon" width="18" height="18" viewBox="0 0 24 24" fill="none"
               stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
            <circle cx="11" cy="11" r="7"></circle>
            <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
          </svg>
          <input type="search" id="fristen-mode-a-search-input"
                 class="fristen-mode-a-search-input"
                 autocomplete="off" spellcheck="false"
                 data-i18n-placeholder="deadlines.overhaul.modea.search.placeholder"
                 placeholder="Klageerhebung, Hinweisbeschluss, oral hearing…"
                 value="${escAttr(state.q)}" />
        </div>
      </section>

      <section class="fristen-mode-a-results" aria-label="${escAttr(t("deadlines.overhaul.modea.results.label"))}">
        <header class="fristen-mode-a-results-header">
          <span class="fristen-mode-a-results-title">${escHtml(t("deadlines.overhaul.modea.results.heading"))}</span>
          <span class="fristen-mode-a-results-count" id="fristen-mode-a-results-count"></span>
        </header>
        <ul class="fristen-mode-a-result-list" id="fristen-mode-a-result-list" role="listbox" aria-live="polite"></ul>
      </section>
    </div>
  `;

  renderForumChips();
  renderKindChips();
  renderPartyChips();
  renderInboxChips();
  // Proceeding chips render later, after fetch.

  // Wire search input.
  const input = document.getElementById("fristen-mode-a-search-input") as HTMLInputElement | null;
  if (input) {
    input.addEventListener("input", () => {
      state.q = input.value;
      scheduleSearch(180);
    });
    input.addEventListener("keydown", (e) => {
      if ((e as KeyboardEvent).key === "Enter") {
        e.preventDefault();
        scheduleSearch(0);
      }
    });
  }
}

// Filter-strip chip renderers ----------------------------------------

function renderForumChips(): void {
  const host = document.getElementById("fristen-mode-a-chips-forum");
  if (!host) return;
  const chips = [
    chipHtml("forum", "", t("deadlines.overhaul.modea.chip.all"), state.jurisdiction === ""),
    ...FORUMS.map((j) => chipHtml("forum", j, j, state.jurisdiction === j)),
  ];
  host.innerHTML = chips.join("");
  host.querySelectorAll<HTMLButtonElement>(".fristen-mode-a-chip").forEach((btn) => {
    btn.addEventListener("click", () => {
      const v = btn.dataset.value || "";
      state.jurisdiction = v;
      // Forum change invalidates the proc pick if it falls outside.
      state.proc = "";
      syncUrl();
      renderForumChips();
      void loadProceedingChips();
      scheduleSearch(0);
    });
  });
}

function renderKindChips(): void {
  const host = document.getElementById("fristen-mode-a-chips-kind");
  if (!host) return;
  const chips = [
    chipHtml("kind", "", t("deadlines.overhaul.modea.chip.all"), state.eventKind === ""),
    ...EVENT_KINDS.map((k) => chipHtml("kind", k, t(`deadlines.overhaul.kind.${k}` as never), state.eventKind === k, eventKindIconForChip(k))),
  ];
  host.innerHTML = chips.join("");
  host.querySelectorAll<HTMLButtonElement>(".fristen-mode-a-chip").forEach((btn) => {
    btn.addEventListener("click", () => {
      state.eventKind = btn.dataset.value || "";
      syncUrl();
      renderKindChips();
      scheduleSearch(0);
    });
  });
}

function renderPartyChips(): void {
  const host = document.getElementById("fristen-mode-a-chips-party");
  if (!host) return;
  const chips = [
    chipHtml("party", "", t("deadlines.overhaul.modea.chip.all"), state.party === ""),
    ...PARTIES.map((p) => chipHtml("party", p, t(`deadlines.party.${p}` as never), state.party === p)),
  ];
  host.innerHTML = chips.join("");
  host.querySelectorAll<HTMLButtonElement>(".fristen-mode-a-chip").forEach((btn) => {
    btn.addEventListener("click", () => {
      state.party = btn.dataset.value || "";
      syncUrl();
      renderPartyChips();
      scheduleSearch(0);
    });
  });
}

function renderInboxChips(): void {
  const host = document.getElementById("fristen-mode-a-chips-inbox");
  if (!host) return;
  const opts = [
    { v: "", label: t("deadlines.overhaul.modea.chip.all") },
    { v: "cms", label: "CMS" },
    { v: "bea", label: "beA" },
    { v: "postal", label: t("deadlines.overhaul.modea.inbox.postal") },
  ];
  host.innerHTML = opts.map((o) => chipHtml("inbox", o.v, o.label, state.inbox === o.v)).join("");
  host.querySelectorAll<HTMLButtonElement>(".fristen-mode-a-chip").forEach((btn) => {
    btn.addEventListener("click", () => {
      const v = btn.dataset.value || "";
      state.inbox = v;
      // Auto-nudge forum from inbox per design §3.3.
      const nudge = INBOX_TO_FORUM[v];
      if (nudge !== undefined && nudge !== "") {
        state.jurisdiction = nudge;
        state.proc = "";
        renderForumChips();
        void loadProceedingChips();
      }
      renderInboxChips();
      scheduleSearch(0);
    });
  });
}

// Proceeding chips — dynamic fetch.

let lastProcFetchKey = "";

async function loadProceedingChips(): Promise<void> {
  const host = document.getElementById("fristen-mode-a-chips-proc");
  if (!host) return;
  const key = `j=${state.jurisdiction}`;
  if (lastProcFetchKey === key) return; // cached for current jurisdiction
  lastProcFetchKey = key;
  host.innerHTML = `<span class="fristen-mode-a-chip-loading">${escHtml(t("deadlines.overhaul.modea.loading"))}</span>`;

  const url = new URL("/api/tools/proceeding-types", window.location.origin);
  url.searchParams.set("kind", "proceeding");
  if (state.jurisdiction) url.searchParams.set("jurisdiction", state.jurisdiction);

  let chips: ProceedingChip[] = [];
  try {
    const resp = await fetch(url.toString(), { headers: { Accept: "application/json" } });
    if (resp.ok) {
      const data = (await resp.json()) as ProceedingChip[] | null;
      chips = data || [];
    }
  } catch {
    // Soft-fail: chip strip just hides; search still runs without
    // proceeding narrowing.
  }

  renderProceedingChips(chips);
}

function renderProceedingChips(chips: ProceedingChip[]): void {
  const host = document.getElementById("fristen-mode-a-chips-proc");
  if (!host) return;
  const lang = getLang();
  if (chips.length === 0) {
    host.innerHTML = `<span class="fristen-mode-a-chip-empty">${escHtml(t("deadlines.overhaul.modea.no_proceedings"))}</span>`;
    return;
  }
  const rendered = [
    chipHtml("proc", "", t("deadlines.overhaul.modea.chip.all"), state.proc === ""),
    ...chips.map((c) => {
      const label = lang === "en" ? c.nameEN || c.name : c.name;
      return chipHtml("proc", c.code, label, state.proc === c.code, undefined, c.code);
    }),
  ];
  host.innerHTML = rendered.join("");
  host.querySelectorAll<HTMLButtonElement>(".fristen-mode-a-chip").forEach((btn) => {
    btn.addEventListener("click", () => {
      state.proc = btn.dataset.value || "";
      syncUrl();
      renderProceedingChips(chips);
      scheduleSearch(0);
    });
  });
}

// Search ------------------------------------------------------------

function scheduleSearch(delayMs: number): void {
  if (searchTimer !== null) clearTimeout(searchTimer);
  searchTimer = setTimeout(() => {
    searchTimer = null;
    void runSearch();
  }, delayMs);
}

async function runSearch(): Promise<void> {
  searchSeq++;
  const mySeq = searchSeq;

  const list = document.getElementById("fristen-mode-a-result-list");
  const count = document.getElementById("fristen-mode-a-results-count");
  if (!list || !count) return;

  list.innerHTML = `<li class="fristen-mode-a-result-loading">${escHtml(t("deadlines.overhaul.modea.loading"))}</li>`;
  count.textContent = "";

  const url = new URL("/api/tools/fristenrechner/search", window.location.origin);
  url.searchParams.set("kind", "events");
  if (state.q) url.searchParams.set("q", state.q);
  if (state.jurisdiction) url.searchParams.set("jurisdiction", state.jurisdiction);
  if (state.proc) url.searchParams.set("proc", state.proc);
  if (state.eventKind) url.searchParams.set("event_kind", state.eventKind);
  if (state.party) url.searchParams.set("party", state.party);

  let data: EventSearchResponse;
  try {
    const resp = await fetch(url.toString(), { headers: { Accept: "application/json" } });
    if (!resp.ok) {
      if (mySeq === searchSeq) {
        list.innerHTML = `<li class="fristen-mode-a-result-error">${escHtml(t("deadlines.overhaul.modea.search_error"))}</li>`;
      }
      return;
    }
    data = (await resp.json()) as EventSearchResponse;
  } catch {
    if (mySeq === searchSeq) {
      list.innerHTML = `<li class="fristen-mode-a-result-error">${escHtml(t("deadlines.overhaul.modea.search_error"))}</li>`;
    }
    return;
  }

  if (mySeq !== searchSeq) return; // stale response

  renderResults(data);
}

function renderResults(data: EventSearchResponse): void {
  const list = document.getElementById("fristen-mode-a-result-list");
  const count = document.getElementById("fristen-mode-a-results-count");
  if (!list || !count) return;
  count.textContent = tDyn("deadlines.overhaul.modea.results.count").replace("{n}", String(data.total));

  if (data.events.length === 0) {
    list.innerHTML = `<li class="fristen-mode-a-result-empty">${escHtml(t("deadlines.overhaul.modea.no_results"))}</li>`;
    return;
  }

  const lang = getLang();
  list.innerHTML = data.events.map((e) => {
    const name = lang === "en" ? e.name_en || e.name_de : e.name_de;
    const pt = e.proceeding_type;
    const ptName = lang === "en" ? pt.name_en || pt.name_de : pt.name_de;
    const icon = eventKindIconForChip(e.event_kind);
    const followUps = tDyn("deadlines.overhaul.modea.row.followups").replace("{n}", String(e.follow_up_count));
    const juris = pt.jurisdiction || "";
    return `
      <li class="fristen-mode-a-result" data-event-code="${escAttr(e.code)}" tabindex="0" role="option">
        <span class="fristen-mode-a-result-icon" aria-hidden="true">${icon}</span>
        <div class="fristen-mode-a-result-body">
          <div class="fristen-mode-a-result-title">${escHtml(name)}</div>
          <div class="fristen-mode-a-result-meta">
            <span class="fristen-mode-a-result-pt">${escHtml(pt.code)}</span>
            <span class="fristen-mode-a-result-pt-name">${escHtml(ptName)}</span>
            ${juris ? `<span class="fristen-mode-a-result-juris">${escHtml(juris)}</span>` : ""}
            <span class="fristen-mode-a-result-followups">${escHtml(followUps)}</span>
          </div>
        </div>
        <span class="fristen-mode-a-result-cta" aria-hidden="true">&rarr;</span>
      </li>
    `;
  }).join("");

  list.querySelectorAll<HTMLLIElement>(".fristen-mode-a-result").forEach((li) => {
    li.addEventListener("click", () => commitEvent(li.dataset.eventCode || ""));
    li.addEventListener("keydown", (e) => {
      const k = (e as KeyboardEvent).key;
      if (k === "Enter" || k === " ") {
        e.preventDefault();
        commitEvent(li.dataset.eventCode || "");
      }
    });
  });
}

// Commit — user picked a result; lock the event as trigger and
// transition to the §4 result view (S2).
function commitEvent(code: string): void {
  if (!code) return;
  // Reflect in URL before re-mounting so the result view's deep link
  // is consistent.
  const url = new URL(window.location.href);
  url.searchParams.set("overhaul", "1");
  url.searchParams.set("event", code);
  // Preserve project / forum / kind filters so a back-navigation
  // brings Mode A back with the same filters.
  history.pushState(null, "", url.pathname + url.search + url.hash);
  void mountResultView({
    eventRef: code,
    party: state.party || undefined,
  });
}

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

function chipHtml(axis: string, value: string, label: string, active: boolean, icon?: string, title?: string): string {
  const cls = `fristen-mode-a-chip${active ? " is-active" : ""}`;
  const t = title ? ` title="${escAttr(title)}"` : "";
  const i = icon ? `<span class="fristen-mode-a-chip-icon" aria-hidden="true">${icon}</span>` : "";
  return `<button type="button" class="${cls}" data-axis="${escAttr(axis)}" data-value="${escAttr(value)}"${t}>${i}<span class="fristen-mode-a-chip-label">${escHtml(label)}</span></button>`;
}

function eventKindIconForChip(kind?: string): string {
  switch (kind) {
    case "filing":   return "&#128229;";
    case "hearing":  return "&#127963;&#65039;";
    case "decision": return "&#9878;&#65039;";
    case "order":    return "&#128220;";
    default:         return "&#128269;";
  }
}

// syncUrl writes the active filter set into the URL so the deep link
// restores Mode A in the same state.
function syncUrl(): void {
  const url = new URL(window.location.href);
  url.searchParams.set("overhaul", "1");
  setOrClear(url, "forum", state.jurisdiction);
  setOrClear(url, "pt", state.proc);
  setOrClear(url, "kind", state.eventKind);
  setOrClear(url, "party", state.party);
  setOrClear(url, "q", state.q);
  history.replaceState(null, "", url.pathname + url.search + url.hash);
}

function setOrClear(url: URL, key: string, val: string): void {
  if (val) url.searchParams.set(key, val);
  else url.searchParams.delete(key);
}
