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

// t-paliad-154 — admin approval-policy authoring page orchestration.
//
// Two sections: Partner-Unit-Standards (accordion list) and Projekt-spezifisch
// (project picker → 8-cell matrix). Edits hit the per-scope CRUD endpoints
// from the same page; re-renders refresh from server state to surface
// inheritance changes.

interface PartnerUnit {
  id: string;
  name: string;
  office: string;
}

interface UnitPolicy {
  id: string;
  partner_unit_id: string | null;
  project_id: string | null;
  entity_type: string;
  lifecycle_event: string;
  // t-paliad-160 split-grammar.
  requires_approval: boolean;
  min_role?: string | null;
}

interface EffectivePolicy {
  entity_type: string;
  lifecycle_event: string;
  requires_approval: boolean;
  min_role?: string | null;
  source?: string | null;
  source_id?: string | null;
  source_name?: string | null;
}

interface ProjectNode {
  id: string;
  title: string;
  reference?: string | null;
  type?: string;
  parent_id?: string | null;
  children?: ProjectNode[];
}

const ENTITY_TYPES = ["deadline", "appointment"] as const;
const LIFECYCLES = ["create", "update", "complete", "delete"] as const;
// Strict-ladder roles only. The legacy "none" sentinel is gone — its job
// (suppress the gate) is now done by the requires_approval=false checkbox
// (t-paliad-160 §A).
const ROLE_OPTIONS = [
  "partner",
  "of_counsel",
  "associate",
  "senior_pa",
  "pa",
];

let partnerUnits: PartnerUnit[] = [];
let unitPolicies: Record<string, UnitPolicy[]> = {};
let allProjects: ProjectNode[] = [];
let selectedProjectID: string | null = null;
let selectedProjectTitle: string = "";

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

function escAttr(s: string): string {
  return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;");
}

function showFeedback(msg: string, isError: boolean): void {
  const el = document.getElementById("ap-feedback");
  if (!el) return;
  el.textContent = msg;
  el.className = "form-msg " + (isError ? "form-msg-error" : "form-msg-ok");
  el.style.display = "block";
  if (!isError) setTimeout(() => { el.style.display = "none"; }, 3500);
}

// ============================================================================
// Loaders.
// ============================================================================

async function loadPartnerUnits(): Promise<void> {
  const resp = await fetch("/api/partner-units");
  if (!resp.ok) {
    partnerUnits = [];
    return;
  }
  partnerUnits = (await resp.json()) as PartnerUnit[];
}

async function loadUnitPolicies(unitID: string): Promise<UnitPolicy[]> {
  const resp = await fetch(`/api/admin/partner-units/${encodeURIComponent(unitID)}/approval-policies`);
  if (!resp.ok) return [];
  return (await resp.json()) as UnitPolicy[];
}

async function loadAllUnitPolicies(): Promise<void> {
  const out: Record<string, UnitPolicy[]> = {};
  for (const u of partnerUnits) {
    out[u.id] = await loadUnitPolicies(u.id);
  }
  unitPolicies = out;
}

async function loadProjects(): Promise<void> {
  const resp = await fetch("/api/projects/tree");
  if (!resp.ok) {
    allProjects = [];
    return;
  }
  const tree = (await resp.json()) as ProjectNode[];
  allProjects = flattenTree(tree);
}

function flattenTree(nodes: ProjectNode[]): ProjectNode[] {
  const out: ProjectNode[] = [];
  const walk = (n: ProjectNode): void => {
    out.push(n);
    if (n.children) n.children.forEach(walk);
  };
  nodes.forEach(walk);
  return out;
}

async function loadMatrix(projectID: string): Promise<EffectivePolicy[]> {
  const resp = await fetch(`/api/admin/approval-policies/matrix?project_id=${encodeURIComponent(projectID)}`);
  if (!resp.ok) return [];
  return (await resp.json()) as EffectivePolicy[];
}

// ============================================================================
// Rendering — partner unit accordion.
// ============================================================================

function lifecycleLabel(l: string): string {
  return tDyn("admin.approval_policies.lifecycle." + l) || l;
}

function entityLabel(e: string): string {
  return tDyn("admin.approval_policies.entity." + e) || e;
}

function roleLabel(r: string): string {
  return tDyn("admin.approval_policies.role." + r) || r;
}

function policyForCell(rows: UnitPolicy[], entity: string, lifecycle: string): UnitPolicy | undefined {
  return rows.find((p) => p.entity_type === entity && p.lifecycle_event === lifecycle);
}

