import { t, tDyn, getLang, type I18nKey } from "../i18n";

// mount-calendar.ts — the canonical month/week/day calendar (t-paliad-224).
// Lifted from the original shape-calendar.ts so both Custom Views
// (shape=calendar) and /events Kalender tab render through the same DOM.
// See docs/design-calendar-view-align-2026-05-20.md for the audit + plan.
//
// Surfaces wire in via mountCalendar(host, items, opts). The returned
// handle exposes update(items) for re-render after a filter change and
// destroy() for teardown when the host swaps to a different view.

export type CalendarKind =
  | "deadline" | "appointment" | "project_event" | "approval_request";

export interface CalendarItem {
  kind: CalendarKind;
  id: string;
  title: string;
  /** ISO-8601 timestamp or date string. First 10 chars are read as the
   *  calendar bucket (yyyy-mm-dd). */
  event_date: string;
  project_id?: string;
  project_title?: string;
  project_reference?: string;
}

export type CalendarView = "month" | "week" | "day";

export interface CalendarOpts {
  /** Initial view if URL has no override (or urlState is disabled). */
  defaultView?: CalendarView;
  /** Read/write ?cal_view + ?cal_date so a refresh restores the calendar.
   *  Surfaces that own their own URL contract pass urlState=false. */
  urlState?: boolean;
  /** Optional URL param prefix (e.g. "events" → ?eventsCalView=…). Only
   *  meaningful when urlState=true. Leave empty for the default
   *  ?cal_view / ?cal_date contract. */
  urlPrefix?: string;
  /** Override how a row's href is built. Default routes by kind. */
  hrefFor?: (item: CalendarItem) => string;
}

export interface CalendarHandle {
  /** Replace the item set and re-paint at the current view+anchor. */
  update(items: CalendarItem[]): void;
  /** Clear host + drop the keep-alive state. After destroy(), the handle
   *  is dead; create a fresh one with mountCalendar(). */
  destroy(): void;
}

const MAX_PILLS_PER_MONTH_CELL = 3;

