import type { ChecklistEntry } from './checklist';
import type { Deadline } from './types';

/**
 * iCalendar-Export (RFC 5545) für Fristen und Pflichten.
 *
 * Erzeugt eine .ics-Datei mit ganztägigen Terminen und eingebauten
 * Erinnerungen (VALARM, 30 und 7 Tage vor Fälligkeit). Die Datei lässt
 * sich in Outlook, Apple Kalender, Google Kalender, Thunderbird u. a.
 * importieren — auf Smartphone und Desktop, komplett ohne Server.
 */

/** Erinnerungszeitpunkte in Tagen vor der Frist. */
const ALARM_DAYS_BEFORE: readonly number[] = [30, 7];

/** Maskiert Text nach RFC 5545 (Backslash, Semikolon, Komma, Zeilenumbruch). */
function escapeIcs(text: string): string {
  return text
    .replaceAll('\\', '\\\\')
    .replaceAll(';', '\\;')
    .replaceAll(',', '\\,')
    .replaceAll('\r\n', '\\n')
    .replaceAll('\n', '\\n');
}

/**
 * Faltet eine Content-Zeile nach RFC 5545 (max. 75 Oktette je Zeile,
 * Fortsetzungszeilen beginnen mit einem Leerzeichen).
 */
function foldLine(line: string): string {
  const bytes = new TextEncoder().encode(line);
  if (bytes.length <= 75) return line;

  const parts: string[] = [];
  let current = '';
  let currentBytes = 0;
  for (const char of line) {
    const charBytes = new TextEncoder().encode(char).length;
    const limit = parts.length === 0 ? 75 : 74; // Folgezeilen: 1 Oktett für das Leerzeichen
    if (currentBytes + charBytes > limit) {
      parts.push(current);
      current = char;
      currentBytes = charBytes;
    } else {
      current += char;
      currentBytes += charBytes;
    }
  }
  if (current !== '') parts.push(current);
  return parts.join('\r\n ');
}

/** YYYY-MM-DD → YYYYMMDD (DATE-Wert nach RFC 5545). */
function toIcsDate(isoDate: string): string {
  return isoDate.replaceAll('-', '');
}

/** ISO-Zeitstempel → YYYYMMDDTHHMMSSZ (UTC, DTSTAMP). */
function toIcsTimestamp(isoDateTime: string): string {
  return `${isoDateTime.slice(0, 19).replace(/[-:]/g, '')}Z`;
}

/** Folgetag eines ISO-Datums (DTEND ganztägiger Termine ist exklusiv). */
function nextDay(isoDate: string): string {
  const [y, m, d] = isoDate.split('-').map(Number);
  const date = new Date(Date.UTC(y ?? 1970, (m ?? 1) - 1, d ?? 1));
  date.setUTCDate(date.getUTCDate() + 1);
  return date.toISOString().slice(0, 10);
}

interface IcsEvent {
  readonly uid: string;
  readonly date: string;
  readonly summary: string;
  readonly description: string;
}

function eventBlock(event: IcsEvent, dtstamp: string): readonly string[] {
  const lines = [
    'BEGIN:VEVENT',
    `UID:${event.uid}`,
    `DTSTAMP:${dtstamp}`,
    `DTSTART;VALUE=DATE:${toIcsDate(event.date)}`,
    `DTEND;VALUE=DATE:${toIcsDate(nextDay(event.date))}`,
    `SUMMARY:${escapeIcs(event.summary)}`,
    `DESCRIPTION:${escapeIcs(event.description)}`,
    'CATEGORIES:AI-Act-Kompass',
    'TRANSP:TRANSPARENT',
  ];
  for (const days of ALARM_DAYS_BEFORE) {
    lines.push(
      'BEGIN:VALARM',
      'ACTION:DISPLAY',
      `TRIGGER:-P${days}D`,
      `DESCRIPTION:${escapeIcs(event.summary)}`,
      'END:VALARM',
    );
  }
  lines.push('END:VEVENT');
  return lines;
}

/** Parameter für {@link checklistIcs}. */
export interface ChecklistIcsParams {
  /** Offene Aufgaben (fällig + anstehend) aus der Checkliste. */
  readonly entries: readonly ChecklistEntry[];
  /** Allgemeine Geltungsfristen des AI Act (Content-Pack). */
  readonly deadlines: readonly Deadline[];
  /** Stichtag im ISO-Format — nur Termine ab diesem Tag werden exportiert. */
  readonly today: string;
  /** Erzeugungszeitpunkt (ISO-Datetime) für DTSTAMP. */
  readonly timestamp: string;
  /** Anzeigename des Kalenders. */
  readonly calendarName: string;
}

/**
 * Erzeugt eine iCalendar-Datei mit allen zukünftigen Pflicht-Fristen
 * (je System) und den allgemeinen AI-Act-Geltungsfristen — jeweils als
 * ganztägiger Termin mit Erinnerungen 30 und 7 Tage vorher.
 *
 * @returns Inhalt der .ics-Datei (CRLF-Zeilenenden, RFC-5545-Faltung)
 */
export function checklistIcs(params: ChecklistIcsParams): string {
  const dtstamp = toIcsTimestamp(params.timestamp);

  const taskEvents: IcsEvent[] = params.entries
    .filter((e) => e.obligation.deadline >= params.today)
    .map((e) => ({
      uid: `${e.systemId}-${e.obligation.id}@ai-act-kompass`,
      date: e.obligation.deadline,
      summary: `AI Act: ${e.obligation.title} — ${e.systemName}`,
      description: `${e.obligation.articles} · ${e.obligation.description}\n\nAI-Act-Kompass`,
    }));

  const deadlineEvents: IcsEvent[] = params.deadlines
    .filter((d) => d.date >= params.today)
    .map((d) => ({
      uid: `deadline-${d.id}@ai-act-kompass`,
      date: d.date,
      summary: `AI Act: ${d.label}`,
      description: `${d.description}\n\nAI-Act-Kompass`,
    }));

  const lines = [
    'BEGIN:VCALENDAR',
    'VERSION:2.0',
    'PRODID:-//AI-Act-Kompass//Compliance Reminders//DE',
    'CALSCALE:GREGORIAN',
    'METHOD:PUBLISH',
    `X-WR-CALNAME:${escapeIcs(params.calendarName)}`,
    ...[...taskEvents, ...deadlineEvents].flatMap((e) => eventBlock(e, dtstamp)),
    'END:VCALENDAR',
  ];

  return lines.map(foldLine).join('\r\n') + '\r\n';
}