// Cell control state, t-paliad-160 §A.
//   none       → no project-specific rule authored (the cell inherits).
//   off        → requires_approval=false explicitly authored.
//   on(role)   → requires_approval=true with the given min_role.
//
// rendered as: [✓] requires approval   [role select]
//   - checkbox unchecked → role select disabled (greyed).
//   - checkbox checked   → role select enabled, min_role required.
//   - "no rule" — surfaced as a third button next to the controls so the
//     admin can explicitly clear an authored cell back to inheritance.
type CellAuthored =
  | { kind: "none" }
  | { kind: "off" }
  | { kind: "on"; role: string };

function authoredFromUnitPolicy(p: UnitPolicy | undefined): CellAuthored {
  if (!p) return { kind: "none" };
  if (!p.requires_approval) return { kind: "off" };
  return { kind: "on", role: p.min_role || "associate" };
}

function authoredFromEffective(r: EffectivePolicy): CellAuthored {
  if (r.source !== "project") return { kind: "none" };
  if (!r.requires_approval) return { kind: "off" };
  return { kind: "on", role: r.min_role || "associate" };
}

function renderCellControls(authored: CellAuthored, dataAttrs: string): string {
  const checked = authored.kind === "on";
  const disabled = authored.kind !== "on";
  const role = authored.kind === "on" ? authored.role : "associate";
  const opts = ROLE_OPTIONS.map((r) =>
    `<option value="${esc(r)}"${role === r ? " selected" : ""}>${esc(roleLabel(r))}</option>`
  ).join("");
  const reqLabel = esc(t("admin.approval_policies.cell.requires") || "Genehmigung");
  const clearLabel = esc(t("admin.approval_policies.cell.clear") || "—");
  const clearTitle = esc(t("admin.approval_policies.cell.clear.title") || "Regel zurücksetzen (erben)");
  const cleared = authored.kind === "none";
  return `
    <label class="ap-cell-toggle">
      <input type="checkbox" class="ap-cell-requires" ${dataAttrs}${checked ? " checked" : ""} aria-label="${reqLabel}" />
      <span class="ap-cell-toggle-label">${reqLabel}</span>
    </label>
    <select class="ap-cell-role" ${dataAttrs}${disabled ? " disabled" : ""}>${opts}</select>
    <button type="button" class="ap-cell-clear" ${dataAttrs} title="${clearTitle}"${cleared ? " disabled" : ""}>${clearLabel}</button>
  `;
}

function renderUnitMatrix(unit: PartnerUnit): string {
  const rows = unitPolicies[unit.id] || [];
  const buildCell = (e: string, l: string): string => {
    const p = policyForCell(rows, e, l);
    const authored = authoredFromUnitPolicy(p);
    const attrs = `data-scope="unit" data-unit-id="${escAttr(unit.id)}" data-entity="${esc(e)}" data-lifecycle="${esc(l)}"`;
    return `<div class="ap-cell-controls">${renderCellControls(authored, attrs)}</div>`;
  };

  let cells = "";
  for (const e of ENTITY_TYPES) {
    cells += `<tr><th class="ap-matrix-rowhead">${esc(entityLabel(e))}</th>`;
    for (const l of LIFECYCLES) {
      cells += `<td class="ap-matrix-cell">${buildCell(e, l)}</td>`;
    }
    cells += `</tr>`;
  }

  // Stacked sections for mobile (CSS toggles the table vs the list).
  let stacked = "";
  for (const e of ENTITY_TYPES) {
    stacked += `<div class="ap-matrix-section"><h4>${esc(entityLabel(e))}</h4>`;
    for (const l of LIFECYCLES) {
      stacked += `<div class="ap-matrix-row">
        <span class="ap-matrix-row-label">${esc(lifecycleLabel(l))}</span>
        ${buildCell(e, l)}
      </div>`;
    }
    stacked += `</div>`;
  }

  return `
    <table class="ap-matrix">
      <thead><tr><th></th>
        <th>${esc(lifecycleLabel("create"))}</th>
        <th>${esc(lifecycleLabel("update"))}</th>
        <th>${esc(lifecycleLabel("complete"))}</th>
        <th>${esc(lifecycleLabel("delete"))}</th>
      </tr></thead>
      <tbody>${cells}</tbody>
    </table>
    <div class="ap-matrix-stacked">${stacked}</div>
  `;
}

