import sqlite3
import datetime
import argparse
import os

import requests


def okmsg(msg):
    return f"\033[92m{msg}\033[0m"


def failmsg(msg):
    return f"\033[91m{msg}\033[0m"


def parser_scr():
    """Parse les arguments (paramètres/options) donnés à la commande
    d'exécution du script.

    Paramètres :
      token         Jeton d'identification de l'utilisateur
      url           URL d'accès à Timelapps (ex: '127.0.0.1:8000/')

    Options:
      -d, --directory       Répertoire d'ccès à la base de données Hamster
      -n, --nhours          Nombre d'heures des facts à traiter
      -h, --help            Affichage des arguments possibles
    """

    parser = argparse.ArgumentParser(description='Timelapps Migration Script.')
    parser.add_argument(
        'token', metavar='token', type=str, help='Token of your account'
    )
    parser.add_argument(
        'urltl', metavar='url', type=str, help='URL to join timelapps'
    )
    parser.add_argument(
        '-d', '--directory', type=str, help='path to Hamster Database'
    )
    parser.add_argument(
        '-n',
        '--nhours',
        type=int,
        help='add of fact -N heure'
    )
    return parser


def request_result(value_url, headers):
    """Retourne les résultats d'une requête GET vers une vue API."""

    response = requests.get(value_url, headers=headers)
    if response.status_code == 200:
        results = response.json()['results']
        while response.json()['next']:
            response = requests.get(response.json()['next'], headers=headers)
            results.extend(response.json()['results'])
        return results
    else:
        return None


def recup_facts(db_loc, nh):
    """
    Retourne la liste des facts contenus dans la BDD locale de Hamster sous
    forme de liste à quatre index:
      [0] - start_time
      [1] - end_time
      [2] - activity__name
      [3] - activity__category__name
    """

    cursor = db_loc.cursor()
    if nh == 0:
        cursor.execute(
            '''SELECT f.start_time, f.end_time, a.name, c.name
            FROM facts AS f, activities AS a, categories AS c
            WHERE f.activity_id=a.id AND a.category_id=c.id ;'''
        )
    else:
        d = (datetime.datetime.now() - datetime.timedelta(hours=nh))
        cursor.execute(
            '''SELECT f.start_time, f.end_time, a.name, c.name
            FROM facts AS f, activities AS a, categories AS c
            WHERE f.activity_id=a.id AND a.category_id=c.id
            AND f.start_time > ?;''', (d,)
        )
    return cursor.fetchall()


def srch_user(token, url, headers):
    """Cherche si un jeton d'authentification donné correspond à un
    utilisateur de la BDD Timelapps. Renvoie l'id de l'utilisateur
    si oui, 0 sinon."""

    results = requests.get(url+'api/token?key='+token, headers=headers)
    if results.status_code == 200:
        res = results.json()['results']
        if res:
            return res[0]['user']
    return 0


def search_cat(url, headers, category):
    """Cherche si un nom de catégorie donné correspond à une catégorie de la
    BDD Timelapps. Renvoie l'id de la catégorie si oui, 0 sinon."""

    results = requests.get(
        url+'api/categories?name='+category, headers=headers)

    if results.status_code == 200:
        res = results.json()['results']
        if res:
            return res[0]['id']

        print(
            f'la categorie "{category}" n\'existe pas sur Timelapps,'
            ' pas d\'envoi'
        )

    return None


def search_act(url, headers, activity, idcat, category):
    """Cherche si un nom d'activité et un id et un nom de catégorie donnés
    correspondent à une activité de la BDD Timelapps. Renvoie l'id de
    l'activité si oui, 0 sinon."""

    idAct = 0
    search = '?name='+activity+'&category__name='+category
    results = request_result(url+'api/activities'+search, headers)
    for result in results:
        c = result['category']
        if c == idcat and result['name'] == activity:
            idAct = result['id']
            break
    if not idAct:
        data = {'name': activity, 'category': idcat}
        results = requests.post(
            url+'api/activities', data=data, headers=headers)
        idAct = results.json()['id']
    return idAct


def search_fact(url, headers, start_t, end_t, iduser, idact, activity):
    """Cherche s'il existe dans la BDD Timelapps un fact correspondant à une
    date de départ, une date de fin, un id d'utilisateur et une activité.
    Renvoie l'id du fact si oui, 0 sinon."""

    start = list(start_t)
    end = list(end_t)
    start[10] = 'T'
    end[10] = 'T'
    chaine_s = ''.join(start)
    chaine_e = ''.join(end)
    search = '?start_time='+chaine_s+'&end_time='+chaine_e
    search = search+'&activity__name='+activity
    results = request_result(url+'api/facts'+search, headers)
    for result in results:
        t = list(result['start_time'])
        t[10] = ' '
        t2 = ''.join(t)
        s = list(result['end_time'])
        s[10] = ' '
        s2 = ''.join(s)
        if iduser == result['user']:
            if start_t == t2 and end_t == s2 and idact == result['activity']:
                return result['id']
    return 0


