import { initI18n, onLangChange } from "./i18n";
import { initSidebar } from "./sidebar";
import { attachEventTypeMultiSelectFilter, type FilterHandle } from "./event-types";
import { renderAgendaTimeline, type AgendaItem } from "./agenda-render";

let eventTypeFilter: FilterHandle | null = null;

type TypeFilter = "both" | "deadlines" | "appointments";

interface AgendaPayload {
  items: AgendaItem[];
  from: string;
  to: string;
  types: string[];
}

declare global {
  interface Window {
    __PALIAD_AGENDA__?: AgendaPayload | null;
  }
}

// Range presets match the TSX chips; 30d stays the default (server agrees).
const RANGE_DAYS_DEFAULT = 30;
const VALID_RANGES = new Set([7, 14, 30, 90]);

const state = {
  items: [] as AgendaItem[],
  type: "both" as TypeFilter,
  rangeDays: RANGE_DAYS_DEFAULT,
};

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

  const inlined = window.__PALIAD_AGENDA__;
  if (inlined !== undefined) {
    if (inlined === null) {
      showUnavailable();
    } else {
      hydrate(inlined);
    }
  } else {
    void refetch();
  }

  wireControls();
  onLangChange(() => render());
});

// Pull initial state from ?types=...&range=... so reloads and bookmarks work.
// Any deviation triggers a refetch via wireControls once the UI is ready.
function readInitialStateFromURL(): void {
  const q = new URLSearchParams(window.location.search);
  const typesRaw = q.get("types");
  if (typesRaw) {
    const set = new Set(typesRaw.split(",").map((s) => s.trim()));
    const hasD = set.has("deadlines");
    const hasA = set.has("appointments");
    if (hasD && !hasA) state.type = "deadlines";
    else if (hasA && !hasD) state.type = "appointments";
    else state.type = "both";
  }
  const rangeRaw = q.get("range");
  if (rangeRaw) {
    const n = parseInt(rangeRaw, 10);
    if (!isNaN(n) && VALID_RANGES.has(n)) state.rangeDays = n;
  }
}

function hydrate(payload: AgendaPayload): void {
  state.items = payload.items;
  // Infer type filter from server payload when the URL didn't pin it.
  if (!window.location.search.includes("types=")) {
    const set = new Set(payload.types);
    if (set.has("deadlines") && !set.has("appointments")) state.type = "deadlines";
    else if (set.has("appointments") && !set.has("deadlines")) state.type = "appointments";
    else state.type = "both";
  }
  render();
}

function wireControls(): void {
  document.querySelectorAll<HTMLButtonElement>(".agenda-chip[data-type]").forEach((btn) => {
    btn.addEventListener("click", () => {
      const next = (btn.dataset.type || "both") as TypeFilter;
      if (state.type === next) return;
      state.type = next;
      pushURL();
      void refetch();
    });
  });
  document.querySelectorAll<HTMLButtonElement>(".agenda-chip[data-range]").forEach((btn) => {
    btn.addEventListener("click", () => {
      const next = parseInt(btn.dataset.range || "30", 10);
      if (!VALID_RANGES.has(next) || state.rangeDays === next) return;
      state.rangeDays = next;
      pushURL();
      void refetch();
    });
  });
  syncChips();

  const eventTrigger = document.getElementById("agenda-filter-event-type") as HTMLButtonElement | null;
  const eventPanel = document.getElementById("agenda-filter-event-type-panel") as HTMLElement | null;
  if (eventTrigger && eventPanel) {
    const q = new URLSearchParams(window.location.search);
    const initialEventIDs: string[] = [];
    let initialIncludeUntyped = false;
    const raw = q.get("event_type") ?? "";
    if (raw) {
      for (const tok of raw.split(",")) {
        const t = tok.trim();
        if (!t) continue;
        if (t === "none") initialIncludeUntyped = true;
        else initialEventIDs.push(t);
      }
    }
    eventTypeFilter = attachEventTypeMultiSelectFilter(eventTrigger, eventPanel, {
      initialIDs: initialEventIDs,
      initialIncludeUntyped,
      onChange: () => {
        pushURL();
        void refetch();
      },
    });
  }
}

function pushURL(): void {
  const q = new URLSearchParams(window.location.search);
  q.set("range", String(state.rangeDays));
  q.set("types", typesParam(state.type));
  const eventQuery = eventTypeFilter?.toQueryValue() ?? "";
  if (eventQuery) q.set("event_type", eventQuery);
  else q.delete("event_type");
  history.replaceState(null, "", `${window.location.pathname}?${q.toString()}`);
}

function typesParam(tf: TypeFilter): string {
  if (tf === "deadlines") return "deadlines";
  if (tf === "appointments") return "appointments";
  return "deadlines,appointments";
}

async function refetch(): Promise<void> {
  const loading = document.getElementById("agenda-loading")!;
  const timeline = document.getElementById("agenda-timeline")!;
  const empty = document.getElementById("agenda-empty")!;
  loading.style.display = "block";
  timeline.style.display = "none";
  empty.style.display = "none";
  syncChips();

  const from = toISODate(startOfToday());
  const to = toISODate(addDays(startOfToday(), state.rangeDays - 1));
  const eventQuery = eventTypeFilter?.toQueryValue() ?? "";
  const eventParam = eventQuery ? `&event_type=${encodeURIComponent(eventQuery)}` : "";
  const url = `/api/agenda?from=${from}&to=${to}&types=${typesParam(state.type)}${eventParam}`;
  try {
    const resp = await fetch(url);
    if (resp.status === 503) {
      showUnavailable();
      return;
    }
    if (!resp.ok) throw new Error(`status ${resp.status}`);
    state.items = (await resp.json()) as AgendaItem[];
    render();
  } catch {
    showUnavailable();
  } finally {
    loading.style.display = "none";
  }
}

function showUnavailable(): void {
  document.getElementById("agenda-unavailable")!.style.display = "block";
  document.getElementById("agenda-timeline")!.style.display = "none";
  document.getElementById("agenda-empty")!.style.display = "none";
}

function render(): void {
  syncChips();
  const timeline = document.getElementById("agenda-timeline")!;
  const empty = document.getElementById("agenda-empty")!;

  if (!state.items.length) {
    timeline.innerHTML = "";
    timeline.style.display = "none";
    empty.style.display = "block";
    return;
  }
  empty.style.display = "none";
  timeline.style.display = "";
  timeline.innerHTML = renderAgendaTimeline(state.items);
}

function syncChips(): void {
  document.querySelectorAll<HTMLButtonElement>(".agenda-chip[data-type]").forEach((btn) => {
    btn.classList.toggle("agenda-chip-active", btn.dataset.type === state.type);
  });
  document.querySelectorAll<HTMLButtonElement>(".agenda-chip[data-range]").forEach((btn) => {
    btn.classList.toggle("agenda-chip-active", btn.dataset.range === String(state.rangeDays));
  });
}

function startOfToday(): Date {
  const d = new Date();
  d.setHours(0, 0, 0, 0);
  return d;
}

function addDays(d: Date, days: number): Date {
  const r = new Date(d);
  r.setDate(r.getDate() + days);
  return r;
}

function toISODate(d: Date): string {
  const y = d.getFullYear();
  const m = String(d.getMonth() + 1).padStart(2, "0");
  const day = String(d.getDate()).padStart(2, "0");
  return `${y}-${m}-${day}`;
}