function renderUnits(): void {
  const host = document.getElementById("ap-units-list");
  if (!host) return;
  // Preserve which unit blocks were expanded across re-renders. Without this,
  // changing any cell's required_role saves and re-renders, collapsing the
  // accordion the admin was working in (m, 2026-05-08).
  const openUnitIDs = new Set<string>();
  host.querySelectorAll<HTMLDetailsElement>("details.ap-unit-block").forEach((d) => {
    if (d.open && d.dataset.unitId) openUnitIDs.add(d.dataset.unitId);
  });
  if (partnerUnits.length === 0) {
    host.innerHTML = `<p class="form-hint">${esc(t("admin.approval_policies.units.empty") || "Keine Partner Units vorhanden.")}</p>`;
    return;
  }
  host.innerHTML = partnerUnits.map((u) => `
    <details class="ap-unit-block" data-unit-id="${esc(u.id)}"${openUnitIDs.has(u.id) ? " open" : ""}>
      <summary class="ap-unit-summary">
        <span class="ap-unit-name">${esc(u.name)}</span>
        <span class="office-chip office-${esc(u.office)}">${esc(u.office)}</span>
      </summary>
      <div class="ap-unit-body">
        ${renderUnitMatrix(u)}
      </div>
    </details>
  `).join("");
  bindCellChangeHandlers(host);
}

// ============================================================================
// Rendering — project matrix with attribution chips.
// ============================================================================

function renderProjectMatrix(rows: EffectivePolicy[]): string {
  const cell = (r: EffectivePolicy): string => {
    const own = r.source === "project";
    const attrs = `data-scope="project" data-project-id="${escAttr(selectedProjectID || "")}" data-entity="${esc(r.entity_type)}" data-lifecycle="${esc(r.lifecycle_event)}"`;
    // The controls show the AUTHORED state — the project row's own values
    // when there is one, else the inherited state is rendered via the
    // attribution chip and the controls sit unset (kind="none"). Most-
    // strict-wins inheritance from ancestors / unit defaults is purely
    // informational on this row; flipping the controls writes a new
    // project-specific row.
    const authored = authoredFromEffective(r);
    let chip = "";
    if (r.source && !own && r.requires_approval) {
      // Inherited from ancestor or unit default. Surface attribution +
      // the inherited min_role so the admin sees what the cell is
      // resolving to before they author an override.
      const sourceKey = r.source === "ancestor" ? "admin.approval_policies.source.ancestor" :
                        r.source === "unit_default" ? "admin.approval_policies.source.unit_default" :
                        "admin.approval_policies.source.project";
      const label = tDyn(sourceKey) || r.source;
      const name = r.source_name ? ` · ${esc(r.source_name)}` : "";
      const role = r.min_role ? ` · ${esc(roleLabel(r.min_role))}+` : "";
      chip = `<span class="ap-source-chip ap-source-${esc(r.source)}">${esc(label)}${name}${role}</span>`;
    } else if (r.source && !own && !r.requires_approval) {
      // Inherited "no approval needed" — distinct from "no rule at all".
      const sourceKey = r.source === "ancestor" ? "admin.approval_policies.source.ancestor" :
                        "admin.approval_policies.source.unit_default";
      const label = tDyn(sourceKey) || r.source;
      const name = r.source_name ? ` · ${esc(r.source_name)}` : "";
      const offLabel = esc(t("admin.approval_policies.source.no_approval") || "keine Genehmigung");
      chip = `<span class="ap-source-chip ap-source-${esc(r.source)}">${esc(label)}${name} · ${offLabel}</span>`;
    } else if (own) {
      chip = `<span class="ap-source-chip ap-source-project">${esc(t("admin.approval_policies.source.project") || "Projekt")}</span>`;
    }
    return `<div class="ap-cell-wrap"><div class="ap-cell-controls">${renderCellControls(authored, attrs)}</div>${chip}</div>`;
  };

  const byCell = new Map<string, EffectivePolicy>();
  for (const r of rows) byCell.set(`${r.entity_type}:${r.lifecycle_event}`, r);
  const cellFor = (e: string, l: string): EffectivePolicy =>
    byCell.get(`${e}:${l}`) || { entity_type: e, lifecycle_event: l };

  let table = "";
  for (const e of ENTITY_TYPES) {
    table += `<tr><th class="ap-matrix-rowhead">${esc(entityLabel(e))}</th>`;
    for (const l of LIFECYCLES) {
      table += `<td class="ap-matrix-cell">${cell(cellFor(e, l))}</td>`;
    }
    table += `</tr>`;
  }

  let stacked = "";
  for (const e of ENTITY_TYPES) {
    stacked += `<div class="ap-matrix-section"><h4>${esc(entityLabel(e))}</h4>`;
    for (const l of LIFECYCLES) {
      stacked += `<div class="ap-matrix-row">
        <span class="ap-matrix-row-label">${esc(lifecycleLabel(l))}</span>
        ${cell(cellFor(e, l))}
      </div>`;
    }
    stacked += `</div>`;
  }

  return `
    <table class="ap-matrix">
      <thead><tr><th></th>
        <th>${esc(lifecycleLabel("create"))}</th>
        <th>${esc(lifecycleLabel("update"))}</th>
        <th>${esc(lifecycleLabel("complete"))}</th>
        <th>${esc(lifecycleLabel("delete"))}</th>
      </tr></thead>
      <tbody>${table}</tbody>
    </table>
    <div class="ap-matrix-stacked">${stacked}</div>
  `;
}

