from bs4 import BeautifulSoup
import calendar
from datetime import datetime, timedelta
import json
import locale
import logging
import requests
import requests_cache

locale.setlocale(locale.LC_ALL, 'es_ES.UTF-8')


class Event:

    def __init__(self, acronym, date, first_edition_year, name, url):
        self.logger = logging.getLogger(__name__)
        self.acronym = acronym
        self.date = date
        self.first_edition_year = first_edition_year
        self.name = name
        self.edition = self.date.year - self.first_edition_year + 1
        self.url = url

    def time_left(self):
        '''
        Return: days, hours, minutes and seconds
                remaining for the next LAN party
        '''
        now = datetime.now()
        td = self.date - now
        days = td.days
        hours = td.seconds // 3600
        minutes = (td.seconds // 60) % 60
        seconds = (td.seconds % 3600) % 60
        return days, hours, minutes, seconds

    def update_event(self):
        response = requests.get(self.url)
        if response.from_cache:
            self.logger.info(
                'Loading request for %s event from cache.' % self.name
            )
        else:
            self.logger.info(
                'Caching request for %s event to cache.' % self.name
            )
        if response.history:
            url_edition = int(response.url.split('.')[0]
                              .replace('https://', '').replace(self.acronym.lower(), '')
                              )
            if url_edition == self.edition + 1:
                soup = BeautifulSoup(response.text, 'html.parser')
                try:
                    # str e.g: Del 26 al 29 de julio de 2018
                    str_date = (soup.find('a', {'name': 'cuando'})
                                .parent.findNext('p').text.lower().split()
                                )
                    # pre-event day is the previous day to the official start.
                    start_day = int(str_date[1]) - 1
                    end_day = int(str_date[3])
                    month = None
                    for value, key in enumerate(calendar.month_name):
                        if key == str_date[5]:
                            month = value
                    year = int(str_date[7])
                    date = datetime(
                        year,
                        month,
                        start_day,
                        self.date.hour,
                        self.date.minute
                    )
                    self.date = date
                    self.edition += 1
                except:
                    self.logger.exception()
            else:
                self.logger.info(
                    'URL for edition {} of {} event has not been updated yet.'
                    .format(self.edition + 1, self.name)
                )
        else:
            self.logger.error('URL redirection of %s event did not happen.'
                              % self.name)
        return self


class Events:

    CACHE_EXPIRE = 3600

    def __init__(self):
        self.logger = logging.getLogger(__name__)
        requests_cache.install_cache(
            cache_name='date_cache',
            backend='memory',
            expire_after=Events.CACHE_EXPIRE
        )
        self.logger.info(
            'Initialized in-memory cache for event date updater ' +
            'with expiration set for %d minutes.' % (Events.CACHE_EXPIRE / 60)
        )
        self.events = self._load_events()

    def _load_events(self, filename='events.json'):
        file = open(filename)
        data = json.load(file)
        file.close()
        json_events = data['events']
        now = datetime.now()
        events = []
        for event in json_events:
            current = event['event']
            url = current['url']
            year = current['date']['year']
            month = current['date']['month']
            day = current['date']['day']
            hour = current['date']['hour']
            minute = current['date']['minute']
            date = datetime(year, month, day, hour, minute)
            current_event = Event(
                current['acronym'],
                date,
                current['first_edition_year'],
                current['name'],
                url
            )
            # if date < now:
            #    current_event = current_event.update_event()
            events.append(current_event)
        return events

    def get_event(self, acronym):
        for event in self.events:
            if event.acronym.lower() == acronym.lower():
                return event
        return None

    def set_event(self, acronym, set_event):
        for index, event in enumerate(self.events):
            if event.acronym.lower() == acronym.lower():
                self.events[index] = set_event

    def next_event(self):
        now = datetime.now()
        # Since events occur annually max. time-lapse is 1 year.
        # Using Leap year just in case.
        soonest_date = now + timedelta(days=366)
        next_event = None
        for event in self.events:
            if event.date > now and event.date < soonest_date:
                next_event = event
                soonest_date = event.date
        return next_event

from bs4 import BeautifulSoup
import calendar
from datetime import datetime, timedelta
import json
import locale
import logging
import requests
import requests_cache

locale.setlocale(locale.LC_ALL, 'es_ES.UTF-8')


class Event:

    def __init__(self, acronym, date, first_edition_year, name, url):
        self.logger = logging.getLogger(__name__)
        self.acronym = acronym
        self.date = date
        self.first_edition_year = first_edition_year
        self.name = name
        self.edition = self.date.year - self.first_edition_year + 1
        self.url = url

    def time_left(self):
        '''
        Return: days, hours, minutes and seconds
                remaining for the next LAN party
        '''
        now = datetime.now()
        td = self.date - now
        days = td.days
        hours = td.seconds // 3600
        minutes = (td.seconds // 60) % 60
        seconds = (td.seconds % 3600) % 60
        return days, hours, minutes, seconds

    def update_event(self):
        response = requests.get(self.url)
        if response.from_cache:
            self.logger.info(
                'Loading request for %s event from cache.' % self.name
            )
        else:
            self.logger.info(
                'Caching request for %s event to cache.' % self.name
            )
        if response.history:
            url_edition = int(response.url.split('.')[0]
                              .replace('https://', '').replace(self.acronym.lower(), '')
                              )
            if url_edition == self.edition + 1:
                soup = BeautifulSoup(response.text, 'html.parser')
                try:
                    # str e.g: Del 26 al 29 de julio de 2018
                    str_date = (soup.find('a', {'name': 'cuando'})
                                .parent.findNext('p').text.lower().split()
                                )
                    # pre-event day is the previous day to the official start.
                    start_day = int(str_date[1]) - 1
                    end_day = int(str_date[3])
                    month = None
                    for value, key in enumerate(calendar.month_name):
                        if key == str_date[5]:
                            month = value
                    year = int(str_date[7])
                    date = datetime(
                        year,
                        month,
                        start_day,
                        self.date.hour,
                        self.date.minute
                    )
                    self.date = date
                    self.edition += 1
                except:
                    self.logger.exception()
            else:
                self.logger.info(
                    'URL for edition {} of {} event has not been updated yet.'
                    .format(self.edition + 1, self.name)
                )
        else:
            self.logger.error('URL redirection of %s event did not happen.'
                              % self.name)
        return self


class Events:

    CACHE_EXPIRE = 3600

    def __init__(self):
        self.logger = logging.getLogger(__name__)
        requests_cache.install_cache(
            cache_name='date_cache',
            backend='memory',
            expire_after=Events.CACHE_EXPIRE
        )
        self.logger.info(
            'Initialized in-memory cache for event date updater ' +
            'with expiration set for %d minutes.' % (Events.CACHE_EXPIRE / 60)
        )
        self.events = self._load_events()

    def _load_events(self, filename='events.json'):
        file = open(filename)
        data = json.load(file)
        file.close()
        json_events = data['events']
        now = datetime.now()
        events = []
        for event in json_events:
            current = event['event']
            url = current['url']
            year = current['date']['year']
            month = current['date']['month']
            day = current['date']['day']
            hour = current['date']['hour']
            minute = current['date']['minute']
            date = datetime(year, month, day, hour, minute)
            current_event = Event(
                current['acronym'],
                date,
                current['first_edition_year'],
                current['name'],
                url
            )
            # if date < now:
            #    current_event = current_event.update_event()
            events.append(current_event)
        return events

    def get_event(self, acronym):
        for event in self.events:
            if event.acronym.lower() == acronym.lower():
                return event
        return None

    def set_event(self, acronym, set_event):
        for index, event in enumerate(self.events):
            if event.acronym.lower() == acronym.lower():
                self.events[index] = set_event

    def next_event(self):
        now = datetime.now()
        # Since events occur annually max. time-lapse is 1 year.
        # Using Leap year just in case.
        soonest_date = now + timedelta(days=366)
        next_event = None
        for event in self.events:
            if event.date > now and event.date < soonest_date:
                next_event = event
                soonest_date = event.date
        return next_event

Return: days, hours, minutes and seconds                 remaining for the next LAN party # str e.g: Del 26 al 29 de julio de 2018 # pre-event day is the previous day to the official start. # if date < now: #    current_event = current_event.update_event() # Since events occur annually max. time-lapse is 1 year. # Using Leap year just in case.