export function mountCalendar(
  host: HTMLElement,
  initialItems: CalendarItem[],
  opts: CalendarOpts = {},
): CalendarHandle {
  let items = initialItems;
  let view: CalendarView;
  let anchor: Date;
  let destroyed = false;

  const urlEnabled = opts.urlState ?? false;
  const viewParam = urlEnabled ? paramName(opts.urlPrefix, "cal_view") : "";
  const dateParam = urlEnabled ? paramName(opts.urlPrefix, "cal_date") : "";

  view = urlEnabled
    ? readView(viewParam, opts.defaultView ?? "month")
    : (opts.defaultView ?? "month");
  anchor = urlEnabled ? readAnchor(dateParam, items) : firstAnchor(items);

  paint();

  return {
    update(nextItems) {
      if (destroyed) return;
      items = nextItems;
      paint();
    },
    destroy() {
      destroyed = true;
      host.innerHTML = "";
    },
  };

  // --- paint -----------------------------------------------------------

  function paint(): void {
    if (destroyed) return;
    host.innerHTML = "";

    // Mobile fallback notice (<600px). Documented in design-calendar-
    // view-align-2026-05-20.md §6. CSS still lays out the grid; the
    // notice just nudges users toward a friendlier view.
    if (typeof window !== "undefined" && window.innerWidth < 600) {
      const notice = document.createElement("p");
      notice.className = "views-calendar-mobile-notice";
      notice.textContent = t("views.calendar.mobile_fallback");
      host.appendChild(notice);
    }

    const wrap = document.createElement("div");
    wrap.className = `views-calendar views-calendar--${view}`;
    wrap.appendChild(renderToolbar());
    if (view === "month") {
      wrap.appendChild(renderMonth());
    } else if (view === "week") {
      wrap.appendChild(renderWeek());
    } else {
      wrap.appendChild(renderDay());
    }
    host.appendChild(wrap);
  }

  function setView(nextView: CalendarView, nextAnchor: Date): void {
    view = nextView;
    anchor = nextAnchor;
    if (urlEnabled) writeURL(viewParam, dateParam, nextView, nextAnchor);
    paint();
  }

  // --- Toolbar ---------------------------------------------------------

  function renderToolbar(): HTMLElement {
    const bar = document.createElement("div");
    bar.className = "views-calendar-toolbar";

    const switcher = document.createElement("div");
    switcher.className = "views-calendar-view-switcher agenda-chip-row";
    switcher.setAttribute("role", "tablist");
    for (const v of ["month", "week", "day"] as CalendarView[]) {
      const chip = document.createElement("button");
      chip.type = "button";
      chip.className = "agenda-chip views-calendar-view-chip" + (v === view ? " agenda-chip-active" : "");
      chip.dataset.calView = v;
      chip.setAttribute("role", "tab");
      chip.setAttribute("aria-selected", v === view ? "true" : "false");
      chip.textContent = t(`cal.view.${v}` as I18nKey);
      chip.addEventListener("click", () => {
        if (v === view) return;
        setView(v, anchor);
      });
      switcher.appendChild(chip);
    }
    bar.appendChild(switcher);

    const nav = document.createElement("div");
    nav.className = "views-calendar-nav";

    const prev = document.createElement("button");
    prev.type = "button";
    prev.className = "btn-secondary btn-small views-calendar-nav-btn";
    prev.setAttribute("aria-label", t(navLabelKey(view, "prev")));
    prev.textContent = "‹";
    prev.addEventListener("click", () => setView(view, shift(anchor, view, -1)));
    nav.appendChild(prev);

    const label = document.createElement("span");
    label.className = "views-calendar-nav-label";
    label.textContent = formatRangeLabel(view, anchor);
    nav.appendChild(label);

    const next = document.createElement("button");
    next.type = "button";
    next.className = "btn-secondary btn-small views-calendar-nav-btn";
    next.setAttribute("aria-label", t(navLabelKey(view, "next")));
    next.textContent = "›";
    next.addEventListener("click", () => setView(view, shift(anchor, view, 1)));
    nav.appendChild(next);

    // "Heute" button — jump back to today in the current view. Adds a
    // recognisable affordance for the /events Kalender users who relied
    // on the old toolbar's "Heute" button.
    const today = document.createElement("button");
    today.type = "button";
    today.className = "btn-secondary btn-small views-calendar-nav-btn";
    today.textContent = t("cal.today");
    today.addEventListener("click", () => setView(view, startOfDay(new Date())));
    nav.appendChild(today);

    if (view !== "month") {
      const backToMonth = document.createElement("button");
      backToMonth.type = "button";
      backToMonth.className = "btn-link views-calendar-back-to-month";
      backToMonth.textContent = t("cal.day.back_to_month");
      backToMonth.addEventListener("click", () => setView("month", anchor));
      nav.appendChild(backToMonth);
    }

    bar.appendChild(nav);
    return bar;
  }

  // --- Month -----------------------------------------------------------

  function renderMonth(): HTMLElement {
    const lang = getLang() === "de" ? "de-DE" : "en-GB";
    const wrap = document.createElement("div");
    wrap.className = "views-calendar-month";

    const header = document.createElement("h2");
    header.className = "views-calendar-month-label";
    header.textContent = anchor.toLocaleDateString(lang, { month: "long", year: "numeric" });
    wrap.appendChild(header);

    const grid = document.createElement("div");
    grid.className = "views-calendar-grid";

    const weekdayKeys: I18nKey[] = [
      "cal.day.mon", "cal.day.tue", "cal.day.wed", "cal.day.thu",
      "cal.day.fri", "cal.day.sat", "cal.day.sun",
    ];
    for (const k of weekdayKeys) {
      const cell = document.createElement("div");
      cell.className = "views-calendar-weekday";
      cell.textContent = t(k);
      grid.appendChild(cell);
    }

    const monthStart = new Date(anchor.getFullYear(), anchor.getMonth(), 1);
    const startWeekday = (monthStart.getDay() + 6) % 7; // Mon=0
    const daysInMonth = new Date(anchor.getFullYear(), anchor.getMonth() + 1, 0).getDate();

    for (let i = 0; i < startWeekday; i++) {
      const cell = document.createElement("div");
      cell.className = "views-calendar-cell views-calendar-cell--out";
      grid.appendChild(cell);
    }

    const byDate = bucketByDate(items, (d) =>
      d.getMonth() === anchor.getMonth() && d.getFullYear() === anchor.getFullYear(),
    );

    for (let day = 1; day <= daysInMonth; day++) {
      const dayDate = new Date(anchor.getFullYear(), anchor.getMonth(), day);
      const dateKey = isoDate(dayDate);
      const dayRows = byDate.get(dateKey) ?? [];
      grid.appendChild(renderMonthCell(dayDate, day, dayRows));
    }

    wrap.appendChild(grid);
    return wrap;
  }

  function renderMonthCell(dayDate: Date, dayNum: number, dayRows: CalendarItem[]): HTMLElement {
    const cell = document.createElement("div");
    cell.className = "views-calendar-cell";
    if (isToday(dayDate)) cell.classList.add("views-calendar-cell--today");
    if (dayRows.length > 0) cell.classList.add("views-calendar-cell--has");

    const dayLabel = document.createElement("button");
    dayLabel.type = "button";
    dayLabel.className = "views-calendar-cell-day";
    dayLabel.textContent = String(dayNum);
    dayLabel.setAttribute("aria-label", t("cal.day.open_day"));
    dayLabel.addEventListener("click", (e) => {
      e.stopPropagation();
      setView("day", dayDate);
    });
    cell.appendChild(dayLabel);

    if (dayRows.length > 0) {
      const ul = document.createElement("ul");
      ul.className = "views-calendar-pills";
      const visible = dayRows.slice(0, MAX_PILLS_PER_MONTH_CELL);
      for (const row of visible) ul.appendChild(renderPill(row));
      if (dayRows.length > visible.length) {
        const more = document.createElement("li");
        const moreBtn = document.createElement("button");
        moreBtn.type = "button";
        moreBtn.className = "views-calendar-pill views-calendar-pill--more";
        moreBtn.textContent = `+${dayRows.length - visible.length}`;
        moreBtn.setAttribute("aria-label", t("cal.day.open_day"));
        moreBtn.addEventListener("click", (e) => {
          e.stopPropagation();
          setView("day", dayDate);
        });
        more.appendChild(moreBtn);
        ul.appendChild(more);
      }
      cell.appendChild(ul);
    }
    return cell;
  }

  // --- Week ------------------------------------------------------------

  function renderWeek(): HTMLElement {
    const lang = getLang() === "de" ? "de-DE" : "en-GB";
    const wrap = document.createElement("div");
    wrap.className = "views-calendar-week";

    const weekStart = startOfWeek(anchor);
    const weekEnd = new Date(weekStart);
    weekEnd.setDate(weekStart.getDate() + 6);

    const header = document.createElement("h2");
    header.className = "views-calendar-month-label";
    header.textContent = formatWeekHeader(weekStart, weekEnd, lang);
    wrap.appendChild(header);

    const grid = document.createElement("div");
    grid.className = "views-calendar-week-grid";
    for (let i = 0; i < 7; i++) {
      const day = new Date(weekStart);
      day.setDate(weekStart.getDate() + i);
      grid.appendChild(renderWeekColumn(day));
    }
    wrap.appendChild(grid);
    return wrap;
  }

  function renderWeekColumn(day: Date): HTMLElement {
    const lang = getLang() === "de" ? "de-DE" : "en-GB";
    const col = document.createElement("div");
    col.className = "views-calendar-week-column";
    if (isToday(day)) col.classList.add("views-calendar-week-column--today");

    const head = document.createElement("div");
    head.className = "views-calendar-week-head";
    const weekdayKey = WEEKDAY_KEYS[(day.getDay() + 6) % 7];
    const dow = document.createElement("span");
    dow.className = "views-calendar-week-dow";
    dow.textContent = t(weekdayKey);
    const dnum = document.createElement("span");
    dnum.className = "views-calendar-week-dnum";
    dnum.textContent = day.toLocaleDateString(lang, { day: "numeric", month: "short" });
    head.appendChild(dow);
    head.appendChild(dnum);
    col.appendChild(head);

    const dayRows = filterByDay(items, day);
    if (dayRows.length === 0) {
      const empty = document.createElement("p");
      empty.className = "views-calendar-week-empty";
      empty.textContent = t("cal.day.no_entries");
      col.appendChild(empty);
      return col;
    }

    const ul = document.createElement("ul");
    ul.className = "views-calendar-week-list";
    for (const row of dayRows) {
      const li = document.createElement("li");
      li.appendChild(renderRowAnchor(row, "week"));
      ul.appendChild(li);
    }
    col.appendChild(ul);
    return col;
  }

  // --- Day -------------------------------------------------------------

  function renderDay(): HTMLElement {
    const lang = getLang() === "de" ? "de-DE" : "en-GB";
    const wrap = document.createElement("div");
    wrap.className = "views-calendar-day-wrap";

    const header = document.createElement("h2");
    header.className = "views-calendar-month-label";
    header.textContent = anchor.toLocaleDateString(lang, {
      weekday: "long", year: "numeric", month: "long", day: "numeric",
    });
    wrap.appendChild(header);

    const dayRows = filterByDay(items, anchor);
    if (dayRows.length === 0) {
      const empty = document.createElement("p");
      empty.className = "views-calendar-day-empty";
      empty.textContent = t("cal.day.no_entries");
      wrap.appendChild(empty);
      return wrap;
    }

    const ul = document.createElement("ul");
    ul.className = "views-calendar-day-list";
    for (const row of dayRows) {
      const li = document.createElement("li");
      li.appendChild(renderRowAnchor(row, "day"));
      ul.appendChild(li);
    }
    wrap.appendChild(ul);
    return wrap;
  }

  // --- Row rendering ---------------------------------------------------

  function renderPill(row: CalendarItem): HTMLElement {
    const li = document.createElement("li");
    const a = document.createElement("a");
    a.className = `views-calendar-pill views-calendar-pill--${row.kind}`;
    a.href = hrefFor(row);
    a.textContent = row.title;
    a.title = row.title + (row.project_title ? ` — ${row.project_title}` : "");
    a.addEventListener("click", (e) => e.stopPropagation());
    li.appendChild(a);
    return li;
  }

  function renderRowAnchor(row: CalendarItem, density: "week" | "day"): HTMLElement {
    const a = document.createElement("a");
    a.className = `views-calendar-row views-calendar-row--${density} views-calendar-row--${row.kind}`;
    a.href = hrefFor(row);

    const dot = document.createElement("span");
    dot.className = `views-calendar-row-dot views-calendar-row-dot--${row.kind}`;
    a.appendChild(dot);

    const body = document.createElement("span");
    body.className = "views-calendar-row-body";

    const title = document.createElement("span");
    title.className = "views-calendar-row-title";
    title.textContent = row.title;
    body.appendChild(title);

    const metaParts: string[] = [];
    metaParts.push(tDyn("views.kind." + row.kind));
    if (row.project_reference) metaParts.push(row.project_reference);
    else if (row.project_title) metaParts.push(row.project_title);
    if (metaParts.length > 0) {
      const meta = document.createElement("span");
      meta.className = "views-calendar-row-meta";
      meta.textContent = metaParts.join(" · ");
      body.appendChild(meta);
    }

    a.appendChild(body);
    return a;
  }

  function hrefFor(row: CalendarItem): string {
    if (opts.hrefFor) return opts.hrefFor(row);
    return defaultHrefFor(row);
  }
}

