import { initI18n, t, tDyn, getLang } from "./i18n";
import { initSidebar } from "./sidebar";
import {
  attachEventTypePicker,
  eventTypeLabel,
  fetchEventTypes,
  type EventType,
  type PickerHandle,
} from "./event-types";
import { projectIndent } from "./project-indent";
import { formatRuleLabel, formatRuleLabelHTML } from "./rule-label";

let eventTypePicker: PickerHandle | null = null;
let currentUserAdmin = false;
let eventTypesByID = new Map<string, EventType>();

interface Project {
  id: string;
  reference?: string | null;
  title: string;
  path: string;
  // Used by the Type→Rule resolver to narrow rule candidates to the
  // project's own proceeding when one applies. Optional because clients
  // and matter-level projects don't carry a proceeding type.
  proceeding_type_id?: number | null;
}

interface DeadlineRule {
  id: string;
  code?: string;
  name: string;
  name_en: string;
  rule_code?: string;
  legal_source?: string | null;
  proceeding_type_id?: number | null;
  sequence_order?: number;
  // t-paliad-165 — canonical event_type for the rule's concept. The
  // catalog is indexed by it so we can resolve Type → canonical Rule.
  concept_default_event_type_id?: string | null;
}

interface ProceedingType {
  id: number;
  code: string;
  name: string;
  name_en?: string;
  jurisdiction: string;
  sort_order?: number;
}

// Rule mode (t-paliad-258 / m/paliad#89). The form has two states:
//   auto   — rule_id resolved from the chosen event_type, rendered
//            read-only as "Auto: Name · Citation".
//   custom — free-text input; submits as custom_rule_text on the API.
type RuleMode = "auto" | "custom";
let ruleMode: RuleMode = "auto";

let rulesByID = new Map<string, DeadlineRule>();
let allRules: DeadlineRule[] = [];
let proceedingTypesByID = new Map<number, ProceedingType>();
let projectsByID = new Map<string, Project>();

let preselectedProjectID = "";
let preselectedProjectIDLocal = "";

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

function showError(msg: string) {
  const el = document.getElementById("deadline-new-msg")!;
  el.textContent = msg;
  el.className = "form-msg form-msg-error";
}

function proceedingLabel(pt: ProceedingType | undefined): string {
  if (!pt) return "";
  const lang = getLang();
  const name = (lang === "en" && pt.name_en) ? pt.name_en : pt.name;
  return `${pt.jurisdiction} — ${name}`;
}

async function loadProjects() {
  const sel = document.getElementById("deadline-project") as HTMLSelectElement;
  const hint = document.getElementById("deadline-project-empty-hint")!;
  try {
    const resp = await fetch("/api/projects");
    if (!resp.ok) return;
    const projects: Project[] = await resp.json();
    projectsByID = new Map(projects.map((p) => [p.id, p]));
    if (projects.length === 0) {
      hint.style.display = "";
      hint.innerHTML = `${esc(t("deadlines.field.akte.empty"))} <a href="/projects/new">${esc(t("deadlines.field.akte.empty.link"))}</a>`;
      return;
    }
    const options: string[] = [
      `<option value="" disabled${preselectedProjectID ? "" : " selected"} data-i18n="deadlines.field.akte.choose">${esc(t("deadlines.field.akte.choose"))}</option>`,
    ];
    for (const p of projects) {
      const isSelected = preselectedProjectID === p.id ? " selected" : "";
      const ref = p.reference || "";
      const indent = projectIndent(p.path);
      options.push(
        `<option value="${esc(p.id)}"${isSelected}>${indent}${esc(ref)} — ${esc(p.title)}</option>`,
      );
    }
    sel.innerHTML = options.join("");
  } catch {
    /* non-fatal */
  }
}

async function loadProceedingTypes() {
  try {
    const resp = await fetch("/api/proceeding-types-db");
    if (!resp.ok) return;
    const types: ProceedingType[] = await resp.json();
    proceedingTypesByID = new Map(types.map((pt) => [pt.id, pt]));
  } catch {
    /* non-fatal */
  }
}

async function loadRules() {
  try {
    const resp = await fetch("/api/deadline-rules");
    if (!resp.ok) return;
    allRules = (await resp.json()) as DeadlineRule[];
    rulesByID = new Map(allRules.map((r) => [r.id, r]));
  } catch {
    /* non-fatal — rule display falls back to "—" */
  }
}

