"""Connexion OAuth à un compte Google et téléversement de fichiers vers Google Drive,
via des appels REST directs (pas de dépendance google-api-python-client).

Portée volontairement restreinte à drive.file : l'application ne voit/modifie que les
fichiers qu'elle a elle-même créés dans le Drive de l'utilisateur.
"""

import json

import httpx

from app.config import (
    definir_jeton_google,
    obtenir_identifiants_google,
    obtenir_jeton_google,
)

SCOPE = "https://www.googleapis.com/auth/drive.file"
URL_AUTORISATION = "https://accounts.google.com/o/oauth2/v2/auth"
URL_JETON = "https://oauth2.googleapis.com/token"
URL_USERINFO = "https://www.googleapis.com/oauth2/v2/userinfo"
URL_FICHIERS = "https://www.googleapis.com/drive/v3/files"
URL_UPLOAD = "https://www.googleapis.com/upload/drive/v3/files"

NOM_DOSSIER_DRIVE = "ELFIRMA_Sauvegardes"


class ErreurGoogleDrive(Exception):
    pass


def construire_url_autorisation(redirect_uri: str, state: str) -> str:
    client_id, _ = obtenir_identifiants_google()
    if not client_id:
        raise ErreurGoogleDrive("Identifiants Google non configurés")
    params = {
        "client_id": client_id,
        "redirect_uri": redirect_uri,
        "response_type": "code",
        "scope": SCOPE,
        "access_type": "offline",
        "prompt": "consent",
        "state": state,
    }
    requete = httpx.Request("GET", URL_AUTORISATION, params=params)
    return str(requete.url)


def echanger_code_contre_jetons(code: str, redirect_uri: str) -> dict:
    client_id, client_secret = obtenir_identifiants_google()
    if not client_id or not client_secret:
        raise ErreurGoogleDrive("Identifiants Google non configurés")
    reponse = httpx.post(
        URL_JETON,
        data={
            "code": code,
            "client_id": client_id,
            "client_secret": client_secret,
            "redirect_uri": redirect_uri,
            "grant_type": "authorization_code",
        },
        timeout=15,
    )
    if reponse.status_code != 200:
        raise ErreurGoogleDrive(f"Échec de l'échange du code Google : {reponse.text}")
    return reponse.json()


def connecter_compte(code: str, redirect_uri: str) -> str:
    """Échange le code d'autorisation reçu sur /callback, récupère l'email du compte et
    stocke le refresh_token. Renvoie l'email du compte connecté."""
    jetons = echanger_code_contre_jetons(code, redirect_uri)
    refresh_token = jetons.get("refresh_token")
    if not refresh_token:
        raise ErreurGoogleDrive(
            "Google n'a pas renvoyé de jeton de renouvellement. Retirez l'accès d'ELFIRMA sur "
            "https://myaccount.google.com/permissions puis réessayez de vous connecter."
        )
    email = obtenir_email_compte(jetons["access_token"])
    definir_jeton_google(refresh_token, email)
    return email


def _obtenir_access_token() -> str:
    client_id, client_secret = obtenir_identifiants_google()
    jeton = obtenir_jeton_google()
    refresh_token = jeton.get("refresh_token")
    if not client_id or not client_secret or not refresh_token:
        raise ErreurGoogleDrive("Aucun compte Google Drive connecté")

    reponse = httpx.post(
        URL_JETON,
        data={
            "client_id": client_id,
            "client_secret": client_secret,
            "refresh_token": refresh_token,
            "grant_type": "refresh_token",
        },
        timeout=15,
    )
    if reponse.status_code != 200:
        raise ErreurGoogleDrive(f"Impossible de rafraîchir l'accès Google Drive : {reponse.text}")
    return reponse.json()["access_token"]


def obtenir_email_compte(access_token: str) -> str:
    reponse = httpx.get(URL_USERINFO, headers={"Authorization": f"Bearer {access_token}"}, timeout=15)
    if reponse.status_code != 200:
        raise ErreurGoogleDrive("Impossible de récupérer l'adresse du compte Google")
    return reponse.json().get("email", "")


def compte_connecte() -> str | None:
    """Renvoie l'email du compte connecté, ou None si aucun compte n'est connecté."""
    jeton = obtenir_jeton_google()
    return jeton.get("email") or None


def _obtenir_ou_creer_dossier(access_token: str) -> str:
    entetes = {"Authorization": f"Bearer {access_token}"}
    requete = (
        f"name='{NOM_DOSSIER_DRIVE}' and mimeType='application/vnd.google-apps.folder' "
        f"and trashed=false"
    )
    reponse = httpx.get(
        URL_FICHIERS, headers=entetes, params={"q": requete, "fields": "files(id,name)"}, timeout=15
    )
    if reponse.status_code == 200:
        fichiers = reponse.json().get("files", [])
        if fichiers:
            return fichiers[0]["id"]

    creation = httpx.post(
        URL_FICHIERS,
        headers=entetes,
        json={"name": NOM_DOSSIER_DRIVE, "mimeType": "application/vnd.google-apps.folder"},
        timeout=15,
    )
    if creation.status_code not in (200, 201):
        raise ErreurGoogleDrive(f"Impossible de créer le dossier Drive : {creation.text}")
    return creation.json()["id"]


def televerser_fichier(chemin_local, nom_fichier: str) -> None:
    """Téléverse un fichier vers le dossier ELFIRMA_Sauvegardes du Drive connecté."""
    access_token = _obtenir_access_token()
    dossier_id = _obtenir_ou_creer_dossier(access_token)

    metadonnees = {"name": nom_fichier, "parents": [dossier_id]}
    with open(chemin_local, "rb") as fichier:
        reponse = httpx.post(
            URL_UPLOAD,
            headers={"Authorization": f"Bearer {access_token}"},
            params={"uploadType": "multipart"},
            files={
                "metadata": ("metadata", json.dumps(metadonnees), "application/json"),
                "file": (nom_fichier, fichier, "application/zip"),
            },
            timeout=60,
        )
    if reponse.status_code not in (200, 201):
        raise ErreurGoogleDrive(f"Échec du téléversement vers Google Drive : {reponse.text}")
