"""iCalendar (.ics) generation for grants events — currently interview invites.

Hand-rolled VCALENDAR/VEVENT (no external icalendar dependency in this project's
deps). RFC 5545 minimum-viable subset: VERSION, PRODID, UID, DTSTAMP, DTSTART,
DTEND, SUMMARY, DESCRIPTION, LOCATION, ORGANIZER, ATTENDEE.
"""
from __future__ import annotations

import hashlib
import re
from datetime import datetime, timedelta, timezone as dt_timezone

from django.conf import settings
from django.utils import timezone


_LINE_MAX = 75  # RFC 5545 line-folding boundary


def _fmt_dt(value: datetime) -> str:
    """Format datetime as UTC iCal timestamp: YYYYMMDDTHHMMSSZ."""
    if timezone.is_naive(value):
        value = timezone.make_aware(value, timezone.get_current_timezone())
    utc_value = value.astimezone(dt_timezone.utc)
    return utc_value.strftime("%Y%m%dT%H%M%SZ")


def _escape(text: str) -> str:
    """Escape iCal special characters per RFC 5545 §3.3.11."""
    if not text:
        return ""
    return (
        text.replace("\\", "\\\\")
        .replace(";", "\\;")
        .replace(",", "\\,")
        .replace("\n", "\\n")
    )


def _fold(line: str) -> str:
    """RFC 5545 line folding — split lines longer than 75 octets, continuation
    lines start with a single space."""
    if len(line) <= _LINE_MAX:
        return line
    chunks = [line[:_LINE_MAX]]
    rest = line[_LINE_MAX:]
    while rest:
        chunks.append(" " + rest[: _LINE_MAX - 1])
        rest = rest[_LINE_MAX - 1 :]
    return "\r\n".join(chunks)


def _interview_uid(interview) -> str:
    """Stable UID per interview row — survives reschedules so the calendar
    client can update the existing event instead of creating duplicates."""
    host = re.sub(r"[^a-z0-9.]", "", (getattr(settings, "PUBLIC_APP_BASE_URL", "iilmp.local") or "").lower()) or "iilmp.local"
    digest = hashlib.sha1(f"interview-{interview.pk}".encode()).hexdigest()[:12]
    return f"interview-{interview.pk}-{digest}@{host}"


def build_interview_ics(interview, *, duration_minutes: int = 45) -> bytes:
    """Render a single-event VCALENDAR for an InterviewSchedule row.

    The interview model stores only ``scheduled_for``; we infer end time from
    ``duration_minutes`` (default 45 min — matches common practice for grant
    interview slots; the value can grow into a model field later without
    breaking the helper signature).
    """
    application = interview.application
    call = application.call
    applicant = application.applicant
    start = interview.scheduled_for
    end = start + timedelta(minutes=duration_minutes)

    summary = f"Interview: {call.title}"
    location = (interview.venue or "").strip() or interview.get_format_display()
    description_lines = [
        f"Application reference: {application.anonymized_code or application.pk}",
        f"Format: {interview.get_format_display()}",
    ]
    if interview.venue:
        description_lines.append(f"Venue: {interview.venue}")
    if interview.notes:
        description_lines.append("")
        description_lines.append(interview.notes)
    description = "\n".join(description_lines)

    organizer_email = getattr(settings, "DEFAULT_FROM_EMAIL", "no-reply@ruforum.org")

    lines = [
        "BEGIN:VCALENDAR",
        "VERSION:2.0",
        "PRODID:-//RUFORUM IILMP//Grants Interview//EN",
        "CALSCALE:GREGORIAN",
        "METHOD:REQUEST",
        "BEGIN:VEVENT",
        f"UID:{_interview_uid(interview)}",
        f"DTSTAMP:{_fmt_dt(timezone.now())}",
        f"DTSTART:{_fmt_dt(start)}",
        f"DTEND:{_fmt_dt(end)}",
        f"SUMMARY:{_escape(summary)}",
        f"DESCRIPTION:{_escape(description)}",
        f"LOCATION:{_escape(location)}",
        f"ORGANIZER;CN=RUFORUM IILMP:mailto:{organizer_email}",
        f"ATTENDEE;CN={_escape(applicant.get_full_name() or applicant.email)};RSVP=TRUE:mailto:{applicant.email}",
        f"STATUS:{'CONFIRMED' if interview.status == 'scheduled' else 'TENTATIVE'}",
        "TRANSP:OPAQUE",
        "END:VEVENT",
        "END:VCALENDAR",
    ]
    payload = "\r\n".join(_fold(line) for line in lines) + "\r\n"
    return payload.encode("utf-8")


def ics_filename_for_interview(interview) -> str:
    return f"interview-{interview.pk}.ics"
