import sqlite3
import requests


from argparse import ArgumentParser
from os.path import expanduser
from datetime import datetime, timedelta


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


def warnmsg(msg):
    return f"\033[93m{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: 'http://127.0.0.1:8000/')

    Options:
      -d, --directory   Répertoire d'ccès à la base de données Hamster
      -n, --nhours      Nombre d'heures maximum en arrière à prendre en compte
      -h, --help        Affichage des arguments possibles
    """

    parser = 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='Number of hours in the past to check'
    )
    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:
        print(f"\n{failmsg('ERR:')} impossible d'accéder à {value_url}\n")
        return None


def recup_facts_local(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.now() - 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 recup_facts_remote(nh, url, headers, idUser):
    """Retourne la liste des facts contenus de la BDD distante (Timelapps)
    dont la date de départ remonte à nh heures.

    Si nh = 0, alors tous les facts distants seront récupérés.
    Retourne None si l'API n'est pas accessible."""

    if nh:
        now = datetime.now().replace(second=0, microsecond=0)
        start = now - timedelta(hours=nh)
        search = f"{url}api/facts?start_time__gte={start}&user={idUser}"
        return request_result(
            search,
            headers
        )

    else:
        return request_result(
            f"{url}api/facts?user={idUser}",
            headers
        )


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:
            if len(res) > 1:
                print(warnmsg(
                    f"{len(res)} utilisateurs trouvés pour le token {token}"
                ))
            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 = f"?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 = (
        f"?start_time={chaine_s}&end_time={chaine_e}"
        f"&activity__name={activity}&user={iduser}"
    )
    results = request_result(url+'api/facts'+search, headers)

    if results:
        if len(results) > 1:
            print(warnmsg(f"{len(results)} doubles de fact trouvés."))

        return results[0]['id']

    return 0


def main():
    parser = parser_scr()
    args = parser.parse_args()

    to_ken = args.token

    url = args.urltl
    if not (url.startswith("http") or url[-1] == '/'):
        print(warnmsg("Veuillez vérifier l'URL donnée."))

    if args.directory:
        hdir = args.directory
    else:
        hdir = expanduser("~/.local/share/hamster-applet/hamster.db")

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

    # On essaye d'ouvrir la BDD Hamster
    try:
        db_loc = sqlite3.connect(hdir)
    except Exception:
        # On ne peut pas ouvrir : on stoppe le script
        print(
            failmsg("ERR: Impossible d'ouvrir la base de données de Hamster.")
        )
        return 1

    # 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_local(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
        remote_facts = recup_facts_remote(nh, url, headers, idUser)
        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(warnmsg("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."))


if __name__ == '__main__':
    main()