async function selectProject(p: ProjectNode): Promise<void> {
  selectedProjectID = p.id;
  selectedProjectTitle = p.title;
  const matrix = await loadMatrix(p.id);
  const wrap = document.getElementById("ap-project-matrix");
  const host = document.getElementById("ap-matrix-host");
  const titleEl = document.getElementById("ap-project-title");
  if (!wrap || !host || !titleEl) return;
  wrap.style.display = "block";
  titleEl.textContent = p.title + (p.reference ? ` · ${p.reference}` : "");
  host.innerHTML = renderProjectMatrix(matrix);
  bindCellChangeHandlers(host);
}

function renderProjectResults(filter: string): void {
  const host = document.getElementById("ap-project-results");
  if (!host) return;
  const q = filter.trim().toLowerCase();
  let matches = allProjects;
  if (q.length > 0) {
    matches = allProjects.filter((p) => {
      const t = (p.title || "").toLowerCase();
      const r = (p.reference || "").toLowerCase();
      return t.includes(q) || r.includes(q);
    });
  }
  matches = matches.slice(0, 30);
  if (matches.length === 0) {
    host.innerHTML = `<p class="form-hint">${esc(t("admin.approval_policies.picker.no_results") || "Keine Treffer.")}</p>`;
    return;
  }
  host.innerHTML = matches.map((p) => `
    <button type="button" class="ap-project-result" data-id="${escAttr(p.id)}">
      <span class="ap-project-result-title">${esc(p.title)}</span>
      ${p.reference ? `<span class="ap-project-result-ref">${esc(p.reference)}</span>` : ""}
    </button>
  `).join("");
  host.querySelectorAll<HTMLButtonElement>(".ap-project-result").forEach((btn) => {
    btn.addEventListener("click", () => {
      const id = btn.dataset.id || "";
      const p = allProjects.find((x) => x.id === id);
      if (p) void selectProject(p);
    });
  });
}

// ============================================================================
// Cell change → server.
// ============================================================================

function bindCellChangeHandlers(scope: HTMLElement): void {
  // Each cell now has THREE controls — the requires-approval checkbox,
  // the role select, and the explicit "clear / inherit" button. They all
  // share data-* attrs so onCellChange can derive the URL + intended
  // post-state from any of them.
  scope.querySelectorAll<HTMLInputElement>(".ap-cell-requires").forEach((cb) => {
    cb.addEventListener("change", () => {
      // Toggle the sibling role select disabled state immediately for
      // visual feedback — the server PUT might lag.
      const wrap = cb.closest(".ap-cell-controls") as HTMLElement | null;
      const sel = wrap?.querySelector<HTMLSelectElement>(".ap-cell-role");
      if (sel) sel.disabled = !cb.checked;
      void onCellChangeFromCheckbox(cb);
    });
  });
  scope.querySelectorAll<HTMLSelectElement>(".ap-cell-role").forEach((sel) => {
    sel.addEventListener("change", () => void onCellChangeFromRole(sel));
  });
  scope.querySelectorAll<HTMLButtonElement>(".ap-cell-clear").forEach((btn) => {
    btn.addEventListener("click", () => void onCellClear(btn));
  });
}

