import { initI18n, t, tDyn, getLang } from "./i18n";
import { initSidebar } from "./sidebar";
import { initNotes } from "./notes";
import { projectIndent } from "./project-indent";
import { openWithdrawWarningModal } from "./components/withdraw-warning-modal";

interface Appointment {
  id: string;
  project_id?: string;
  title: string;
  description?: string;
  start_at: string;
  end_at?: string;
  location?: string;
  appointment_type?: string;
  created_by?: string;
  // t-paliad-138 + t-paliad-160 — pending-approval surface.
  approval_status?: "approved" | "pending" | "legacy";
  pending_request_id?: string | null;
}

interface PendingApprovalRequest {
  id: string;
  status: string;
  requested_by: string;
  requested_at: string;
  required_role: string;
  requester_name?: string;
  // t-paliad-252 — used by the withdraw warning modal to pick the right
  // copy (CREATE warns about deletion; UPDATE/COMPLETE about revert).
  lifecycle_event?: string;
}

interface Me {
  id: string;
}

interface Project {
  id: string;
  reference?: string | null;
  title: string;
  path?: string;
}

let appointment: Appointment | null = null;
let project: Project | null = null;
let allProjects: Project[] = [];
let pendingRequest: PendingApprovalRequest | null = null;
let me: Me | null = null;
// t-paliad-252 — see deadlines-detail.ts. Routes Save to the new
// /api/approval-requests/{id}/edit-entity endpoint when the user picked
// "Termin bearbeiten" in the withdraw warning modal.
let pendingEditMode = false;

function parseAppointmentID(): string | null {
  const parts = window.location.pathname.split("/").filter(Boolean);
  if (parts[0] !== "appointments" || !parts[1]) return null;
  return parts[1];
}

function fmtDateTime(iso: string): string {
  try {
    const d = new Date(iso);
    return d.toLocaleString(getLang() === "de" ? "de-DE" : "en-GB", {
      year: "numeric",
      month: "2-digit",
      day: "2-digit",
      hour: "2-digit",
      minute: "2-digit",
    });
  } catch {
    return iso;
  }
}

function toLocalInput(iso?: string): string {
  if (!iso) return "";
  const d = new Date(iso);
  const pad = (n: number) => String(n).padStart(2, "0");
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
}

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

async function loadAppointment(id: string): Promise<boolean> {
  try {
    const resp = await fetch(`/api/appointments/${id}`);
    if (!resp.ok) return false;
    appointment = await resp.json();
    return true;
  } catch {
    return false;
  }
}

async function loadProject(id: string) {
  try {
    const resp = await fetch(`/api/projects/${id}`);
    if (resp.ok) project = await resp.json();
  } catch {
    /* non-fatal */
  }
}

async function loadAllProjects() {
  try {
    const resp = await fetch("/api/projects");
    if (resp.ok) allProjects = await resp.json();
  } catch {
    /* non-fatal */
  }
}

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

// loadPendingRequest mirrors deadlines-detail.ts (t-paliad-160 §C+E):
// pull the in-flight approval_request when the entity is pending so the
// badge tooltip + the Withdraw button can be wired correctly.
async function loadPendingRequest(): Promise<void> {
  pendingRequest = null;
  if (!appointment || appointment.approval_status !== "pending" || !appointment.pending_request_id) {
    return;
  }
  try {
    const resp = await fetch(`/api/approval-requests/${appointment.pending_request_id}`);
    if (resp.ok) pendingRequest = await resp.json();
  } catch {
    /* non-fatal */
  }
}

function populateProjectPicker() {
  const sel = document.getElementById("appointment-project-edit") as HTMLSelectElement | null;
  if (!sel) return;
  const none = sel.querySelector('option[value=""]');
  sel.innerHTML = "";
  if (none) sel.appendChild(none);
  for (const p of allProjects) {
    const opt = document.createElement("option");
    opt.value = p.id;
    opt.textContent = `${projectIndent(p.path)}${p.reference || ""} — ${p.title}`;
    sel.appendChild(opt);
  }
  if (appointment) {
    sel.value = appointment.project_id ?? "";
  }
}