// --- Pure helpers (shared, not closure-bound) ----------------------------

const WEEKDAY_KEYS: I18nKey[] = [
  "cal.day.mon", "cal.day.tue", "cal.day.wed", "cal.day.thu",
  "cal.day.fri", "cal.day.sat", "cal.day.sun",
];

function navLabelKey(view: CalendarView, dir: "prev" | "next"): I18nKey {
  if (view === "month") return dir === "prev" ? "cal.month.prev" : "cal.month.next";
  if (view === "week")  return dir === "prev" ? "cal.week.prev"  : "cal.week.next";
  return dir === "prev" ? "cal.day.prev" : "cal.day.next";
}

function defaultHrefFor(row: CalendarItem): string {
  switch (row.kind) {
    case "deadline":         return `/deadlines/${encodeURIComponent(row.id)}`;
    case "appointment":      return `/appointments/${encodeURIComponent(row.id)}`;
    case "approval_request": return `/inbox`;
    case "project_event":    return row.project_id ? `/projects/${encodeURIComponent(row.project_id)}` : "#";
  }
}

export function bucketByDate(
  rows: CalendarItem[], filter: (d: Date) => boolean,
): Map<string, CalendarItem[]> {
  const out = new Map<string, CalendarItem[]>();
  for (const row of rows) {
    const d = new Date(row.event_date);
    if (isNaN(d.getTime())) continue;
    if (!filter(d)) continue;
    const key = isoDate(d);
    const arr = out.get(key);
    if (arr) arr.push(row);
    else out.set(key, [row]);
  }
  return out;
}