function cellEndpointURL(el: HTMLElement): string | null {
  const scope = el.dataset.scope;
  const entity = el.dataset.entity || "";
  const lifecycle = el.dataset.lifecycle || "";
  if (scope === "unit") {
    const unitID = el.dataset.unitId || "";
    return `/api/admin/partner-units/${encodeURIComponent(unitID)}/approval-policies/${encodeURIComponent(entity)}/${encodeURIComponent(lifecycle)}`;
  }
  if (scope === "project") {
    const projectID = el.dataset.projectId || "";
    return `/api/projects/${encodeURIComponent(projectID)}/approval-policies/${encodeURIComponent(entity)}/${encodeURIComponent(lifecycle)}`;
  }
  return null;
}

async function onCellChangeFromCheckbox(cb: HTMLInputElement): Promise<void> {
  // Checkbox flipped — write the cell as (requires_approval, min_role).
  const wrap = cb.closest(".ap-cell-controls") as HTMLElement | null;
  const sel = wrap?.querySelector<HTMLSelectElement>(".ap-cell-role");
  const requires = cb.checked;
  const minRole = requires ? (sel?.value || "associate") : null;
  await putCellSplit(cb, requires, minRole);
}

async function onCellChangeFromRole(sel: HTMLSelectElement): Promise<void> {
  // Role select changed — only meaningful when the checkbox is on (the
  // disabled state would block this on a real interaction, but pin it
  // for safety).
  const wrap = sel.closest(".ap-cell-controls") as HTMLElement | null;
  const cb = wrap?.querySelector<HTMLInputElement>(".ap-cell-requires");
  if (!cb || !cb.checked) return;
  await putCellSplit(sel, true, sel.value);
}

async function onCellClear(btn: HTMLButtonElement): Promise<void> {
  // Explicit "back to inheritance" — DELETE the project / unit row.
  const url = cellEndpointURL(btn);
  if (!url) return;
  try {
    const resp = await fetch(url, { method: "DELETE" });
    if (!resp.ok) {
      const errBody = await resp.text();
      showFeedback(`${t("admin.approval_policies.cell.error_msg") || "Fehler"}: ${errBody}`, true);
      return;
    }
    showFeedback(t("admin.approval_policies.cell.saved_msg") || "Gespeichert.", false);
    await refreshAfterCellMutation(btn);
  } catch (err) {
    showFeedback(`${t("admin.approval_policies.cell.error_msg") || "Fehler"}: ${err}`, true);
  }
}

async function putCellSplit(el: HTMLElement, requires: boolean, minRole: string | null): Promise<void> {
  const url = cellEndpointURL(el);
  if (!url) return;
  try {
    const resp = await fetch(url, {
      method: "PUT",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ requires_approval: requires, min_role: minRole }),
    });
    if (!resp.ok) {
      const errBody = await resp.text();
      showFeedback(`${t("admin.approval_policies.cell.error_msg") || "Fehler"}: ${errBody}`, true);
      return;
    }
    showFeedback(t("admin.approval_policies.cell.saved_msg") || "Gespeichert.", false);
    await refreshAfterCellMutation(el);
  } catch (err) {
    showFeedback(`${t("admin.approval_policies.cell.error_msg") || "Fehler"}: ${err}`, true);
  }
}

async function refreshAfterCellMutation(el: HTMLElement): Promise<void> {
  const scope = el.dataset.scope;
  if (scope === "unit") {
    const unitID = el.dataset.unitId || "";
    unitPolicies[unitID] = await loadUnitPolicies(unitID);
    renderUnits();
  } else if (selectedProjectID) {
    const matrix = await loadMatrix(selectedProjectID);
    const host = document.getElementById("ap-matrix-host");
    if (host) {
      host.innerHTML = renderProjectMatrix(matrix);
      bindCellChangeHandlers(host);
    }
  }
}

// ============================================================================
// Bulk-apply to descendants.
// ============================================================================

function descendantsOf(rootID: string): ProjectNode[] {
  // Build parent-child map from the flat list.
  const byParent = new Map<string, ProjectNode[]>();
  for (const p of allProjects) {
    const parent = p.parent_id || "";
    if (!byParent.has(parent)) byParent.set(parent, []);
    byParent.get(parent)!.push(p);
  }
  const out: ProjectNode[] = [];
  const walk = (id: string): void => {
    const kids = byParent.get(id) || [];
    for (const k of kids) {
      out.push(k);
      walk(k.id);
    }
  };
  walk(rootID);
  return out;
}

