import calendar

#############################################################################
# Question 1                                                                #
#############################################################################


def est_bissextile(annee):
    """Renvoie True si annee est bissextile, False sinon."""
    if annee % 400 == 0:
        return True
    elif annee % 100 == 0:
        return False
    elif annee % 4 == 0:
        return True
    else:
        return False


# Forme compacte equivalente :
# def est_bissextile(annee):
#     return annee % 4 == 0 and (annee % 100 != 0 or annee % 400 == 0)


def test_est_bissextile():
    assert est_bissextile(2024) == True
    assert est_bissextile(2100) == False
    assert est_bissextile(2000) == True


#############################################################################
# Question 2                                                                #
#############################################################################


def determiner_phase(jour):
    """Renvoie le numero de la phase du cycle correspondant au jour donne.
       1 : regles, 2 : phase folliculaire, 3 : ovulation, 4 : phase luteale."""
    assert 1 <= jour <= 28, "le jour du cycle doit être compris entre 1 et 28"
    if jour <= 5:
        return 1
    elif jour <= 13:
        return 2
    elif jour == 14:
        return 3
    else:
        return 4


def test_determiner_phase():
    for i in range(1, 29):
        print("Jour", i, "... phase:", determiner_phase(i))


#############################################################################
# Fonctions fournies pour la question 3                                     #
#############################################################################
def jours_dans_mois(annee, mois):
    """Renvoie le nombre de jours dans un mois donné d'une année donnée.
       Utilise le module calendar pour gérer les années bissextiles."""
    if mois == 2:  # février
        return 29 if calendar.isleap(annee) else 28
    elif mois in [1, 3, 5, 7, 8, 10, 12]:
        return 31
    else:
        return 30


def ajouter_jours(date, nb_jours):
    """Ajoute nb_jours à une date donnée et renvoie la nouvelle date.
       La date est représentée par un tuple (jour, mois, année)."""
    jour, mois, annee = date
    jour = jour + nb_jours

    # Ajustement du jour et du mois si dépassement
    while jour > jours_dans_mois(annee, mois):
        jour = jour - jours_dans_mois(annee, mois)
        mois = mois + 1
        if mois > 12:  # passage à l'année suivante
            mois = 1
            annee = annee + 1

    return (jour, mois, annee)


#############################################################################
# Question 3                                                                #
#############################################################################


def test_ajouter_jours():
    # 1. Test simple - déjà donné dans l'énoncé
    assert ajouter_jours((7, 9, 2025), 3) == (10, 9, 2025)

    # 2. Changement de mois : mois de 30 jours (septembre)
    assert ajouter_jours((20, 9, 2025), 13) == (3, 10, 2025)

    # 3. Changement de mois : mois de 31 jours (mai)
    assert ajouter_jours((29, 5, 2026), 4) == (2, 6, 2026)

    # 4. Changement de mois : février à 28 jours en 2026
    assert ajouter_jours((27, 2, 2026), 5) == (4, 3, 2026)

    # 5. Changement de mois : février à 29 jours en 2024
    assert ajouter_jours((27, 2, 2024), 5) == (3, 3, 2024)

    # 6. Changement d'année
    assert ajouter_jours((30, 12, 2025), 2) == (1, 1, 2026)


#############################################################################
# Question 4                                                                #
#############################################################################


def calendrier_cycles(date_regles):
    """Renvoie une chaîne de caractère contenant au format iCalendar, l'ensemble
    des dates de début de règles qui se présentent dans les 100 jours suivants
    `date_regles`, date incluse.

    Hypothèse : cycle régulier de 28 jours. """

    cal_lignes = ['BEGIN:VCALENDAR', 'VERSION:2.0', 'PRODID:']

    date_courante = date_regles
    jours_ecoules = 0

    # On ajoute les dates tant que l'on ne dépasse pas 100 jours écoulés
    while jours_ecoules + 28 <= 100:
        jour, mois, annee = date_courante
        cal_lignes.append('BEGIN:VEVENT')
        cal_lignes.append('SUMMARY: Règles')
        # Correction : chaque composante est completee par un zero pour que
        # DTSTART fasse toujours 8 caracteres, au format AAAAMMJJ.
        if mois <= 9:
            mois_str = "0" + str(mois)
        else:
            mois_str = str(mois)
        if jour <= 9:
            jour_str = "0" + str(jour)
        else:
            jour_str = str(jour)
        date = str(annee)+mois_str+jour_str
        cal_lignes.append('DTSTART:'+date)
        cal_lignes.append('END:VEVENT')
        date_courante = ajouter_jours(date_courante, 28)
        jours_ecoules += 28

    cal_lignes.append('END:VCALENDAR')

    # La méthode join va renvoyer ici une unique chaîne contenant toutes les
    # chaînes de la liste séparées par des sauts de lignes.
    return '\n'.join(cal_lignes)


def test_calendrier_cycles():
    '''Crée un calendrier et le charge avec le module ics pour vérifier sa
    validité.

    Nécessite que le module ics soit présent sur la machine (pip install ics).
    '''
    from ics import Calendar
    c = calendrier_cycles((12, 3, 2026))
    print(c)
    cal = Calendar(c)
    print(cal.events)


if __name__ == '__main__':
    test_est_bissextile()
    test_determiner_phase()
    test_ajouter_jours()
    print(calendrier_cycles((12, 3, 2026)))