// resolveAutoRuleForType picks the best-match catalog rule for the
// chosen event type, scoring by:
//   1. project's proceeding_type_id (if known) — exact match wins,
//   2. otherwise event_type.jurisdiction matches the rule's proceeding's
//      jurisdiction (EPA→EPO canonicalised),
//   3. otherwise the first candidate in canonical sequence_order.
//
// Returns null when no rule maps. Callers render that as "no Auto rule
// available" so the user can flip to Custom or pick a different Type.
function resolveAutoRuleForType(eventTypeID: string, projectID: string): DeadlineRule | null {
  const candidates = allRules.filter((r) => r.concept_default_event_type_id === eventTypeID);
  if (candidates.length === 0) return null;
  if (candidates.length === 1) return candidates[0];

  const project = projectID ? projectsByID.get(projectID) : undefined;
  if (project?.proceeding_type_id) {
    const exact = candidates.find((r) => r.proceeding_type_id === project.proceeding_type_id);
    if (exact) return exact;
  }

  const et = eventTypesByID.get(eventTypeID);
  if (et?.jurisdiction && et.jurisdiction !== "any") {
    const want = et.jurisdiction === "EPO" ? "EPA" : et.jurisdiction;
    const jurMatch = candidates.find((r) => {
      const pt = r.proceeding_type_id ? proceedingTypesByID.get(r.proceeding_type_id) : undefined;
      return pt?.jurisdiction === want;
    });
    if (jurMatch) return jurMatch;
  }

  return candidates[0];
}

// currentAutoRule returns the catalog rule the Auto mode would resolve
// to for the current form state, or null when no Type is picked or no
// rule maps. Centralised so the Auto display, submitForm, and the
// Standardtitel button all agree on the same resolution.
function currentAutoRule(): DeadlineRule | null {
  const picked = eventTypePicker?.getIDs() ?? [];
  if (picked.length !== 1) return null;
  const projectID = (document.getElementById("deadline-project") as HTMLSelectElement | null)?.value || "";
  return resolveAutoRuleForType(picked[0], projectID);
}

// refreshRuleAutoDisplay updates the read-only Auto display panel to
// reflect the rule that would be saved in Auto mode. Hides itself when
// the user is in Custom mode (the input takes its place).
function refreshRuleAutoDisplay(): void {
  const panel = document.getElementById("deadline-rule-auto-display");
  const text = document.getElementById("deadline-rule-auto-text");
  if (!panel || !text) return;
  if (ruleMode !== "auto") {
    panel.style.display = "none";
    return;
  }
  panel.style.display = "";
  const rule = currentAutoRule();
  if (rule) {
    // Canonical "Name · Citation" with muted citation (t-paliad-258 addendum).
    text.innerHTML = formatRuleLabelHTML(rule, esc);
    text.classList.remove("rule-auto-text--empty");
    return;
  }
  const picked = eventTypePicker?.getIDs() ?? [];
  const fallback = picked.length === 1
    ? (t("deadlines.field.rule.auto_no_match") || "Keine Regel zur gewählten Verfahrenshandlung")
    : (t("deadlines.field.rule.auto_pick_type") || "Wählen Sie zuerst eine Verfahrenshandlung");
  text.textContent = fallback;
  text.classList.add("rule-auto-text--empty");
}

function applyRuleModeUI(): void {
  const toggleBtn = document.getElementById("deadline-rule-mode-toggle") as HTMLButtonElement | null;
  const autoPanel = document.getElementById("deadline-rule-auto-display");
  const customInput = document.getElementById("deadline-rule-custom-input") as HTMLInputElement | null;
  if (!toggleBtn || !autoPanel || !customInput) return;
  if (ruleMode === "auto") {
    autoPanel.style.display = "";
    customInput.style.display = "none";
    toggleBtn.textContent = t("deadlines.field.rule.mode.toggle_to_custom") || "Eigene Regel eingeben";
    toggleBtn.setAttribute("data-i18n", "deadlines.field.rule.mode.toggle_to_custom");
  } else {
    autoPanel.style.display = "none";
    customInput.style.display = "";
    toggleBtn.textContent = t("deadlines.field.rule.mode.toggle_to_auto") || "Zurück zu Auto";
    toggleBtn.setAttribute("data-i18n", "deadlines.field.rule.mode.toggle_to_auto");
  }
  refreshRuleAutoDisplay();
}