function renderHeader() {
  if (!appointment) return;
  document.getElementById("appointment-title-display")!.textContent = appointment.title;

  const time = appointment.end_at
    ? `${fmtDateTime(appointment.start_at)} — ${fmtDateTime(appointment.end_at)}`
    : fmtDateTime(appointment.start_at);
  document.getElementById("appointment-time-display")!.textContent = time;

  const badge = document.getElementById("appointment-type-badge")!;
  if (appointment.appointment_type) {
    badge.textContent = tDyn(`appointments.type.${appointment.appointment_type}`) || appointment.appointment_type;
    badge.className = `termin-type-badge termin-type-${appointment.appointment_type}`;
    badge.style.display = "";
  } else {
    badge.style.display = "none";
  }

  const projectRow = document.getElementById("appointment-project-row")!;
  if (appointment.project_id && project) {
    const link = document.getElementById("appointment-project-link") as HTMLAnchorElement;
    link.href = `/projects/${project.id}`;
    link.textContent = `${project.reference || ""} — ${project.title}`;
    projectRow.style.display = "";
  } else {
    projectRow.style.display = "none";
  }

  // t-paliad-160 §C+E — pending-approval badge + withdraw + freeze controls.
  const isPending = appointment.approval_status === "pending";
  const isRequester = !!(me && pendingRequest && me.id === pendingRequest.requested_by);
  const apBadge = document.getElementById("appointment-pending-approval-badge") as HTMLElement | null;
  if (apBadge) {
    if (isPending) {
      apBadge.style.display = "";
      const labelDe = t("approvals.pending.badge") || "Wartet auf Genehmigung";
      apBadge.textContent = labelDe;
      if (pendingRequest) {
        const role = tDyn(`approvals.required_role.${pendingRequest.required_role}`) || pendingRequest.required_role;
        const requester = pendingRequest.requester_name || pendingRequest.requested_by;
        const when = fmtDateTime(pendingRequest.requested_at);
        apBadge.title = `${labelDe} · ${role}+ · ${requester} · ${when}`;
      } else {
        apBadge.title = labelDe;
      }
    } else {
      apBadge.style.display = "none";
      apBadge.title = "";
    }
  }

  const withdrawBtn = document.getElementById("appointment-withdraw-btn") as HTMLButtonElement | null;
  if (withdrawBtn) {
    withdrawBtn.style.display = (isPending && isRequester) ? "" : "none";
    withdrawBtn.disabled = false;
  }

  // Freeze the edit form + delete button while a request is in flight.
  // t-paliad-252 — when the user picked "Termin bearbeiten" in the
  // withdraw modal, pendingEditMode unfreezes the form so Save can route
  // to /edit-entity (which keeps the request pending + merges payload).
  const form = document.getElementById("appointment-edit-form") as HTMLFormElement | null;
  if (form) {
    const freeze = isPending && !pendingEditMode;
    form.querySelectorAll<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | HTMLButtonElement>("input, select, textarea, button[type=submit]")
      .forEach((el) => { el.disabled = freeze; });
  }
  const deleteBtn = document.getElementById("appointment-delete-btn") as HTMLButtonElement | null;
  if (deleteBtn) deleteBtn.disabled = isPending;
}

function fillEditForm() {
  if (!appointment) return;
  (document.getElementById("appointment-title-edit") as HTMLInputElement).value = appointment.title;
  (document.getElementById("appointment-start-edit") as HTMLInputElement).value = toLocalInput(appointment.start_at);
  (document.getElementById("appointment-end-edit") as HTMLInputElement).value = toLocalInput(appointment.end_at);
  (document.getElementById("appointment-type-edit") as HTMLSelectElement).value = appointment.appointment_type ?? "";
  (document.getElementById("appointment-location-edit") as HTMLInputElement).value = appointment.location ?? "";
  (document.getElementById("appointment-description-edit") as HTMLTextAreaElement).value = appointment.description ?? "";
  const projectSel = document.getElementById("appointment-project-edit") as HTMLSelectElement | null;
  if (projectSel) projectSel.value = appointment.project_id ?? "";
}

