// Shared agenda timeline rendering primitives. The standalone /agenda page
// (client/agenda.ts) and the inline Agenda section on /dashboard
// (client/dashboard.ts) both render the same item shape; this module is
// the single source of truth for how an AgendaItem turns into HTML.
//
// Stateless. The caller fetches /api/agenda, hands the items to
// renderAgendaTimeline(), and drops the resulting HTML into a container.
// i18n labels are resolved at render time via t/tDyn from ./i18n, so the
// onLangChange hook on the calling page re-renders correctly.

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

// Two-eyes glyph 👀 inside .approval-pill--icon. Kept in sync with the
// matching constants in events.ts / inbox.ts / dashboard.ts.
const APPROVAL_PILL_GLYPH = "👀";
// Sparkle glyph ✨ for Paliadin-drafted pending rows (t-paliad-161).
// Renders alongside (not in place of) 👀 — orthogonal axes.
const AGENT_PILL_GLYPH = "✨";

export type Urgency = "overdue" | "today" | "tomorrow" | "this_week" | "later";
export type AgendaType = "deadline" | "appointment";

export interface AgendaItem {
  id: string;
  type: AgendaType;
  title: string;
  date: string;                    // ISO 8601
  end_at?: string | null;
  due_date?: string | null;        // YYYY-MM-DD (deadlines only)
  status?: string | null;
  location?: string | null;
  appointment_type?: string | null;
  urgency: Urgency;
  project_id?: string | null;
  project_title?: string | null;
  project_type?: string | null;
  project_reference?: string | null;
  approval_status?: "approved" | "pending" | "legacy" | null;
  requester_kind?: "user" | "agent" | null;
}

interface DayBucket {
  dayKey: string;
  day: Date;
  items: AgendaItem[];
}

// Render a full timeline (day buckets with items) for an array of agenda
// items. Returns a single HTML string ready to assign via innerHTML.
// The empty case returns an empty string — callers that want an empty-
// state UI handle it themselves (different copy on /agenda vs the
// dashboard inline slot).
export function renderAgendaTimeline(items: AgendaItem[]): string {
  if (!items.length) return "";
  const buckets = groupByDay(items);
  return buckets.map((b) => renderDay(b)).join("");
}

function groupByDay(items: AgendaItem[]): DayBucket[] {
  const map = new Map<string, DayBucket>();
  for (const it of items) {
    const d = new Date(it.date);
    if (isNaN(d.getTime())) continue;
    const key = toLocalDayKey(d);
    let b = map.get(key);
    if (!b) {
      b = { dayKey: key, day: new Date(d.getFullYear(), d.getMonth(), d.getDate()), items: [] };
      map.set(key, b);
    }
    b.items.push(it);
  }
  return Array.from(map.values()).sort((a, b) => a.day.getTime() - b.day.getTime());
}

function renderDay(bucket: DayBucket): string {
  const expected = expectedUrgency(bucket.day);
  return `<section class="agenda-day">
    <h2 class="agenda-day-heading">
      <span class="agenda-day-relative">${esc(relativeDayLabel(bucket.day))}</span>
      <span class="agenda-day-full">${esc(fullDateLabel(bucket.day))}</span>
    </h2>
    <ul class="agenda-items">
      ${bucket.items.map((it) => renderItem(it, expected)).join("")}
    </ul>
  </section>`;
}

// F-32: an item's urgency tag duplicates the day-bucket heading in the
// common case (a "Heute" item under HEUTE, a "Diese Woche" item under "in 3
// Tagen"). The tag stays only when it disagrees with the bucket — e.g. an
// "Überfällig" deadline that lands in today's bucket because of a filter
// quirk. expectedUrgency mirrors the server's bucketing rule against the
// bucket's day.
function expectedUrgency(day: Date): Urgency {
  const today = startOfToday();
  const diff = Math.round((day.getTime() - today.getTime()) / 86400000);
  if (diff < 0) return "overdue";
  if (diff === 0) return "today";
  if (diff === 1) return "tomorrow";
  if (diff <= 6) return "this_week";
  return "later";
}