def main():
    parser = parser_scr()
    args = parser.parse_args()
    to_ken = args.token
    if args.directory:
        hdir = args.directory
    else:
        hdir = os.path.expanduser("~/.local/share/hamster-applet/hamster.db")
    url = args.urltl

    if args.nhours:
        nh = args.nhours
    else:
        nh = 0

    test_opening = True
    # On essaye d'ouvrir la BDD Hamster
    try:
        db_loc = sqlite3.connect(hdir)
    except Exception:
        test_opening = False

    if test_opening:
        # Récupération des données de Timelapps grâce au token
        headers = {'Authorization': 'Token ' + to_ken}

        idUser = srch_user(to_ken, url, headers)
        if idUser:
            # Récupération des facts de Hamster
            local_facts = recup_facts(db_loc, nh)

            # Listes des catégories/activités déjà utilisées
            name_cats = {}
            name_acts = {}

            # Liste des facts déjà traités
            remote_facts_ids = []

            # Compteur de facts ajoutés
            added = 0

            # # Vérification des facts locaux par rapport à Timelapps
            for fact in local_facts:

                if fact[0] and fact[1] and fact[2] and fact[3]:
                    if fact[3] in name_cats:
                        idCat = name_cats[fact[3]]
                    else:
                        # On cherche la catégorie fact[3] sur Timelapps
                        idCat = search_cat(url, headers, fact[3])
                        if not idCat:
                            continue
                        name_cats[fact[3]] = idCat

                    concat = f"{fact[2]}{fact[3]}"
                    if concat in name_acts:
                        idAct = name_acts[concat]
                    else:
                        idAct = search_act(
                            url, headers, fact[2], idCat, fact[3])
                        name_acts[concat] = idAct

                    idFact = search_fact(
                        url, headers, fact[0], fact[1],
                        idUser, idAct, fact[2]
                    )
                    if not idFact:
                        # Le fact n'existe pas sur Timelapps, on l'ajoute
                        data = {
                            'user': idUser,
                            'start_time': fact[0],
                            'end_time': fact[1],
                            'activity': idAct
                        }
                        requests.post(
                            url+'api/facts', data=data, headers=headers)

                        idFact = search_fact(
                            url, headers, fact[0], fact[1],
                            idUser, idAct, fact[2]
                        )
                        added += 1

                    remote_facts_ids.append(idFact)

            print(
                f"{len(remote_facts_ids)} facts vérifiés ({added} ajoutés)."
            )

            # # Vérification des facts distants désynchronisés
            # On ne fait pas cette étape si l'option -n est donnée
            if not nh:
                remote_facts = request_result(
                    f"{url}api/facts?user={idUser}",
                    headers
                )

                if len(local_facts) < len(remote_facts):
                    print(
                        "Les temps suivants vont être supprimés de Timelapps:")
                    for fact in remote_facts:
                        if fact['id'] not in remote_facts_ids:
                            print(
                                f"  Début: {fact['start_time']}   "
                                f"Fin : {fact['end_time']}"
                            )

                    choice = None
                    while choice != 'o' and choice != 'n':
                        choice = input("Confirmer ? [o/n] ")

                    if choice == 'o':
                        for fact in remote_facts:
                            if fact['id'] not in remote_facts_ids:
                                print(
                                    f"[Début: {fact['start_time']}     "
                                    f"Fin : {fact['end_time']}]",
                                    end=" "
                                )
                                response = requests.delete(
                                    f"{url}api/destroy-fact/{fact['id']}",
                                    headers=headers
                                )
                                if response.status_code == 202:
                                    print(okmsg("OK"))
                                else:
                                    print(failmsg(
                                        f"ERR: code {response.status_code}"
                                    ))

                    else:
                        print("Les temps n'ont pas été supprimés.")

                else:
                    print("Pas de temps à supprimer.")

            db_loc.close()
            print("Fin de script.")

        else:
            print(failmsg("ERR: Utilisateur inconnu."))

    else:
        print(
            failmsg("ERR: Impossible d'ouvrir la base de données de Hamster.")
        )


if __name__ == '__main__':
    main()
