# -*- coding: utf-8 -*-
"""
GCal Plugin
Google Calendar Events
"""
import datetime, os, webbrowser
from dateutil import relativedelta, tz
from icalendar import Calendar
from PyQt5 import QtWidgets
from pkm import log, utils, SHAREDIR
from pkm.decorators import never_raise
from pkm.exceptions import ValidationError
from pkm.plugin import BasePlugin, BaseConfig
from pkm.filters import register_filter

NAME = 'Google Calendar'
TDELTAS = {
    'YEARLY': relativedelta.relativedelta(years=1),
    'MONTHLY': relativedelta.relativedelta(months=1),
    'WEEKLY': datetime.timedelta(weeks=1),
    'DAILY': datetime.timedelta(days=1)
}


class Plugin(BasePlugin):
    DEFAULT_INTERVAL = 600
    DELTANONE = datetime.datetime.now()

    @never_raise
    def update(self):
        self.data['events'] = []
        self.tzutc = tz.tzutc()
        self.tzlocal = tz.tzlocal()
        urls, colors = [], {}
        for cal in self._iter_calendars():
            urls.append(cal.url)
            colors[cal.url] = cal.color
        for result in utils.iter_responses(urls, timeout=5):
            response = result.get('response')
            if response:
                ical = Calendar.from_ical(response.read().decode('utf-8'))
                color = colors[result.get('url')]
                self.data['events'] += self._parse_events(ical, color)
        self.data['events'] = sorted(self.data['events'], key=lambda e:e['start'])
        # Calculate time to next event
        now = datetime.datetime.now()
        next = [e for e in self.data['events'] if e['start'] > now][0]['start'] if self.data['events'] else self.DELTANONE
        if next < now + datetime.timedelta(seconds=self.DEFAULT_INTERVAL*1.5): self.data['next'] = 'Now'
        else: self.data['next'] = utils.natural_time(next-now, 1)
        super(Plugin, self).update()

    def _iter_calendars(self):
        for i in range(6):
            baseurl = self.pkmeter.config.get(self.namespace, 'cal%s' % i)
            url = Plugin.build_url(baseurl)
            color = self.pkmeter.config.get(self.namespace, 'color%s' % i)
            if baseurl: yield utils.Bunch({'url':url, 'color':color})

    def _parse_events(self, ical, color):
        events = []
        today = datetime.datetime.combine(datetime.date.today(), datetime.time.min)
        title = ical.get('x-wr-calname', ical.get('version', ''))
        for event in ical.walk():
            if event.name == "VEVENT":
                start = self._event_start(event)
                if today <= start <= today + datetime.timedelta(days=14):
                    events.append({
                        'title': event.get('summary'),
                        'calendar': title,
                        'color': color,
                        'start': start,
                        'where': event.get('location'),
                        'status': event.get('description'),
                    })
        return events

    def _event_start(self, event):
        dt = self._fix_dt(event.get('dtstart').dt)
        recur = utils.rget(event, 'RRULE.FREQ.0')
        if recur in TDELTAS:
            today = datetime.datetime.combine(datetime.date.today(), datetime.time.min)
            until = utils.rget(event, 'RRULE.UNTIL')
            if until:
                until = self._fix_dt(min(until))
            while dt < today and (not until or dt < until):
                dt += TDELTAS[recur]
        return dt

    def _fix_dt(self, dt):
        if not isinstance(dt, datetime.datetime):
            return datetime.datetime.combine(dt, datetime.time.min)
        dt = dt.replace(tzinfo=self.tzutc)
        dt = dt.astimezone(self.tzlocal)
        return dt.replace(tzinfo=None)

    @never_raise
    def open_gcal(self, widget):
        url = 'http://google.com/calendar'
        log.info('Opening Google Calendar: %s', url)
        webbrowser.open(url)

    @staticmethod
    def build_url(baseurl):
        today = datetime.datetime.today()
        mindate = today.strftime('%Y-%m-%d')
        maxdate = (today + datetime.timedelta(days=30)).strftime('%Y-%m-%d')
        url = '%s?alt=json&singleevents=true&orderby=starttime' % baseurl
        url += '&start-min=%s&start-max=%s' % (mindate, maxdate)
        return url


class Config(BaseConfig):
    TEMPLATE = os.path.join(SHAREDIR, 'templates', 'gcal_config.html')
    FIELDS = utils.Bunch(BaseConfig.FIELDS,
        cal1={'default': ''}, color1={'default': '#4986E7'},
        cal2={'default': ''}, color2={'default': '#CD74E6'},
        cal3={'default': ''}, color3={'default': '#F83A22'},
        cal4={'default': ''}, color4={'default': '#FFAD46'},
        cal5={'default': ''}, color5={'default': '#7BD148'},
    )

    def __init__(self, *args, **kwargs):
        super(Config, self).__init__(*args, **kwargs)
        self._button = None
        self.colorpicker = self._init_colorpicker()

    def _init_colorpicker(self):
        colorpicker = QtWidgets.QColorDialog()
        colorpicker.finished.connect(self.colorpicker_finished)
        return colorpicker

    def btn_color(self, widget):
        self._button = widget
        self.colorpicker.setCurrentColor(utils.hex_to_qcolor(widget.data.color))
        self.colorpicker.open()

    def colorpicker_finished(self):
        field = self.fields[self._button.id]
        color = self.colorpicker.currentColor().name()
        self._button.set_value(color)
        self._validate(field)

    def _validate_cal(self, field, value):
        if not value:
            field.help.setText(field.help_default)
            return value
        url = Plugin.build_url(value)
        response = utils.http_request(url, timeout=2).get('response')
        if not response:
            raise ValidationError('No response from Google.')
        ical = Calendar.from_ical(response.read().decode('utf-8'))
        title = ical.get('x-wr-calname', ical.get('version', ''))
        if not title:
            raise ValidationError('Invalid response from Google.')
        field.help.setText(title)
        return value

    def validate_cal1(self, field, value):
        return self._validate_cal(field, value)

    def validate_cal2(self, field, value):
        return self._validate_cal(field, value)

    def validate_cal3(self, field, value):
        return self._validate_cal(field, value)

    def validate_cal4(self, field, value):
        return self._validate_cal(field, value)

    def validate_cal5(self, field, value):
        return self._validate_cal(field, value)


@register_filter()
def gcal_dtstr(value):
    # Select format based on for how far away event is
    today = datetime.datetime.combine(datetime.date.today(), datetime.datetime.min.time())
    tomorrow = today + datetime.timedelta(days=1)
    nextweek = today + datetime.timedelta(days=7)
    if value >= nextweek: dtstr = value.strftime('%b %d')
    elif value >= tomorrow: dtstr = value.strftime('%a %I:%M%p')
    else: dtstr = value.strftime(' %I:%M%p')
    # Replace a few things to make it simpler
    dtstr = dtstr.replace(' 0',' ').replace(':00','')
    dtstr = dtstr.replace('AM','a').replace('PM','p')
    dtstr = dtstr.replace('12a', 'All day' if value < tomorrow else '')
    return dtstr.strip()