function setRuleMode(mode: RuleMode): void {
  ruleMode = mode;
  applyRuleModeUI();
  if (mode === "custom") {
    const input = document.getElementById("deadline-rule-custom-input") as HTMLInputElement | null;
    input?.focus();
  }
}

// computeDefaultTitle — t-paliad-251 Part 4. Priority order picks the head:
//   1. event_type label (when exactly one Typ chip is set)
//   2. canonical rule name (when Auto resolves to a rule)
//   3. custom rule text (when in Custom mode)
//   4. proceeding type name (when project carries one)
//   5. fallback i18n key
// Suffix: " — <project-reference>" when not already in head.
function computeDefaultTitle(): string {
  const projectID = (document.getElementById("deadline-project") as HTMLSelectElement | null)?.value || "";
  const project = projectID ? projectsByID.get(projectID) : undefined;
  const picked = eventTypePicker?.getIDs() ?? [];

  let head = "";
  if (picked.length === 1) {
    const et = eventTypesByID.get(picked[0]);
    if (et) head = eventTypeLabel(et);
  }
  if (!head) {
    if (ruleMode === "auto") {
      const rule = currentAutoRule();
      if (rule) head = formatRuleLabel(rule);
    } else {
      const customInput = document.getElementById("deadline-rule-custom-input") as HTMLInputElement | null;
      const txt = customInput?.value.trim() || "";
      if (txt) head = txt;
    }
  }
  if (!head && project?.proceeding_type_id) {
    const pt = proceedingTypesByID.get(project.proceeding_type_id);
    if (pt) head = proceedingLabel(pt);
  }
  if (!head) {
    head = t("deadlines.field.title.default_fallback");
  }

  const ref = project?.reference?.trim() || "";
  if (ref && !head.includes(ref)) {
    return `${head} — ${ref}`;
  }
  return head;
}

async function submitForm(e: Event) {
  e.preventDefault();
  const submitBtn = document.querySelector<HTMLButtonElement>("#deadline-new-form button[type=submit]")!;
  const msg = document.getElementById("deadline-new-msg")!;

  const projectID = (document.getElementById("deadline-project") as HTMLSelectElement).value;
  const title = (document.getElementById("deadline-title") as HTMLInputElement).value.trim();
  const due = (document.getElementById("deadline-due") as HTMLInputElement).value;
  const notes = (document.getElementById("deadline-notes") as HTMLTextAreaElement).value.trim();

  if (!projectID || !title || !due) {
    showError(t("deadlines.error.required"));
    return;
  }

  msg.textContent = "";
  msg.className = "form-msg";
  submitBtn.disabled = true;

  const payload: Record<string, unknown> = {
    title,
    due_date: due,
    source: "manual",
  };
  // Rule field: Auto resolves to rule_id, Custom sends the free text.
  if (ruleMode === "auto") {
    const rule = currentAutoRule();
    if (rule) payload.rule_id = rule.id;
  } else {
    const customInput = document.getElementById("deadline-rule-custom-input") as HTMLInputElement | null;
    const txt = customInput?.value.trim() || "";
    if (txt) payload.custom_rule_text = txt;
  }
  if (notes) payload.notes = notes;
  const eventTypeIDs = eventTypePicker?.getIDs() ?? [];
  if (eventTypeIDs.length > 0) payload.event_type_ids = eventTypeIDs;

  try {
    const resp = await fetch(`/api/projects/${encodeURIComponent(projectID)}/deadlines`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(payload),
    });
    if (!resp.ok) {
      const data = await resp.json().catch(() => ({}) as { error?: string });
      showError(data.error || t("deadlines.error.generic"));
      submitBtn.disabled = false;
      return;
    }
    const created = await resp.json();
    if (preselectedProjectIDLocal) {
      window.location.href = `/projects/${preselectedProjectIDLocal}/deadlines`;
    } else {
      window.location.href = `/deadlines/${created.id}`;
    }
  } catch {
    showError(t("deadlines.error.generic"));
    submitBtn.disabled = false;
  }
}

function detectPreselect() {
  // Path /projects/{id}/deadlines/new pre-selects that project.
  const parts = window.location.pathname.split("/").filter(Boolean);
  if (parts[0] === "projects" && parts[1] && parts[2] === "deadlines" && parts[3] === "new") {
    preselectedProjectID = parts[1];
  }
  // Or ?project_id= query string
  const qp = new URLSearchParams(window.location.search);
  const fromQuery = qp.get("project_id");
  if (fromQuery) preselectedProjectID = fromQuery;
}

