#!/usr/bin/env python3

import os
import sys
import json
import tempfile
import shutil
import subprocess
from typing import Optional
from urllib.parse import urlparse

from fastapi import Depends, FastAPI, HTTPException, Header,  Request
from loguru import logger
import glob

# Configuration du logging
logger.remove()
logger.add(sys.stdout, format="{time} | {level} | {message}", level="INFO")

logger.info("🚀 Initialisation du plugin cluster-filter")

# Initialisation de FastAPI
app = FastAPI(
    title="Cluster Filter Plugin",
    description="Plugin pour filtrer les clusters selon leur état enabled dans les fichiers .argocd.json",
)

# Token d'authentification
AUTH_TOKEN = os.environ.get("AUTH_TOKEN", "my-plugin-token")
GIT_CONFIG_PATH = os.environ.get("GIT_CONFIG_PATH", "/etc/gitconfig")

# Répertoire temporaire pour les clones Git
TMP_DIR = os.environ.get("TMP_DIR", "/tmp")

# Middleware d'authentification
async def verify_token(authorization: Optional[str] = Header(None)):
    """
    Vérifie le token d'authentification dans l'en-tête HTTP
    
    Args:
        authorization: Valeur de l'en-tête Authorization (format "Bearer <token>")
        
    Returns:
        Le token validé
        
    Raises:
        HTTPException: Si l'authentification échoue
    """
    if not authorization or not authorization.startswith("Bearer "):
        logger.error("❌ Entête d'authentification manquant ou incorrect")
        raise HTTPException(status_code=401, detail="Authentification invalide")

    token = authorization.replace("Bearer ", "")
    
    # Vérifier le token (vous pouvez adapter cette logique selon vos besoins)
    if token != AUTH_TOKEN:
        logger.error("❌ Token d'authentification invalide")
        raise HTTPException(status_code=401, detail="Token invalide")
    
    logger.debug("✅ Authentification réussie")
    return token

def clone_repo(repo_url, revision, tmp_dir,):
    """Clone un dépôt Git dans un répertoire temporaire en utilisant HTTPS avec PAT token"""
    os.makedirs(tmp_dir, exist_ok=True)
    repo_name = repo_url.split('/')[-1].replace('.git', '')
    repo_dir = os.path.join(tmp_dir, repo_name)
    
    # Supprimer le répertoire s'il existe déjà
    if os.path.exists(repo_dir):
        shutil.rmtree(repo_dir)
    
    logger.info(f"📥 Clonage du dépôt {repo_url}")
    
    # Configurer l'environnement pour Git
    env = os.environ.copy()
    pat_token = os.getenv("PAT_TOKEN")
    # Configuration pour HTTPS avec PAT token
    if pat_token and repo_url.startswith("https://"):
        # Reformater l'URL pour inclure le token
        parsed_url = urlparse(repo_url)
        # Format: https://{token}@{domain}/{path}
        auth_url = f"https://{pat_token}@{parsed_url.netloc}{parsed_url.path}"
        repo_url = auth_url
        logger.debug("🔑 Utilisation du PAT token pour l'authentification HTTPS")
    
    # Configuration proxy Git si nécessaire
    if os.path.exists(GIT_CONFIG_PATH):
        env["GIT_CONFIG_GLOBAL"] = GIT_CONFIG_PATH
    
    try:
        # Cloner le dépôt
        cmd = ["git", "clone", repo_url, repo_dir]
        # Exécution sans afficher le token dans les logs
        process = subprocess.Popen(
            cmd,
            env=env,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE
        )
        stdout, stderr = process.communicate()
        
        if process.returncode != 0:
            # Masquer le token dans les logs d'erreur
            safe_stderr = stderr.decode().replace(pat_token or "", "***") if pat_token else stderr.decode()
            logger.error(f"❌ Erreur lors du clone: {safe_stderr}")
            raise RuntimeError(f"Erreur Git: Code {process.returncode}")
        
        # Checkout de la révision spécifiée
        cmd = ["git", "-C", repo_dir, "checkout", revision]
        subprocess.run(cmd, check=True, env=env)
        
        return repo_dir
    except Exception as e:
        logger.error(f"❌ Erreur lors du clone/checkout: {str(e)}")
        raise RuntimeError(f"Erreur Git: {str(e)}")
    
def find_files(base_dir, pattern):
    """Trouve tous les fichiers correspondant au pattern dans base_dir"""
    search_pattern = os.path.join(base_dir, pattern)
    logger.debug(f"Recherche avec pattern: {search_pattern}")
    
    # Utiliser glob pour trouver les fichiers
    files = glob.glob(search_pattern, recursive=True)
    
    if not files:
        # Si le pattern est trop spécifique, essayer de le généraliser
        if "**" not in pattern:
            generalized_pattern = "**/" + os.path.basename(pattern)
            logger.debug(f"Pattern généralisé: {generalized_pattern}")
            files = glob.glob(os.path.join(base_dir, generalized_pattern), recursive=True)
    
    logger.info(f"🔍 {len(files)} fichiers trouvés avec le pattern {pattern}")
    return files