async function saveEdit(ev: Event) {
  ev.preventDefault();
  if (!appointment) return;
  const msg = document.getElementById("appointment-edit-msg")!;
  const submitBtn = (ev.target as HTMLFormElement).querySelector<HTMLButtonElement>("button[type=submit]")!;
  msg.textContent = "";

  const title = (document.getElementById("appointment-title-edit") as HTMLInputElement).value.trim();
  const startRaw = (document.getElementById("appointment-start-edit") as HTMLInputElement).value;
  const endRaw = (document.getElementById("appointment-end-edit") as HTMLInputElement).value;
  const type = (document.getElementById("appointment-type-edit") as HTMLSelectElement).value;
  const location = (document.getElementById("appointment-location-edit") as HTMLInputElement).value.trim();
  const description = (document.getElementById("appointment-description-edit") as HTMLTextAreaElement).value;
  const projectSel = document.getElementById("appointment-project-edit") as HTMLSelectElement | null;
  const newProjectID = projectSel ? projectSel.value : "";
  const currentProjectID = appointment.project_id ?? "";

  const payload: Record<string, unknown> = {
    title,
    start_at: new Date(startRaw).toISOString(),
    end_at: endRaw ? new Date(endRaw).toISOString() : null,
    appointment_type: type,
    location,
    description,
  };
  if (newProjectID !== currentProjectID) {
    if (newProjectID === "") {
      payload.clear_project = true;
    } else {
      payload.project_id = newProjectID;
    }
  }

  submitBtn.disabled = true;
  try {
    // t-paliad-252 — pending-edit mode routes through /edit-entity which
    // keeps the request pending + merges fields into payload. clear_project
    // and project_id are NOT in the counter-allowlist (yet) — the requester
    // can't move projects on a pending request from this surface.
    if (pendingEditMode && pendingRequest) {
      const editFields = { ...payload };
      delete editFields.clear_project;
      const resp = await fetch(
        `/api/approval-requests/${pendingRequest.id}/edit-entity`,
        {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ fields: editFields }),
        },
      );
      if (resp.ok) {
        const fresh = await fetch(`/api/appointments/${appointment.id}`);
        if (fresh.ok) appointment = await fresh.json();
        await loadPendingRequest();
        // Exit pending-edit mode so the form re-freezes (still pending).
        pendingEditMode = false;
        renderHeader();
        fillEditForm();
        msg.textContent = t("appointments.detail.saved");
        msg.className = "form-msg form-msg-ok";
      } else {
        const data = await resp.json().catch(() => ({}) as { error?: string; message?: string });
        msg.textContent = data.message || data.error || t("appointments.error.generic");
        msg.className = "form-msg form-msg-error";
      }
      return;
    }

    const resp = await fetch(`/api/appointments/${appointment.id}`, {
      method: "PATCH",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(payload),
    });
    if (resp.ok) {
      const prevProjectID = appointment.project_id ?? "";
      appointment = await resp.json();
      const nextProjectID = appointment?.project_id ?? "";
      if (nextProjectID !== prevProjectID) {
        project = null;
        if (appointment?.project_id) await loadProject(appointment.project_id);
      }
      renderHeader();
      msg.textContent = t("appointments.detail.saved");
      msg.className = "form-msg form-msg-ok";
    } else {
      const data = await resp.json().catch(() => ({}) as { error?: string });
      msg.textContent = data.error || t("appointments.error.generic");
      msg.className = "form-msg form-msg-error";
    }
  } catch {
    msg.textContent = t("appointments.error.generic");
    msg.className = "form-msg form-msg-error";
  } finally {
    submitBtn.disabled = false;
  }
}

async function deleteAppointment() {
  if (!appointment) return;
  if (!confirm(t("appointments.detail.delete.confirm"))) return;
  try {
    const resp = await fetch(`/api/appointments/${appointment.id}`, { method: "DELETE" });
    if (resp.ok || resp.status === 204) {
      window.location.href = "/events?type=appointment";
    } else {
      const data = await resp.json().catch(() => ({}) as { error?: string; message?: string });
      const msg = document.getElementById("appointment-edit-msg")!;
      msg.textContent = data.message || data.error || t("appointments.error.generic");
      msg.className = "form-msg form-msg-error";
    }
  } catch {
    const msg = document.getElementById("appointment-edit-msg")!;
    msg.textContent = t("appointments.error.generic");
    msg.className = "form-msg form-msg-error";
  }
}

