import re
from operator import attrgetter

import arrow
import bleach
from django.conf import settings
from ics import Calendar

EVENT_KEYS = ("begin", "end", "all_day", "name", "description", "location")


def remove_alarms(source):
    """Removes VALARM blocks from iCal source."""
    return re.sub(r"(BEGIN:VALARM.*?END:VALARM\r?\n)", "", source, flags=re.S)


def parse_ical(source):
    """Parses iCalendar source and returns events as list of dicts."""
    # there is a bug in parsing alarms, but we don't need them, so we remove them
    source = remove_alarms(source)
    cal = Calendar(source)
    events = []
    for event in sorted(cal.events, key=attrgetter("begin"), reverse=True):
        events.append({key: getattr(event, key) for key in EVENT_KEYS})
    return events


def split_events(events):
    """Splits events and returns list of past events and future events."""
    singularity = arrow.utcnow().shift(hours=-2)
    past = [ev for ev in events if ev["end"] < singularity]
    future = [ev for ev in events if ev["end"] > singularity]
    return past, future


def set_event_description(event):
    """Clears even description from unwanted tags."""
    description: str = event.get("description", "") or ""
    event["description"] = bleach.clean(description, tags=["a", "br"], strip=True)
    return event


def set_event_duration(event):
    """Sets duration for event."""
    if event["all_day"]:
        event["duration"] = "celĂ˝ den"
        return event

    delta = event["end"] - event["begin"]
    if delta.days < 1:
        begin = event["begin"].to(settings.TIME_ZONE).format("H:mm")
        end = event["end"].to(settings.TIME_ZONE).format("H:mm")
        event["duration"] = f"{begin} - {end}"
    else:
        begin = event["begin"].to(settings.TIME_ZONE).format("H:mm")
        end = event["end"].to(settings.TIME_ZONE).format("H:mm (D.M.)")
        event["duration"] = f"{begin} - {end}"
    return event


def process_event(event):
    """Processes single event for use in Majak"""
    event = set_event_duration(event)
    event = set_event_description(event)
    return event


def process_ical(source):
    """Parses iCalendar source and returns events as list of dicts. Returns
    tuple of past and future events.
    """
    events = parse_ical(source)
    events = list(map(process_event, events))
    return split_events(events)