"""
Test de l'api Assiduité

Ecrit par HARTMANN Matthias

"""

from random import randint

from tests.api.setup_test_api import GET, POST_JSON, APIError, api_headers

ETUDID = 1
FAUX = 42069
FORMSEMESTREID = 1
MODULE = 1


ASSIDUITES_FIELDS = {
    "assiduite_id": int,
    "etudid": int,
    "moduleimpl_id": int,
    "date_debut": str,
    "date_fin": str,
    "etat": str,
    "desc": str,
    "entry_date": str,
    "user_id": str,
    "est_just": bool,
}

CREATE_FIELD = {"assiduite_id": int}
BATCH_FIELD = {"errors": dict, "success": dict}

COUNT_FIELDS = {"compte": int, "journee": int, "demi": int, "heure": float}

TO_REMOVE = []


def check_fields(data: dict, fields: dict = None):
    """
    Cette fonction permet de vérifier que le dictionnaire data
    contient les bonnes clés et les bons types de valeurs.

    Args:
        data (dict): un dictionnaire (json de retour de l'api)
        fields (dict, optional): Un dictionnaire représentant les clés et les types d'une réponse.
    """
    if fields is None:
        fields = ASSIDUITES_FIELDS
    assert set(data.keys()) == set(fields.keys())
    for key in data:
        if key in ("moduleimpl_id", "desc", "user_id"):
            assert isinstance(data[key], fields[key]) or data[key] is None
        else:
            assert isinstance(data[key], fields[key])


def check_failure_get(path: str, headers: dict, err: str = None):
    """
    Cette fonction vérifiée que la requête GET renvoie bien un 404

    Args:
        path (str): la route de l'api
        headers (dict): le token d'auth de l'api
        err (str, optional): L'erreur qui est sensée être fournie par l'api.

    Raises:
        APIError: Une erreur car la requête a fonctionné (mauvais comportement)
    """

    try:
        GET(path=path, headers=headers)
        # ^ Renvoi un 404
    except APIError as api_err:
        if err is not None:
            assert api_err.payload["message"] == err
    else:
        raise APIError("Le GET n'aurait pas du fonctionner")


def check_failure_post(path: str, headers: dict, data: dict, err: str = None):
    """
    Cette fonction vérifiée que la requête POST renvoie bien un 404

    Args:
        path (str): la route de l'api
        headers (dict): le token d'auth
        data (dict): un dictionnaire (json) à envoyer
        err (str, optional): L'erreur qui est sensée être fournie par l'api.

    Raises:
        APIError: Une erreur car la requête a fonctionné (mauvais comportement)
    """

    try:
        data = POST_JSON(path=path, headers=headers, data=data)
        # ^ Renvoi un 404
    except APIError as api_err:
        if err is not None:
            assert api_err.payload["message"] == err
    else:
        raise APIError("Le GET n'aurait pas du fonctionner")


def create_data(etat: str, day: str, module: int = None, desc: str = None):
    """
    Permet de créer un dictionnaire assiduité

    Args:
        etat (str): l'état de l'assiduité (PRESENT,ABSENT,RETARD)
        day (str): Le jour de l'assiduité
        module (int, optional): Le moduleimpl_id associé
        desc (str, optional): Une description de l'assiduité (eg: motif retard )

    Returns:
        dict: la représentation d'une assiduité
    """
    data = {
        "date_debut": f"2022-01-{day}T08:00",
        "date_fin": f"2022-01-{day}T10:00",
        "etat": etat,
    }

    if module is not None:
        data["moduleimpl_id"] = module
    if desc is not None:
        data["desc"] = desc

    return data


def test_route_assiduite(api_headers):
    """test de la route /assiduite/<assiduite_id:int>"""

    # Bon fonctionnement == id connu
    data = GET(path="/assiduite/1", headers=api_headers)
    check_fields(data)

    # Mauvais Fonctionnement == id inconnu

    check_failure_get(
        f"/assiduite/{FAUX}",
        api_headers,
    )