function openBulkModal(): void {
  if (!selectedProjectID) return;
  const targets = descendantsOf(selectedProjectID);
  const list = document.getElementById("ap-bulk-target-list");
  const modal = document.getElementById("ap-bulk-modal");
  if (!list || !modal) return;
  if (targets.length === 0) {
    showFeedback(t("admin.approval_policies.bulk.no_descendants") || "Keine Unterprojekte vorhanden.", true);
    return;
  }
  list.innerHTML = targets.map((p) => `
    <li><span class="ap-bulk-target-title">${esc(p.title)}</span>${p.reference ? ` <span class="ap-bulk-target-ref">${esc(p.reference)}</span>` : ""}</li>
  `).join("");
  modal.style.display = "flex";
  modal.dataset.targets = JSON.stringify(targets.map((p) => p.id));
}

function closeBulkModal(): void {
  const modal = document.getElementById("ap-bulk-modal");
  if (modal) modal.style.display = "none";
}

async function confirmBulk(): Promise<void> {
  if (!selectedProjectID) return;
  const modal = document.getElementById("ap-bulk-modal");
  const msg = document.getElementById("ap-bulk-msg");
  if (!modal || !msg) return;
  const targets = JSON.parse(modal.dataset.targets || "[]") as string[];
  msg.textContent = t("admin.approval_policies.bulk.modal.applying") || "Übernehme …";
  try {
    const resp = await fetch("/api/admin/approval-policies/apply-to-descendants", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        source_project_id: selectedProjectID,
        target_project_ids: targets,
      }),
    });
    if (!resp.ok) {
      const body = await resp.text();
      msg.textContent = `${t("admin.approval_policies.cell.error_msg") || "Fehler"}: ${body}`;
      return;
    }
    const out = await resp.json() as { writes: number; targets: number };
    closeBulkModal();
    showFeedback(
      (t("admin.approval_policies.bulk.modal.done") || "Übernommen") +
        ` — ${out.writes} ${t("admin.approval_policies.bulk.modal.writes_label") || "Schreibvorgänge"} auf ${out.targets} ${t("admin.approval_policies.bulk.modal.targets_label") || "Projekte"}.`,
      false,
    );
    // Re-fetch the source matrix so any cells the bulk-apply touched on
    // descendants are reflected via inheritance attribution if applicable.
    if (selectedProjectID) {
      const matrix = await loadMatrix(selectedProjectID);
      const host = document.getElementById("ap-matrix-host");
      if (host) {
        host.innerHTML = renderProjectMatrix(matrix);
        bindCellChangeHandlers(host);
      }
    }
  } catch (err) {
    msg.textContent = `${t("admin.approval_policies.cell.error_msg") || "Fehler"}: ${err}`;
  }
}

// ============================================================================
// Wire-up.
// ============================================================================

function wirePicker(): void {
  const input = document.getElementById("ap-project-search") as HTMLInputElement | null;
  if (!input) return;
  input.addEventListener("input", () => renderProjectResults(input.value));
  // Initial empty-search renders top-of-list.
  renderProjectResults("");
}

function wireBulk(): void {
  const btn = document.getElementById("ap-bulk-apply-btn");
  const close = document.getElementById("ap-bulk-close");
  const cancel = document.getElementById("ap-bulk-cancel");
  const confirm = document.getElementById("ap-bulk-confirm");
  if (btn) btn.addEventListener("click", openBulkModal);
  if (close) close.addEventListener("click", closeBulkModal);
  if (cancel) cancel.addEventListener("click", closeBulkModal);
  if (confirm) confirm.addEventListener("click", () => void confirmBulk());
}

async function init(): Promise<void> {
  initI18n();
  initSidebar();
  await Promise.all([loadPartnerUnits(), loadProjects()]);
  await loadAllUnitPolicies();
  renderUnits();
  wirePicker();
  wireBulk();
  onLangChange(() => {
    renderUnits();
    if (selectedProjectID) {
      void loadMatrix(selectedProjectID).then((matrix) => {
        const host = document.getElementById("ap-matrix-host");
        if (host) {
          host.innerHTML = renderProjectMatrix(matrix);
          bindCellChangeHandlers(host);
        }
      });
    }
    renderProjectResults((document.getElementById("ap-project-search") as HTMLInputElement | null)?.value || "");
  });
}

if (document.readyState === "loading") {
  document.addEventListener("DOMContentLoaded", () => void init());
} else {
  void init();
}
