"""
ICS (iCalendar) file generation and parsing utilities.

Generates RFC 5545 compliant iCalendar files for calendar invites.
"""

from __future__ import annotations

import re
from datetime import datetime, timezone

from .types import CalendarEvent, CalendarEventAttendee, ParticipantRole

# Field mappings for ICS parsing
_DIRECT_FIELDS = {"UID": "uid", "URL": "url"}
_UNESCAPE_FIELDS = {"SUMMARY": "title", "DESCRIPTION": "description", "LOCATION": "location"}
_DATE_FIELDS = {"DTSTART": "start", "DTEND": "end"}


def escape_ics(s: str) -> str:
    """Escape special characters for ICS format."""
    return (
        s.replace("\\", "\\\\")
        .replace(";", "\\;")
        .replace(",", "\\,")
        .replace("\n", "\\n")
    )


def unescape_ics(s: str) -> str:
    """Unescape ICS special characters."""
    return (
        s.replace("\\n", "\n")
        .replace("\\,", ",")
        .replace("\\;", ";")
        .replace("\\\\", "\\")
    )


def format_ics_date(iso_string: str) -> str:
    """Format a date for ICS (YYYYMMDDTHHMMSSZ)."""
    return re.sub(r"\.\d{3}", "", re.sub(r"[-:]", "", iso_string))


def parse_ics_date(ics_date: str) -> str:
    """Parse ICS date format to ISO string."""
    if match := re.match(r"^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})Z?$", ics_date):
        year, month, day, hour, minute, second = match.groups()
        return f"{year}-{month}-{day}T{hour}:{minute}:{second}Z"
    return ics_date


def ics_role(role: ParticipantRole) -> str:
    """Map participant role to ICS role."""
    return "OPT-PARTICIPANT" if role == ParticipantRole.OPTIONAL else "REQ-PARTICIPANT"


def fold_line(line: str) -> str:
    """Fold long lines per RFC 5545 (max 75 octets per line)."""
    if len(line) <= 75:
        return line

    lines: list[str] = []
    remaining = line
    while remaining:
        if not lines:
            lines.append(remaining[:75])
            remaining = remaining[75:]
        else:
            lines.append(" " + remaining[:74])
            remaining = remaining[74:]
    return "\r\n".join(lines)


def generate_ics(event: CalendarEvent) -> str:
    """Generate ICS content for a calendar event."""
    now_stamp = format_ics_date(datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ"))

    lines: list[str] = [
        "BEGIN:VCALENDAR",
        "VERSION:2.0",
        "PRODID:-//ElizaOS//SchedulingPlugin//EN",
        "CALSCALE:GREGORIAN",
        "METHOD:REQUEST",
        "BEGIN:VEVENT",
        f"UID:{event.uid}",
        f"DTSTAMP:{now_stamp}",
        f"DTSTART:{format_ics_date(event.start)}",
        f"DTEND:{format_ics_date(event.end)}",
        f"SUMMARY:{escape_ics(event.title)}",
    ]

    if event.description:
        lines.append(f"DESCRIPTION:{escape_ics(event.description)}")
    if event.location:
        lines.append(f"LOCATION:{escape_ics(event.location)}")
    if event.url:
        lines.append(f"URL:{event.url}")
    if event.organizer:
        lines.append(
            f"ORGANIZER;CN={escape_ics(event.organizer.name)}:mailto:{event.organizer.email}"
        )

    for attendee in event.attendees or []:
        role = ics_role(attendee.role)
        lines.append(
            f"ATTENDEE;ROLE={role};PARTSTAT=NEEDS-ACTION;"
            f"CN={escape_ics(attendee.name)}:mailto:{attendee.email}"
        )

    for minutes in event.reminder_minutes or []:
        lines.extend([
            "BEGIN:VALARM",
            "ACTION:DISPLAY",
            f"DESCRIPTION:{escape_ics(event.title)}",
            f"TRIGGER:-PT{minutes}M",
            "END:VALARM",
        ])

    lines.extend(["STATUS:CONFIRMED", "SEQUENCE:0", "END:VEVENT", "END:VCALENDAR"])
    return "\r\n".join(fold_line(line) for line in lines)


def parse_ics(ics: str) -> list[CalendarEvent]:
    """Parse an ICS file and extract events (basic parser)."""
    events: list[CalendarEvent] = []
    lines = re.split(r"\r?\n", ics)

    current_event: dict[str, str] | None = None
    current_line = ""

    for line in lines:
        # Handle line folding (continuation lines start with space or tab)
        if line.startswith((" ", "\t")):
            current_line += line[1:]
            continue

        if current_line and current_event is not None:
            _process_line(current_line, current_event)

        current_line = line

        if line == "BEGIN:VEVENT":
            current_event = {}
        elif line == "END:VEVENT" and current_event is not None:
            if all(k in current_event for k in ("uid", "start", "end", "title")):
                events.append(
                    CalendarEvent(
                        uid=current_event["uid"],
                        title=current_event["title"],
                        start=current_event["start"],
                        end=current_event["end"],
                        time_zone=current_event.get("time_zone", "UTC"),
                        description=current_event.get("description"),
                        location=current_event.get("location"),
                        url=current_event.get("url"),
                    )
                )
            current_event = None

    return events


def _process_line(line: str, event: dict[str, str]) -> None:
    """Process a single ICS property line."""
    colon_index = line.find(":")
    if colon_index == -1:
        return

    key_part = line[:colon_index]
    value = line[colon_index + 1:]
    semi_index = key_part.find(";")
    key = key_part[:semi_index] if semi_index != -1 else key_part

    if key in _DIRECT_FIELDS:
        event[_DIRECT_FIELDS[key]] = value
    elif key in _UNESCAPE_FIELDS:
        event[_UNESCAPE_FIELDS[key]] = unescape_ics(value)
    elif key in _DATE_FIELDS:
        event[_DATE_FIELDS[key]] = parse_ics_date(value)