def parse_path_info(file_path, base_dir):
    """Extrait les informations de chemin relatives au fichier"""
    relative_path = os.path.relpath(file_path, base_dir)
    dir_path = os.path.dirname(relative_path)
    basename = os.path.basename(os.path.dirname(file_path))
    
    return {
        "path": dir_path,
        "basename": basename
    }

@app.post("/api/v1/getparams.execute")
async def get_params(request: Request, token: str = Depends(verify_token)):
    """
    Endpoint pour le plugin generator ArgoCD.
    Format attendu par ArgoCD: {"applicationSetName": "...", "input": {"parameters": {...}}}
    Format de réponse: {"output": {"parameters": [...]}}
    """
    body = await request.json()
    logger.info(f"📥 Requête reçue pour ApplicationSet: {body.get('applicationSetName')}")
    
    # Récupération des paramètres d'entrée
    input_params = body.get("input", {}).get("parameters", {})
    result_params = []
    
    # Vérification des paramètres requis
    repo_url = input_params.get("repoURL")
    if not repo_url:
        logger.error("❌ Paramètre 'repoURL' manquant")
        return {"output": {"parameters": []}}
    
    revision = input_params.get("revision", "main")
    file_pattern = input_params.get("path", "**/.argocd.json")
    
    logger.info(f"🔍 Recherche des fichiers dans {repo_url} (révision: {revision}) avec pattern: {file_pattern}")
    
    # Créer un répertoire temporaire pour le clone
    with tempfile.TemporaryDirectory(dir=TMP_DIR) as tmp_dir:
        try:
            # Cloner le dépôt
            repo_dir = clone_repo(repo_url, revision, tmp_dir)
            logger.info(f"📂 Dépôt cloné dans {repo_dir}")
            
            # Trouver les fichiers .argocd.json
            argocd_files = find_files(repo_dir, file_pattern)
            
            if not argocd_files:
                logger.warning(f"⚠️ Aucun fichier trouvé avec le pattern {file_pattern}")
                return {"output": {"parameters": []}}
            
            # Traiter chaque fichier .argocd.json
            for file_path in argocd_files:
                logger.info(f"📄 Traitement du fichier {file_path}")
                
                try:
                    # Lire et parser le fichier JSON
                    with open(file_path, 'r') as f:
                        file_data = json.load(f)
                    
                    # Extraire les informations de chemin
                    path_info = parse_path_info(file_path, repo_dir)
                    
                    # Vérifier si des clusters sont définis
                    if "clusters" in file_data:
                        clusters_data = file_data.get("clusters", {})
                        
                        for cluster_name, cluster_config in clusters_data.items():
                            if cluster_config.get("enabled", False):
                                logger.info(f"✅ Cluster {cluster_name} activé pour {path_info['basename']}")
                                
                                # Construire les paramètres pour ce cluster
                                param = {
                                    "name": cluster_name,  # Pour le nom de l'application
                                    "path": path_info,     # Pour les informations de chemin
                                    "project": file_data.get("project", "default"),
                                    "destination": {
                                        "name": cluster_name,  # Destinataire du déploiement
                                        "namespace": file_data.get("destination", {}).get("namespace", "default")
                                    },
                                    "source": file_data.get("source", {}),
                                    "syncPolicy": file_data.get("syncPolicy", {}),
                                    # Paramètres spécifiques pour les templating
                                    "valuesRevision": cluster_config.get("valuesRevision", "main"),
                                    "chartRevision": cluster_config.get("chartRevision", file_data.get("source", {}).get("targetRevision", "main")),
                                    # Paramètres pour la fonction 'dig' dans le template
                                    "clusters": {
                                        cluster_name: cluster_config
                                    }
                                }
                                
                                result_params.append(param)
                            else:
                                logger.info(f"🛑 Cluster {cluster_name} désactivé pour {path_info['basename']}")
                    else:
                        logger.warning(f"⚠️ Fichier {file_path} ne contient pas de section 'clusters'")
                
                except json.JSONDecodeError as e:
                    logger.error(f"❌ Erreur de parsing JSON pour {file_path}: {str(e)}")
                except Exception as e:
                    logger.error(f"❌ Erreur lors du traitement du fichier {file_path}: {str(e)}")
        
        except Exception as e:
            logger.error(f"❌ Erreur lors du traitement du dépôt Git: {str(e)}")
            return {"output": {"parameters": []}}
    
    # Retourner les paramètres générés au format attendu par Argo CD
    logger.info(f"📤 Réponse: {len(result_params)} clusters actifs trouvés")
    return {
        "output": {
            "parameters": result_params
        }
    }
@app.get("/")
async def status_check():
    """Point de terminaison pour vérifier l'état du service."""
    return { "version": "1.0.0"}

@app.get("/health")
async def health_check():
    """Point de terminaison pour vérifier l'état du service."""
    return {"status": "healthy"}

if __name__ == "__main__":
    import uvicorn
    port = int(os.environ.get("PORT", 8080))
    logger.info(f"🚀 Démarrage du plugin generator sur le port {port}")
    uvicorn.run(app, host="0.0.0.0", port=port)