from datetime import date, datetime
from dateutil.relativedelta import relativedelta  # BSD


class Birthday:
    def __init__(self, person, date):
        person = person.strip()
        date = date.strip()
        if not person or not date:
            raise ValueError("One of the parameters was invalid or empty!")

        self.PERSON = person.strip()
        self.DATE = date.strip()

    def __eq__(self, other):
        return self.PERSON == other.PERSON and self.DATE == other.DATE

    def __repr__(self):
        return f'{self.__class__.__name__}: {self.PERSON} {self.DATE}'

    # ist nur python 2 => muss für __lt__ als python 3 neugeschrieben werden
    # def __cmp__(self, other):
    #    if hasattr(other, 'DATE') and hasattr(other, 'PERSON'):
    #        splittedDateSelf = self.DATE.split('.')
    #        splittedDateOther = other.DATE.split('.')
    #        comp = splittedDateSelf[1].__cmp__(splittedDateOther[1]) # compare month
     #       if comp == 0:
     #           comp = splittedDateSelf[0].__cmp__(splittedDateOther[0]) # compare day
     # if  comp == 0:
     #               comp = self.PERSON.__cmp__(other.PERSON) # compare name
     #
     #       return comp

    def get_congratulation(self):
        return f"Alles Gute {self.PERSON} ❤"

    def get_csv_line(self):
        return f"{self.PERSON},{self.DATE}"


class ModuleBirthdayReminder:
    def __init__(self, groups, send, log):
        self.tag = "%20s - " % "mBirthdayReminder"
        self.log = log
        self.send = send

        self.birthday_list = {}
        self.GROUPS = groups
        for group in groups:
            self.load_birthdays_from_csv(group)

        self.log.debug(f"{self.tag}Modul BirthdayReminder geladen")

    def load_birthdays_from_csv(self, groupInfo):
        lines = []

        try:
            with open(groupInfo["BIRTHDAYDIR"], 'r', encoding='utf16') as f:
                lines = f.readlines()
        except IOError as exc:
            self.log.error(
                f"{self.tag}Reading birthdays from csv failed: {exc}")

        self.birthday_list[groupInfo["NAME"]] = []

        for line in lines:
            try:
                birthday = self.interpret_birthday_line(line)
                self.birthday_list[groupInfo["NAME"]].append(birthday)
            except (SyntaxError, ValueError) as exc:
                self.log.warning(
                    f"{self.tag}Error while interpreting birthday line:{line}; Error: {exc}")

    def check_for_birthdays(self):
        today = date.today()
        dm = today.strftime("%d.%m")
        for key, value in self.birthday_list.items():
            for birthday in value:
                if dm == birthday.DATE:
                    for group in self.GROUPS:
                        if group["NAME"] == key:
                            self.send(birthday.get_congratulation(), group["ID"])
                            break

    def get_next_birthday(self, groupInfo):
        today = datetime.today()
        format = "%d.%m.%Y"
        delta = 800
        next_birthdays=[]
        next_birthday_date = None
        birthday_next_year_string = ""
        for birthday in self.birthday_list[groupInfo["NAME"]]:
            # Dieses Jahr
            birthday_string = f"{birthday.DATE}.{date.today().strftime('%Y')}"
            birthday_dt = datetime.strptime(birthday_string, format)
            
            delta_temp = (birthday_dt - today).days + 1 # Damit er bei 47h nicht 1 Tag sagt
            if 0 < delta_temp < delta:
                next_birthday_date = birthday_dt
                delta = delta_temp
            else:
                # Nächstes Jahr (Für den Jahresüberlauf)
                next_year = date.today() + relativedelta(years=1)
                birthday_next_year_string = f"{birthday.DATE}.{next_year.strftime('%Y')}"
                birthday_next_year = datetime.strptime(
                    birthday_next_year_string, format)
                delta_temp = (birthday_next_year - today).days + 1  # Damit er bei 47h nicht 1 Tag sagt
                if delta_temp < delta:
                    next_birthday_date = birthday_dt
                    delta = delta_temp
        message = "Niemand hat hier Geburtstag! Wir sind in einer Zeitschleife gefangen."

        if next_birthday_date:
            for birthday in self.birthday_list[groupInfo["NAME"]]:
                # Dieses Jahr
                birthday_string = f"{birthday.DATE}.{date.today().strftime('%Y')}"
                birthday_dt = datetime.strptime(birthday_string, format)

                if birthday_dt == next_birthday_date:
                    next_birthdays.append(birthday)

            if delta == 1:
                message = f"Morgen hat{next_birthdays[0].PERSON} Geburtstag!🎉"
                if(len(next_birthdays)>1):
                    message=f"Morgen haben"    
                    for birthday in next_birthdays:
                        message+=f" {birthday.PERSON} und"
                    message = message[:-4]
                    message+=" Geburtstag!🎉"
            else:
                message = f"Als nächstes hat {next_birthdays[0].PERSON} am {next_birthdays[0].DATE} Geburtstag (noch {delta} Tage)"
                
                if(len(next_birthdays)>1):
                    message=f"Als nächstes haben"    
                    for birthday in next_birthdays:
                        message+=f" {birthday.PERSON} und"
                    message = message[:-4]
                    message+=" am {next_birthdays.DATE} Geburtstag (noch {delta} Tage)"
        return message

    def get_birthdaylist(self, groupInfo):
        message = "Alle Geburtstage:\n"
        liste = self.birthday_list[groupInfo["NAME"]]
        # for birthday in liste.sort():
        for birthday in liste:
            message += f"{birthday.PERSON}, {birthday.DATE}\n"

        return message

    def interpret_birthday_line(self, line):
        splitted = line.strip().split(",")
        if len(splitted) != 2:
            raise SyntaxError(
                "Invalid birthday syntax! Needs to be: person, date(Day.Month) eg.: Karl, 02.09")
        person = splitted[0].strip()
        if len(person) == 0:
            raise SyntaxError(
                "No name given! Needs to be: person, date(Day.Month) eg.: Karl, 02.09")

        splittedDate = splitted[1].strip().split(".")
        if len(splitted) != 2:
            raise SyntaxError(
                "Invalid date! Needs to be: person, date(Day.Month) eg.: Karl, 02.09")

        day = splittedDate[0].strip()
        month = splittedDate[1].strip()

        if len(day) > 2 or len(month) > 2 or len(day) == 0 or len(month) == 0:
            raise SyntaxError(
                "Invalid date! Needs to be: person, date(Day.Month) eg.: Karl, 02.09")
        if len(day) == 1:
            day = f"0{day}"
        if len(month) == 1:
            month = f"0{month}"

        return Birthday(person, f"{day}.{month}")

    def save_birthday(self, message, groupInfo):
        birthday: Birthday = None

        try:
            birthday = self.interpret_birthday_line(message)

            if birthday in self.birthday_list[groupInfo["NAME"]]:
                return "Birthday is already saved"

            self.birthday_list[groupInfo["NAME"]].append(birthday)
        except (SyntaxError, ValueError) as exc:
            self.log.warning(
                f"{self.tag}Error while interpreting birthday line:{message}; Error: {exc}")
            return f"{exc}"

        try:
            with open(groupInfo["BIRTHDAYDIR"], 'a', encoding='utf16') as f:
                f.write(f"{birthday.get_csv_line()}\n")
            return "Successfully added birthday"
        except IOError as exc:
            self.log.error(
                f"{self.tag}Adding new birthday'{birthday.get_csv_line()}' to file failed: {exc}")
            return "Saving birthday failed"