import { sha256Hex } from './crypto';
import { icsRateLimitPerMin } from './env';

function escapeIcsText(s: string): string {
  return String(s)
    .replace(/\\/g, '\\\\')
    .replace(/\n/g, '\\n')
    .replace(/\r/g, '')
    .replace(/,/g, '\\,')
    .replace(/;/g, '\\;');
}

function foldIcsLine(line: string): string {
  // 75 octetos; para simplicidad contamos caracteres (UTF-8 simple en nuestro caso)
  const max = 75;
  if (line.length <= max) return line;
  const parts: string[] = [];
  let i = 0;
  while (i < line.length) {
    const chunk = line.slice(i, i + max);
    parts.push(i === 0 ? chunk : ' ' + chunk);
    i += max;
  }
  return parts.join('\r\n');
}

function padTaskId(id: number, width: number = 4): string {
  const s = String(Math.max(0, Math.floor(id)));
  if (s.length >= width) return s;
  return '0'.repeat(width - s.length) + s;
}

function ymdToBasic(ymd: string): string {
  // Espera YYYY-MM-DD
  const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(ymd);
  if (!m) return '';
  return `${m[1]}${m[2]}${m[3]}`;
}

function addDays(ymd: string, days: number): string {
  const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(ymd);
  if (!m) return ymd;
  const d = new Date(Date.UTC(Number(m[1]), Number(m[2]) - 1, Number(m[3])));
  d.setUTCDate(d.getUTCDate() + days);
  const yyyy = String(d.getUTCFullYear()).padStart(4, '0');
  const mm = String(d.getUTCMonth() + 1).padStart(2, '0');
  const dd = String(d.getUTCDate()).padStart(2, '0');
  return `${yyyy}-${mm}-${dd}`;
}

export type IcsEvent = {
  id: number;
  description: string;
  due_date: string; // YYYY-MM-DD
  group_name?: string | null;
  prefix?: string; // ej: "T" para [T0123]
};

// Rate limiting por token (ventana 1 minuto)
const __icsRateMap = new Map<string, { start: number; count: number }>();

export function checkIcsRateLimit(tokenKey: string): { ok: boolean; retryAfterSec?: number } {
  const limit = Number(icsRateLimitPerMin || 0);
  if (!Number.isFinite(limit) || limit <= 0) return { ok: true };
  const now = Date.now();
  const windowMs = 60 * 1000;
  const winStart = Math.floor(now / windowMs) * windowMs;
  const rec = __icsRateMap.get(tokenKey);
  if (!rec || rec.start !== winStart) {
    __icsRateMap.set(tokenKey, { start: winStart, count: 1 });
    return { ok: true };
  }
  if (rec.count < limit) {
    rec.count += 1;
    return { ok: true };
  }
  const retryAfterSec = Math.max(1, Math.ceil((rec.start + windowMs - now) / 1000));
  return { ok: false, retryAfterSec };
}

export async function buildIcsCalendar(title: string, events: IcsEvent[]): Promise<{ body: string; etag: string }> {
  const lines: string[] = [];
  lines.push('BEGIN:VCALENDAR');
  lines.push('VERSION:2.0');
  lines.push('PRODID:-/Wtask.org//ICS 1.0//ES');
  lines.push('CALSCALE:GREGORIAN');
  lines.push('METHOD:PUBLISH');
  lines.push(`X-WR-CALNAME:${escapeIcsText(title)}`);
  lines.push('X-WR-TIMEZONE:UTC');

  for (const ev of events) {
    const idPad = padTaskId(ev.id);
    const summary = `[${ev.prefix || 'T'}${idPad}] ${ev.description}`;
    const dtStart = ymdToBasic(ev.due_date);
    const dtEnd = ymdToBasic(addDays(ev.due_date, 1));
    const uid = `task-${ev.id}@tw`;

    lines.push('BEGIN:VEVENT');
    lines.push(foldIcsLine(`UID:${uid}`));
    lines.push(foldIcsLine(`SUMMARY:${escapeIcsText(summary)}`));
    lines.push(`DTSTART;VALUE=DATE:${dtStart}`);
    lines.push(`DTEND;VALUE=DATE:${dtEnd}`);
    if (ev.group_name) {
      lines.push(foldIcsLine(`CATEGORIES:${escapeIcsText(ev.group_name || '')}`));
    }
    lines.push('END:VEVENT');
  }

  lines.push('END:VCALENDAR');

  const body = lines.join('\r\n') + '\r\n';
  const etag = await sha256Hex(body);
  return { body, etag: `W/"${etag}"` };
}