function renderItem(it: AgendaItem, bucketUrgency: Urgency): string {
  const urgencyClass = `agenda-item-${it.urgency}`;
  const typeClass = `agenda-item-type-${it.type}`;
  const pendingClass = it.approval_status === "pending" ? " entity-row--pending-update" : "";
  const iconHTML = it.type === "deadline" ? deadlineIcon() : appointmentIcon();
  const detailHref = itemDetailHref(it);
  const project = it.project_id
    ? `<a class="agenda-item-project" href="/projects/${esc(it.project_id)}">${esc(formatProjectLabel(it))}</a>`
    : "";
  const pendingLabel = it.approval_status === "pending" ? tDyn("approvals.pending_update.label") : "";
  const pendingPill = it.approval_status === "pending"
    ? `<span class="approval-pill approval-pill--icon" title="${esc(pendingLabel)}" aria-label="${esc(pendingLabel)}">${APPROVAL_PILL_GLYPH}</span>`
    : "";
  const agentLabel = tDyn("approvals.agent.label");
  const agentPill = it.approval_status === "pending" && it.requester_kind === "agent"
    ? `<span class="approval-pill approval-pill--agent" title="${esc(agentLabel)}" aria-label="${esc(agentLabel)}">${AGENT_PILL_GLYPH}</span>`
    : "";

  const timePart = it.type === "appointment"
    ? `<span class="agenda-item-time">${esc(formatAppointmentTime(it))}</span>`
    : "";
  const urgencyTag = it.urgency !== bucketUrgency
    ? `<span class="agenda-item-urgency">${esc(tDyn(`agenda.urgency.${it.urgency}`))}</span>`
    : "";
  const locationPart = it.type === "appointment" && it.location
    ? `<span class="agenda-item-location">${esc(it.location)}</span>`
    : "";
  const typeLabelKey = it.type === "deadline"
    ? "agenda.label.deadline"
    : (it.appointment_type ? `agenda.appointment_type.${it.appointment_type}` : "agenda.label.appointment");
  const typeLabel = tDyn(typeLabelKey);

  return `<li class="agenda-item ${typeClass} ${urgencyClass}${pendingClass}">
    <a class="agenda-item-link" href="${esc(detailHref)}">
      <span class="agenda-item-icon" aria-hidden="true">${iconHTML}</span>
      <span class="agenda-item-main">
        <span class="agenda-item-headline">
          <span class="agenda-item-type-label">${esc(typeLabel)}:</span>
          <span class="agenda-item-title">${esc(it.title)}</span>
          ${pendingPill}
          ${agentPill}
        </span>
        <span class="agenda-item-sub">
          ${project}
          ${timePart}
          ${locationPart}
        </span>
      </span>
      <span class="agenda-item-meta">
        ${urgencyTag}
      </span>
    </a>
  </li>`;
}

function itemDetailHref(it: AgendaItem): string {
  return it.type === "deadline"
    ? `/deadlines/${encodeURIComponent(it.id)}`
    : `/appointments/${encodeURIComponent(it.id)}`;
}

function formatProjectLabel(it: AgendaItem): string {
  const ref = it.project_reference ? `${it.project_reference} · ` : "";
  const title = it.project_title || "";
  return `${ref}${title}`.trim();
}

function formatAppointmentTime(it: AgendaItem): string {
  const start = new Date(it.date);
  if (isNaN(start.getTime())) return "";
  const locale = getLang() === "de" ? "de-DE" : "en-GB";
  const startStr = start.toLocaleTimeString(locale, { hour: "2-digit", minute: "2-digit" });
  if (!it.end_at) return startStr;
  const end = new Date(it.end_at);
  if (isNaN(end.getTime())) return startStr;
  const endStr = end.toLocaleTimeString(locale, { hour: "2-digit", minute: "2-digit" });
  return `${startStr}–${endStr}`;
}

function relativeDayLabel(day: Date): string {
  const today = startOfToday();
  const diff = Math.round((day.getTime() - today.getTime()) / 86400000);
  if (diff < 0) {
    const n = Math.abs(diff);
    return getLang() === "de"
      ? (n === 1 ? "Gestern" : `vor ${n} Tagen`)
      : (n === 1 ? "Yesterday" : `${n} days ago`);
  }
  if (diff === 0) return t("agenda.day.today");
  if (diff === 1) return t("agenda.day.tomorrow");
  return getLang() === "de" ? `in ${diff} Tagen` : `in ${diff} days`;
}

function fullDateLabel(day: Date): string {
  const locale = getLang() === "de" ? "de-DE" : "en-GB";
  return day.toLocaleDateString(locale, {
    weekday: "long",
    year: "numeric",
    month: "long",
    day: "numeric",
  });
}

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

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}`;
}

function toLocalDayKey(d: Date): string {
  return toISODate(d);
}

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

function deadlineIcon(): string {
  return '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><polyline points="12 7 12 12 15 14"/></svg>';
}

function appointmentIcon(): string {
  return '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="17" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>';
}