def test_route_count_assiduites(api_headers):
    """test de la route /assiduites/<etudid:int>/count"""

    # Bon fonctionnement

    data = GET(path=f"/assiduites/{ETUDID}/count", headers=api_headers)
    check_fields(data, COUNT_FIELDS)

    metrics = {"heure", "compte"}
    data = GET(
        path=f"/assiduites/{ETUDID}/count/query?metric={','.join(metrics)}",
        headers=api_headers,
    )

    assert set(data.keys()) == metrics

    # Mauvais fonctionnement

    check_failure_get(f"/assiduites/{FAUX}/count", api_headers)


def test_route_assiduites(api_headers):
    """test de la route /assiduites/<etudid:int>"""

    # Bon fonctionnement

    data = GET(path=f"/assiduites/{ETUDID}", headers=api_headers)
    assert isinstance(data, list)
    for ass in data:
        check_fields(ass, ASSIDUITES_FIELDS)

    data = GET(path=f"/assiduites/{ETUDID}/query?", headers=api_headers)
    assert isinstance(data, list)
    for ass in data:
        check_fields(ass, ASSIDUITES_FIELDS)

    # Mauvais fonctionnement
    check_failure_get(f"/assiduites/{FAUX}", api_headers)
    check_failure_get(f"/assiduites/{FAUX}/query?", api_headers)


def test_route_formsemestre_assiduites(api_headers):
    """test de la route /assiduites/formsemestre/<formsemestre_id:int>"""

    # Bon fonctionnement

    data = GET(path=f"/assiduites/formsemestre/{FORMSEMESTREID}", headers=api_headers)
    assert isinstance(data, list)
    for ass in data:
        check_fields(ass, ASSIDUITES_FIELDS)

    data = GET(
        path=f"/assiduites/formsemestre/{FORMSEMESTREID}/query?", headers=api_headers
    )
    assert isinstance(data, list)
    for ass in data:
        check_fields(ass, ASSIDUITES_FIELDS)

    # Mauvais fonctionnement
    check_failure_get(
        f"/assiduites/formsemestre/{FAUX}",
        api_headers,
        err="le paramètre 'formsemestre_id' n'existe pas",
    )
    check_failure_get(
        f"/assiduites/formsemestre/{FAUX}/query?",
        api_headers,
        err="le paramètre 'formsemestre_id' n'existe pas",
    )


def test_route_count_formsemestre_assiduites(api_headers):
    """test de la route /assiduites/formsemestre/<formsemestre_id:int>/count"""

    # Bon fonctionnement

    data = GET(
        path=f"/assiduites/formsemestre/{FORMSEMESTREID}/count", headers=api_headers
    )
    check_fields(data, COUNT_FIELDS)
    metrics = {"heure", "compte"}
    data = GET(
        path=f"/assiduites/formsemestre/{FORMSEMESTREID}/count/query?metric={','.join(metrics)}",
        headers=api_headers,
    )
    assert set(data.keys()) == metrics

    # Mauvais fonctionnement
    check_failure_get(
        f"/assiduites/formsemestre/{FAUX}/count",
        api_headers,
        err="le paramètre 'formsemestre_id' n'existe pas",
    )
    check_failure_get(
        f"/assiduites/formsemestre/{FAUX}/count/query?",
        api_headers,
        err="le paramètre 'formsemestre_id' n'existe pas",
    )