function initBackLinks() {
  if (preselectedProjectID) {
    const back = document.getElementById("deadline-new-back") as HTMLAnchorElement;
    const cancel = document.getElementById("deadline-new-cancel") as HTMLAnchorElement;
    back.href = `/projects/${preselectedProjectID}/deadlines`;
    cancel.href = `/projects/${preselectedProjectID}/deadlines`;
  }
  preselectedProjectIDLocal = preselectedProjectID;
}

async function loadMe() {
  try {
    const resp = await fetch("/api/me");
    if (!resp.ok) return;
    const me = await resp.json();
    currentUserAdmin = me?.global_role === "global_admin";
  } catch {
    /* non-fatal */
  }
}

// t-paliad-154 — fetch the effective approval policy for (project,
// deadline, create) and reveal the form-time hint when it applies.
async function refreshApprovalHint(): Promise<void> {
  const hint = document.getElementById("deadline-approval-hint");
  const text = document.getElementById("deadline-approval-hint-text");
  if (!hint || !text) return;
  const projectID = (document.getElementById("deadline-project") as HTMLSelectElement | null)?.value || "";
  if (!projectID) {
    hint.style.display = "none";
    return;
  }
  try {
    const resp = await fetch(
      `/api/projects/${encodeURIComponent(projectID)}/approval-policies/effective?entity_type=deadline&lifecycle=create`,
      { credentials: "include" },
    );
    if (!resp.ok) {
      hint.style.display = "none";
      return;
    }
    const eff = await resp.json() as {
      requires_approval?: boolean;
      min_role?: string | null;
      required_role?: string | null;
      source?: string | null;
      source_name?: string | null;
    };
    const role = eff.min_role || eff.required_role || null;
    const required = (eff.requires_approval === true) || (role !== null && role !== "none");
    if (!required || !role) {
      hint.style.display = "none";
      return;
    }
    const roleLabel = tDyn("admin.approval_policies.role." + role) || role;
    const sourceLabel = eff.source_name
      ? ` · ${tDyn("admin.approval_policies.source." + (eff.source || "")) || ""}: ${eff.source_name}`
      : "";
    text.textContent = (t("deadlines.form.approval_hint") || "4-Augen-Prüfung erforderlich")
      + ` · ${roleLabel}${sourceLabel}`;
    hint.style.display = "";
  } catch {
    hint.style.display = "none";
  }
}

document.addEventListener("DOMContentLoaded", async () => {
  initI18n();
  initSidebar();
  detectPreselect();
  initBackLinks();
  document.getElementById("deadline-new-form")!.addEventListener("submit", submitForm);
  // Default due to today
  const dueInput = document.getElementById("deadline-due") as HTMLInputElement;
  if (!dueInput.value) dueInput.value = new Date().toISOString().split("T")[0];

  await Promise.all([loadProjects(), loadProceedingTypes(), loadRules(), loadMe()]);

  const pickerHost = document.getElementById("deadline-event-types");
  if (pickerHost) {
    eventTypePicker = attachEventTypePicker(pickerHost, {
      currentUserAdmin,
      onChange: () => {
        // Type change shifts which Auto rule resolves; re-render the
        // read-only Auto display panel.
        refreshRuleAutoDisplay();
      },
    });
  }

  // Preload event_types for the Auto display + Standardtitel resolver.
  fetchEventTypes()
    .then((types) => {
      eventTypesByID = new Map(types.map((et) => [et.id, et]));
      refreshRuleAutoDisplay();
    })
    .catch(() => {/* non-fatal */});

  // Rule mode toggle.
  document.getElementById("deadline-rule-mode-toggle")?.addEventListener("click", () => {
    setRuleMode(ruleMode === "auto" ? "custom" : "auto");
  });

  applyRuleModeUI();

  // Approval-hint refresh: on first render + on project change.
  void refreshApprovalHint();
  document.getElementById("deadline-project")?.addEventListener("change", () => {
    void refreshApprovalHint();
    // Project change can shift which Auto rule resolves (via the
    // project's proceeding_type_id).
    refreshRuleAutoDisplay();
  });

  // t-paliad-251 Part 4 — Standardtitel button.
  document.getElementById("deadline-title-default-btn")?.addEventListener("click", () => {
    const titleInput = document.getElementById("deadline-title") as HTMLInputElement | null;
    if (!titleInput) return;
    const derived = computeDefaultTitle();
    if (derived) titleInput.value = derived;
    titleInput.focus();
  });
});