// t-paliad-252 — withdraw warning modal replaces the old confirm().
// Returns:
//   "edit"     → unfreeze the edit form (pending-edit mode); Save will
//                route through /api/approval-requests/{id}/edit-entity
//   "withdraw" → destructive: the existing /revoke endpoint
//   null       → user cancelled
async function withdrawAppointmentRequest() {
  if (!appointment || !pendingRequest) return;
  const btn = document.getElementById("appointment-withdraw-btn") as HTMLButtonElement | null;
  if (btn) btn.disabled = true;
  try {
    const action = await openWithdrawWarningModal({
      entityType: "appointment",
      lifecycleEvent: pendingRequest.lifecycle_event ?? "create",
    });
    if (action === null) {
      if (btn) btn.disabled = false;
      return;
    }
    if (action === "edit") {
      pendingEditMode = true;
      if (btn) btn.disabled = false;
      // renderHeader re-evaluates the freeze and unfreezes the form now
      // that pendingEditMode is set. Focus the first editable field so the
      // user can type immediately.
      renderHeader();
      const titleEl = document.getElementById("appointment-title-edit") as HTMLInputElement | null;
      titleEl?.focus();
      return;
    }
    // action === "withdraw" → destructive path.
    const resp = await fetch(`/api/approval-requests/${pendingRequest.id}/revoke`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({}),
    });
    if (resp.ok) {
      const fresh = await fetch(`/api/appointments/${appointment.id}`);
      if (fresh.ok) {
        appointment = await fresh.json();
        await loadPendingRequest();
        renderHeader();
        fillEditForm();
      } else {
        // CREATE lifecycle: entity gone → back to the list.
        window.location.href = "/events?type=appointment";
      }
    } else {
      const data = await resp.json().catch(() => ({}) as { message?: string; error?: string });
      const msg = document.getElementById("appointment-edit-msg")!;
      msg.textContent = data.message || data.error || (t("approvals.withdraw.error") || "Fehler beim Zurückziehen");
      msg.className = "form-msg form-msg-error";
      if (btn) btn.disabled = false;
    }
  } catch (e) {
    const msg = document.getElementById("appointment-edit-msg")!;
    msg.textContent = (t("approvals.withdraw.error") || "Fehler beim Zurückziehen") + ": " + e;
    msg.className = "form-msg form-msg-error";
    if (btn) btn.disabled = false;
  }
}

async function main() {
  const id = parseAppointmentID();
  const loading = document.getElementById("appointment-loading")!;
  const body = document.getElementById("appointment-body")!;
  const notFound = document.getElementById("appointment-not-found")!;
  if (!id) {
    loading.style.display = "none";
    notFound.style.display = "block";
    return;
  }
  const ok = await loadAppointment(id);
  if (!ok || !appointment) {
    loading.style.display = "none";
    notFound.style.display = "block";
    return;
  }
  await Promise.all([
    appointment.project_id ? loadProject(appointment.project_id) : Promise.resolve(),
    loadAllProjects(),
    loadMe(),
    loadPendingRequest(),
  ]);
  loading.style.display = "none";
  body.style.display = "";
  renderHeader();
  populateProjectPicker();
  fillEditForm();

  document.getElementById("appointment-edit-form")!.addEventListener("submit", saveEdit);
  document.getElementById("appointment-delete-btn")!.addEventListener("click", deleteAppointment);
  const withdrawBtn = document.getElementById("appointment-withdraw-btn");
  if (withdrawBtn) withdrawBtn.addEventListener("click", () => void withdrawAppointmentRequest());

  const notes = document.getElementById("notes-container");
  if (notes) {
    notes.setAttribute("data-parent-id", id);
    void initNotes(notes as HTMLElement, "appointment", id);
  }
}

document.addEventListener("DOMContentLoaded", () => {
  initI18n();
  initSidebar();
  main();
});