export function filterByDay(rows: CalendarItem[], day: Date): CalendarItem[] {
  const key = isoDate(day);
  return rows.filter((r) => {
    const d = new Date(r.event_date);
    if (isNaN(d.getTime())) return false;
    return isoDate(d) === key;
  });
}

export function startOfWeek(d: Date): Date {
  const out = new Date(d.getFullYear(), d.getMonth(), d.getDate());
  const offset = (out.getDay() + 6) % 7;
  out.setDate(out.getDate() - offset);
  return out;
}

export function startOfDay(d: Date): Date {
  return new Date(d.getFullYear(), d.getMonth(), d.getDate());
}

export function shift(d: Date, view: CalendarView, dir: number): Date {
  if (view === "month") return new Date(d.getFullYear(), d.getMonth() + dir, 1);
  if (view === "week")  return new Date(d.getFullYear(), d.getMonth(), d.getDate() + dir * 7);
  return new Date(d.getFullYear(), d.getMonth(), d.getDate() + dir);
}

export function isToday(d: Date): boolean {
  const now = new Date();
  return d.getFullYear() === now.getFullYear()
    && d.getMonth() === now.getMonth()
    && d.getDate() === now.getDate();
}

export function isoDate(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}`;
}

function formatRangeLabel(view: CalendarView, anchor: Date): string {
  const lang = getLang() === "de" ? "de-DE" : "en-GB";
  if (view === "month") {
    return anchor.toLocaleDateString(lang, { month: "long", year: "numeric" });
  }
  if (view === "week") {
    const start = startOfWeek(anchor);
    const end = new Date(start);
    end.setDate(start.getDate() + 6);
    return formatWeekHeader(start, end, lang);
  }
  return anchor.toLocaleDateString(lang, {
    weekday: "short", year: "numeric", month: "long", day: "numeric",
  });
}

function formatWeekHeader(start: Date, end: Date, lang: string): string {
  const startStr = start.toLocaleDateString(lang, { day: "numeric", month: "short" });
  const endStr = end.toLocaleDateString(lang, { day: "numeric", month: "short", year: "numeric" });
  return `${startStr} – ${endStr}`;
}

function firstAnchor(rows: CalendarItem[]): Date {
  for (const row of rows) {
    const d = new Date(row.event_date);
    if (!isNaN(d.getTime())) return startOfDay(d);
  }
  return startOfDay(new Date());
}

function paramName(prefix: string | undefined, base: string): string {
  if (!prefix) return base;
  return `${prefix}_${base}`;
}

function readView(viewParam: string, fallback: CalendarView): CalendarView {
  if (typeof window === "undefined") return fallback;
  const params = new URLSearchParams(window.location.search);
  const raw = params.get(viewParam);
  if (raw === "month" || raw === "week" || raw === "day") return raw;
  return fallback;
}

function readAnchor(dateParam: string, rows: CalendarItem[]): Date {
  if (typeof window === "undefined") return firstAnchor(rows);
  const params = new URLSearchParams(window.location.search);
  const raw = params.get(dateParam);
  if (raw) {
    const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw);
    if (m) {
      const d = new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]));
      if (!isNaN(d.getTime())) return d;
    }
  }
  return firstAnchor(rows);
}

function writeURL(viewParam: string, dateParam: string, view: CalendarView, anchor: Date): void {
  if (typeof window === "undefined") return;
  const url = new URL(window.location.href);
  url.searchParams.set(viewParam, view);
  url.searchParams.set(dateParam, isoDate(anchor));
  history.replaceState(null, "", url.toString());
}