def test_route_create(api_headers):
    """test de la route /assiduite/<etudid:int>/create"""

    # -== Unique ==-

    # Bon fonctionnement
    data = create_data("present", "01")

    res = POST_JSON(f"/assiduite/{ETUDID}/create", [data], api_headers)
    check_fields(res, BATCH_FIELD)
    assert len(res["success"]) == 1

    TO_REMOVE.append(res["success"]["0"]["assiduite_id"])

    data2 = create_data("absent", "02", MODULE, "desc")
    res = POST_JSON(f"/assiduite/{ETUDID}/create", [data2], api_headers)
    check_fields(res, BATCH_FIELD)
    assert len(res["success"]) == 1

    TO_REMOVE.append(res["success"]["0"]["assiduite_id"])

    # Mauvais fonctionnement
    check_failure_post(f"/assiduite/{FAUX}/create", api_headers, [data])

    res = POST_JSON(f"/assiduite/{ETUDID}/create", [data], api_headers)
    check_fields(res, BATCH_FIELD)
    assert len(res["errors"]) == 1
    assert (
        res["errors"]["0"]
        == "Duplication des assiduités (la période rentrée rentre en conflit avec une assiduité enregistrée)"
    )

    res = POST_JSON(
        f"/assiduite/{ETUDID}/create", [create_data("absent", "03", FAUX)], api_headers
    )
    check_fields(res, BATCH_FIELD)
    assert len(res["errors"]) == 1
    assert res["errors"]["0"] == "param 'moduleimpl_id': invalide"

    # -== Multiple ==-

    # Bon Fonctionnement

    etats = ["present", "absent", "retard"]
    data = [
        create_data(etats[d % 3], 10 + d, MODULE if d % 2 else None)
        for d in range(randint(3, 5))
    ]

    res = POST_JSON(f"/assiduite/{ETUDID}/create", data, api_headers)
    check_fields(res, BATCH_FIELD)
    for dat in res["success"]:
        check_fields(res["success"][dat], CREATE_FIELD)
        TO_REMOVE.append(res["success"][dat]["assiduite_id"])

    # Mauvais Fonctionnement

    data2 = [
        create_data("present", "01"),
        create_data("present", "25", FAUX),
        create_data("blabla", 26),
        create_data("absent", 32),
    ]

    res = POST_JSON(f"/assiduite/{ETUDID}/create", data2, api_headers)
    check_fields(res, BATCH_FIELD)
    assert len(res["errors"]) == 4

    assert (
        res["errors"]["0"]
        == "Duplication des assiduités (la période rentrée rentre en conflit avec une assiduité enregistrée)"
    )
    assert res["errors"]["1"] == "param 'moduleimpl_id': invalide"
    assert res["errors"]["2"] == "param 'etat': invalide"
    assert (
        res["errors"]["3"]
        == "param 'date_debut': format invalide, param 'date_fin': format invalide"
    )


def test_route_edit(api_headers):
    """test de la route /assiduite/<assiduite_id:int>/edit"""

    # Bon fonctionnement

    data = {"etat": "retard", "moduleimpl_id": MODULE}
    res = POST_JSON(f"/assiduite/{TO_REMOVE[0]}/edit", data, api_headers)
    assert res == {"OK": True}

    data["moduleimpl_id"] = None
    res = POST_JSON(f"/assiduite/{TO_REMOVE[1]}/edit", data, api_headers)
    assert res == {"OK": True}

    # Mauvais fonctionnement

    check_failure_post(f"/assiduite/{FAUX}/edit", api_headers, data)
    data["etat"] = "blabla"
    check_failure_post(
        f"/assiduite/{TO_REMOVE[2]}/edit",
        api_headers,
        data,
        err="param 'etat': invalide",
    )


def test_route_delete(api_headers):
    """test de la route /assiduite/delete"""
    # -== Unique ==-

    # Bon fonctionnement
    data = TO_REMOVE[0]

    res = POST_JSON("/assiduite/delete", [data], api_headers)
    check_fields(res, BATCH_FIELD)
    for dat in res["success"]:
        assert res["success"][dat] == {"OK": True}

    # Mauvais fonctionnement
    res = POST_JSON("/assiduite/delete", [data], api_headers)
    check_fields(res, BATCH_FIELD)
    assert len(res["errors"]) == 1

    # -== Multiple ==-

    # Bon Fonctionnement

    data = TO_REMOVE[1:]

    res = POST_JSON("/assiduite/delete", data, api_headers)
    check_fields(res, BATCH_FIELD)
    for dat in res["success"]:
        assert res["success"][dat] == {"OK": True}

    # Mauvais Fonctionnement

    data2 = [
        FAUX,
        FAUX + 1,
        FAUX + 2,
    ]

    res = POST_JSON("/assiduite/delete", data2, api_headers)
    check_fields(res, BATCH_FIELD)
    assert len(res["errors"]) == 3

    assert all([res["errors"][i] == "Assiduite non existante" for i in res["errors"]])
