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

interface AuditEntry {
  timestamp: string;
  id: string;
  source: string;
  event_type: string;
  actor: string;
  subject: string;
  project_id?: string;
  title?: string;
  description?: string;
}

interface Cursor {
  ts: string;
  id: string;
}

interface AuditResponse {
  entries: AuditEntry[];
  next_cursor: Cursor | null;
}

const PAGE_SIZE = 50;

let entries: AuditEntry[] = [];
let nextCursor: Cursor | null = null;
let searchDebounce: number | undefined;

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

function fmtDateTime(iso: string): string {
  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",
    second: "2-digit",
  });
}

function sourceLabel(source: string): string {
  return tDyn(`admin.audit.source.${source}`) || source;
}

function sourceClass(source: string): string {
  switch (source) {
    case "project_events":
      return "admin-audit-source admin-audit-source-project";
    case "caldav_sync_log":
      return "admin-audit-source admin-audit-source-caldav";
    case "reminder_log":
      return "admin-audit-source admin-audit-source-reminder";
    default:
      return "admin-audit-source";
  }
}

// eventNarrative produces the localised "what happened" pair (event label,
// description body) for a row. project_events delegates to the shared
// translateEvent — that's the t-paliad-067 PR-1 logic the audit table is
// supposed to reuse so DE/EN narratives stay identical to the dashboard
// activity feed. caldav_sync_log and reminder_log have their own per-event
// label keys; their stored description is already a flat key=value summary
// so we surface it verbatim.
function eventNarrative(e: AuditEntry): { label: string; body: string } {
  if (e.source === "project_events") {
    const { title, description } = translateEvent(
      e.event_type,
      e.title ?? "",
      e.description ?? "",
    );
    return { label: title || e.event_type, body: description };
  }
  const labelKey = `admin.audit.event.${e.event_type}`;
  const translated = tDyn(labelKey);
  const label = translated && translated !== labelKey ? translated : e.event_type;
  return { label, body: e.description ?? "" };
}

function rowHTML(e: AuditEntry): string {
  const { label, body } = eventNarrative(e);
  const subjectCell = e.project_id
    ? `<a href="/projects/${esc(e.project_id)}">${esc(e.subject)}</a>`
    : esc(e.subject);
  return `
    <tr data-id="${esc(e.id)}">
      <td class="admin-audit-time">${esc(fmtDateTime(e.timestamp))}</td>
      <td><span class="${sourceClass(e.source)}">${esc(sourceLabel(e.source))}</span></td>
      <td><code class="admin-audit-event">${esc(label)}</code></td>
      <td>${esc(e.actor)}</td>
      <td>${subjectCell}</td>
      <td class="admin-audit-desc">${esc(body)}</td>
    </tr>`;
}

function showFeedback(msg: string, isError: boolean) {
  const el = document.getElementById("audit-feedback")!;
  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"; }, 2500);
  }
}

function render() {
  const tbody = document.getElementById("audit-tbody")!;
  const empty = document.getElementById("audit-empty")!;
  const counter = document.getElementById("audit-count")!;
  const loadmore = document.getElementById("audit-loadmore") as HTMLButtonElement;

  if (entries.length === 0) {
    tbody.innerHTML = "";
    empty.style.display = "block";
    counter.textContent = "0";
  } else {
    empty.style.display = "none";
    tbody.innerHTML = entries.map(rowHTML).join("");
    counter.textContent = String(entries.length);
  }
  loadmore.style.display = nextCursor ? "" : "none";
}

function rangePresetToFrom(preset: string): Date | null {
  const now = new Date();
  switch (preset) {
    case "24h":
      return new Date(now.getTime() - 24 * 60 * 60 * 1000);
    case "7d":
      return new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
    case "30d":
      return new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
    default:
      return null;
  }
}

function buildQuery(cursor: Cursor | null): string {
  const params = new URLSearchParams();
  const source = (document.getElementById("audit-source") as HTMLSelectElement).value;
  if (source) params.set("source", source);

  const range = (document.getElementById("audit-range") as HTMLSelectElement).value;
  if (range === "custom") {
    const from = (document.getElementById("audit-from") as HTMLInputElement).value;
    const to = (document.getElementById("audit-to") as HTMLInputElement).value;
    if (from) params.set("from", from);
    if (to) params.set("to", to);
  } else if (range !== "all") {
    const from = rangePresetToFrom(range);
    if (from) params.set("from", from.toISOString());
  }

  const q = (document.getElementById("audit-search") as HTMLInputElement).value.trim();
  if (q) params.set("q", q);

  params.set("limit", String(PAGE_SIZE));
  if (cursor) {
    params.set("before_ts", cursor.ts);
    params.set("before_id", cursor.id);
  }
  return params.toString();
}

async function fetchPage(cursor: Cursor | null): Promise<AuditResponse | null> {
  const url = "/api/audit-log?" + buildQuery(cursor);
  const resp = await fetch(url);
  if (resp.status === 403) {
    showFeedback(t("admin.audit.error.forbidden") || "Nur Admins.", true);
    return null;
  }
  if (resp.status === 503) {
    showFeedback(t("admin.audit.error.unavailable") || "Audit-Service nicht verfügbar.", true);
    return null;
  }
  if (!resp.ok) {
    const body = await resp.json().catch(() => ({ error: resp.statusText }));
    showFeedback(body.error || resp.statusText, true);
    return null;
  }
  return (await resp.json()) as AuditResponse;
}

async function reload() {
  const tbody = document.getElementById("audit-tbody")!;
  tbody.innerHTML = `<tr><td colspan="6" class="admin-audit-loading">${esc(t("admin.audit.loading") || "Lade ...")}</td></tr>`;
  const resp = await fetchPage(null);
  if (!resp) return;
  entries = resp.entries;
  nextCursor = resp.next_cursor;
  render();
}

async function loadMore() {
  if (!nextCursor) return;
  const btn = document.getElementById("audit-loadmore") as HTMLButtonElement;
  btn.disabled = true;
  btn.textContent = t("admin.audit.loading") || "Lade ...";
  const resp = await fetchPage(nextCursor);
  btn.disabled = false;
  btn.textContent = t("admin.audit.loadmore") || "Weitere laden";
  if (!resp) return;
  entries = entries.concat(resp.entries);
  nextCursor = resp.next_cursor;
  render();
}

function bindFilters() {
  const sourceSel = document.getElementById("audit-source") as HTMLSelectElement;
  const rangeSel = document.getElementById("audit-range") as HTMLSelectElement;
  const fromInput = document.getElementById("audit-from") as HTMLInputElement;
  const toInput = document.getElementById("audit-to") as HTMLInputElement;
  const searchInput = document.getElementById("audit-search") as HTMLInputElement;
  const customWrap = document.getElementById("audit-custom-range")!;

  const onChange = () => { void reload(); };

  sourceSel.addEventListener("change", onChange);
  rangeSel.addEventListener("change", () => {
    customWrap.style.display = rangeSel.value === "custom" ? "" : "none";
    onChange();
  });
  fromInput.addEventListener("change", onChange);
  toInput.addEventListener("change", onChange);

  searchInput.addEventListener("input", () => {
    if (searchDebounce) window.clearTimeout(searchDebounce);
    searchDebounce = window.setTimeout(reload, 250);
  });

  document.getElementById("audit-loadmore")!.addEventListener("click", () => {
    void loadMore();
  });
}

document.addEventListener("DOMContentLoaded", () => {
  initI18n();
  initSidebar();
  bindFilters();
  onLangChange(render);
  void reload();
});
