#!/usr/bin/env python3
"""
Aetheris by Adryan - Interface en ligne de commande
"""

import argparse
import asyncio
import json
import os
import sys
from typing import List, Optional

from rich.console import Console
from rich.prompt import Confirm

# Configuration du logging et encodage UTF-8
from src.core.logger import log_print, setup_utf8_encoding

setup_utf8_encoding()

try:
    import google.genai as _genai  # noqa: F401 - Vérifie la disponibilité du SDK
except ImportError as e:
    print(f"❌ Dépendances manquantes: {e}")
    print("   Installez avec: pip install -r requirements.txt")
    sys.exit(1)

# Import de CodeAnalyzer depuis le module dédié
from src.core.analyzer import CodeAnalyzer

# Import de la configuration depuis le module dédié
from src.core.config import AnalysisConfig, load_env_file
from src.core.progress import ProgressManager


def parse_changed_files(files_input: Optional[str]) -> List[str]:
    """Parse les fichiers modifiés depuis une chaîne JSON ou une liste séparée par des virgules"""
    if not files_input:
        return []

    try:
        # Essayer de parser comme JSON (depuis GitHub Actions)
        files = json.loads(files_input)
        if isinstance(files, list):
            return files
    except (json.JSONDecodeError, TypeError):
        pass

    # Sinon, traiter comme une liste séparée par des virgules
    files = [f.strip() for f in files_input.split(",") if f.strip()]
    return files


def setup_analysis_parser(subparsers):
    """Configure le parser pour la sous-commande analysis"""
    analysis_parser = subparsers.add_parser(
        "analysis",
        help="Lancer une analyse de code",
        description="Analyse de code avec plusieurs agents coopérants",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Exemples:
  # Analyse complète de la codebase
  aetheris analysis

  # Analyser uniquement les fichiers modifiés (depuis GitHub Actions)
  aetheris analysis --changed-files-only --files '["src/file1.py", "src/file2.py"]'

  # Analyser une liste de fichiers spécifiques
  aetheris analysis --files src/file1.py,src/file2.py

  # Analyser les fichiers d'une PR
  aetheris analysis --changed-files-only --pr-number 123
        """,
    )

    analysis_parser.add_argument(
        "--changed-files-only",
        action="store_true",
        help="Analyser uniquement les fichiers modifiés (pour PR/commits)",
    )

    analysis_parser.add_argument(
        "--files",
        type=str,
        default=None,
        help="Liste de fichiers à analyser (JSON array ou séparés par virgules). Utilisé avec --changed-files-only",
    )

    analysis_parser.add_argument(
        "--pr-number", type=int, default=None, help="Numéro de PR (optionnel, pour information)"
    )

    return analysis_parser


async def run_analysis(args):
    """Exécute l'analyse de code"""
    log_print("=" * 60)
    log_print("🚀 Aetheris by Adryan - Démarrage de l'analyse de code")
    log_print("=" * 60)
    log_print()

    # Charger le fichier .env s'il existe
    env_vars = load_env_file(".env")

    # Déterminer le provider AI (défaut: gemini)
    ai_provider = (env_vars.get("AI_PROVIDER") or os.getenv("AI_PROVIDER", "gemini")).lower()

    # Récupérer la clé API selon le provider
    api_key_env_vars = {
        "gemini": "GEMINI_API_KEY",
        "openai": "OPENAI_API_KEY",
        "claude": "ANTHROPIC_API_KEY",
    }

    api_key_name = api_key_env_vars.get(ai_provider, "GEMINI_API_KEY")
    ai_api_key = env_vars.get(api_key_name) or os.getenv(api_key_name)

    if not ai_api_key:
        log_print(f"❌ Variable d'environnement {api_key_name} non définie")
        log_print(f"   Provider sélectionné: {ai_provider}")
        log_print("   Options:")
        log_print(f"   1. Créez un fichier .env avec: {api_key_name}=votre_cle")
        log_print(f"   2. Ou définissez-la avec: export {api_key_name}=votre_cle (Linux/Mac)")
        log_print(f"   3. Ou définissez-la avec: $env:{api_key_name}='votre_cle' (PowerShell)")
        log_print("   4. Ou configurez-la dans GitHub Secrets (pour GitHub Actions)")
        sys.exit(1)

    # Afficher la clé API masquée pour debug
    masked_key = ai_api_key[:8] + "..." + ai_api_key[-4:] if len(ai_api_key) > 12 else "***"
    log_print(f"🔑 Clé API: {masked_key} (longueur: {len(ai_api_key)})")

    # Valider la clé API avec un test simple
    if ai_provider == "gemini":
        log_print("🔄 Validation de la clé API Gemini...")
        try:
            from google import genai

            test_client = genai.Client(api_key=ai_api_key)
            # Test avec un modèle simple
            test_response = test_client.models.generate_content(
                model="gemini-2.0-flash", contents="Say 'OK' if you can hear me."
            )
            log_print(f"✅ Clé API validée: {test_response.text[:50]}...")
        except Exception as e:
            log_print(f"❌ Erreur de validation de la clé API: {e}")
            log_print(
                "   Vérifiez que votre clé API est valide sur: https://aistudio.google.com/apikey"
            )
            sys.exit(1)

    # Récupérer le modèle (défaut selon provider)
    from src.core.config import get_default_model

    default_model = get_default_model(ai_provider)
    ai_model = env_vars.get("AI_MODEL") or os.getenv("AI_MODEL", default_model)

    # Appliquer les variables du .env à l'environnement pour les autres configs
    for key, value in env_vars.items():
        if key not in os.environ:
            os.environ[key] = value

    # Parser les fichiers modifiés depuis les arguments ou l'environnement GitHub Actions
    changed_files: List[str] = []
    analyze_changed_only = args.changed_files_only

    if analyze_changed_only:
        # Priorité: argument --files, puis variable d'environnement CHANGED_FILES
        files_input = args.files or os.getenv("CHANGED_FILES")
        changed_files = parse_changed_files(files_input)

        if not changed_files:
            log_print("⚠️  Mode --changed-files-only activé mais aucun fichier spécifié")
            log_print("   Utilisez --files ou définissez CHANGED_FILES dans l'environnement")
            sys.exit(1)

    # Obtenir le numéro de PR depuis les arguments ou l'environnement GitHub
    pr_number = args.pr_number
    if not pr_number:
        pr_number_str = os.getenv("PR_NUMBER")
        if pr_number_str:
            try:
                pr_number = int(pr_number_str)
            except ValueError:
                pass

    config = AnalysisConfig(
        ai_provider=ai_provider,
        ai_api_key=ai_api_key,
        ai_model=ai_model,
        batch_size=int(os.getenv("BATCH_SIZE", "10")),
        max_retries=int(os.getenv("MAX_RETRIES", "3")),
        timeout_seconds=int(os.getenv("TIMEOUT_SECONDS", "60")),
        output_dir=os.getenv("OUTPUT_DIR", "docs/analyses"),
        architecture_report_path=os.getenv("ARCHITECTURE_REPORT", "docs/architecture_overview.md"),
        root_dir=os.getenv("ROOT_DIR", "."),
        enable_web_search=os.getenv("ENABLE_WEB_SEARCH", "true").lower() == "true",
        max_web_results=int(os.getenv("MAX_WEB_RESULTS", "3")),
        enable_security_analysis=os.getenv("ENABLE_SECURITY_ANALYSIS", "true").lower() == "true",
        enable_metrics_analysis=os.getenv("ENABLE_METRICS_ANALYSIS", "true").lower() == "true",
        enable_dependency_vulnerability=os.getenv("ENABLE_DEPENDENCY_VULNERABILITY", "true").lower()
        == "true",
        enable_code_generation=os.getenv("ENABLE_CODE_GENERATION", "true").lower() == "true",
        analyze_changed_files_only=analyze_changed_only,
        changed_files=changed_files,
        pr_number=pr_number,
        show_reasoning=os.getenv("SHOW_REASONING", "false").lower() == "true",
        # Gemini Advanced Features (v2.2.0)
        enable_structured_outputs=os.getenv("ENABLE_STRUCTURED_OUTPUTS", "true").lower() == "true",
        enable_thinking=os.getenv("ENABLE_THINKING", "false").lower() == "true",
        thinking_budget=int(os.getenv("THINKING_BUDGET", "1024")),
        enable_code_execution=os.getenv("ENABLE_CODE_EXECUTION", "false").lower() == "true",
        enable_function_calling=os.getenv("ENABLE_FUNCTION_CALLING", "false").lower() == "true",
        enable_context_caching=os.getenv("ENABLE_CONTEXT_CACHING", "true").lower() == "true",
        cache_ttl_seconds=int(os.getenv("CONTEXT_CACHE_TTL_SECONDS", "3600")),
        enable_batch_mode=os.getenv("ENABLE_BATCH_MODE", "false").lower() == "true",
        enable_google_search=os.getenv("ENABLE_GOOGLE_SEARCH", "false").lower() == "true",
    )

    log_print(f"📁 Répertoire racine: {config.root_dir}")
    log_print(f"📂 Répertoire de sortie: {config.output_dir}")
    log_print(f"🤖 Provider AI: {config.ai_provider.upper()}")
    log_print(f"🤖 Modèle: {config.ai_model}")

    if analyze_changed_only:
        log_print(
            f"🔍 Mode: Analyse des fichiers modifiés uniquement ({len(changed_files)} fichier(s))"
        )
        if pr_number:
            log_print(f"📋 PR #{pr_number}")
    else:
        log_print("🔍 Mode: Analyse complète de la codebase")

    log_print()

    # Créer ProgressManager si on est dans un terminal interactif
    progress_manager = ProgressManager(enabled=True)

    analyzer = CodeAnalyzer(config, progress_manager=progress_manager)
    await analyzer.run()

    log_print()
    log_print("=" * 60)
    log_print("✅ Analyse terminée avec succès!")
    log_print("=" * 60)


def setup_fix_parser(subparsers):
    """Configure le parser pour la sous-commande fix"""
    fix_parser = subparsers.add_parser(
        "fix",
        help="Corriger les bugs détectés avec Claude Code",
        description="Lance Claude Code pour corriger les bugs (séquentiel ou parallèle)",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Exemples:
  # Tester la connexion à Claude Code
  aetheris fix --test

  # Corriger les bugs depuis le cache existant (mode séquentiel)
  aetheris fix

  # Mode automatique (sans confirmations)
  aetheris fix --auto

  # Mode parallèle: 10 corrections simultanées (RECOMMANDÉ pour beaucoup de bugs)
  aetheris fix --auto --batch-size 10

  # Mode parallèle avec timeout personnalisé (5 min par bug)
  aetheris fix --auto -b 10 --timeout 300

  # Ouvrir Claude Code dans une fenêtre terminal (mode séquentiel uniquement)
  aetheris fix --terminal

  # Corriger uniquement les bugs critiques et high
  aetheris fix --severity high

  # Lancer une analyse puis corriger en parallèle
  aetheris fix --analyze-first --batch-size 10
        """,
    )

    fix_parser.add_argument(
        "--analyze-first",
        action="store_true",
        help="Lancer une analyse avant de corriger",
    )

    fix_parser.add_argument(
        "--severity",
        choices=["critical", "high", "medium", "low"],
        default="medium",
        help="Sévérité minimale des bugs à corriger (défaut: medium)",
    )

    fix_parser.add_argument(
        "--auto",
        action="store_true",
        help="Mode automatique sans confirmations entre les bugs",
    )

    fix_parser.add_argument(
        "--terminal",
        action="store_true",
        help="Ouvrir Claude Code dans une nouvelle fenêtre terminal (au lieu du mode arrière-plan)",
    )

    fix_parser.add_argument(
        "--test",
        action="store_true",
        help="Tester la connexion à Claude Code sans corriger de bugs",
    )

    fix_parser.add_argument(
        "--batch-size",
        "-b",
        type=int,
        default=1,
        help="Nombre de corrections en parallèle (défaut: 1, max recommandé: 10)",
    )

    fix_parser.add_argument(
        "--timeout",
        type=int,
        default=300,
        help="Timeout par bug en secondes (défaut: 300)",
    )

    return fix_parser


async def run_fix(args):
    """Exécute la correction de bugs avec Claude Code"""
    console = Console()

    console.print()
    console.rule("[bold blue]🔧 Aetheris Fix - Correction de bugs avec Claude Code")
    console.print()

    # Import des modules nécessaires
    from src.models.data_models import BugIdentifier
    from src.services.claude_code_integration import ClaudeCodeIntegration

    # Charger le fichier .env s'il existe
    env_vars = load_env_file(".env")
    root_dir = env_vars.get("ROOT_DIR") or os.getenv("ROOT_DIR", ".")
    output_dir = env_vars.get("OUTPUT_DIR") or os.getenv("OUTPUT_DIR", "docs/analyses")

    # Initialiser l'intégration Claude Code
    claude = ClaudeCodeIntegration(root_dir)

    # Mode test: vérifier la connexion à Claude Code
    if getattr(args, "test", False):
        console.print("[cyan]🔍 Test de Claude Code...[/cyan]")
        console.print()

        if not claude.is_available:
            console.print("[red]❌ Claude Code n'est pas installé ou introuvable[/red]")
            console.print()
            console.print("[yellow]Pour installer Claude Code:[/yellow]")
            console.print("  npm install -g @anthropic-ai/claude-code")
            sys.exit(1)

        console.print(f"[green]✅ Claude Code trouvé: {claude.claude_path}[/green]")

        test_ok, test_msg = claude.test_claude_code()
        if test_ok:
            console.print(f"[green]✅ {test_msg}[/green]")
            console.print()

            # Test avec un prompt simple
            console.print("[cyan]🧪 Test d'exécution avec un prompt simple...[/cyan]")
            exec_ok, exec_msg = claude.test_claude_code_execution(timeout=60)

            if exec_ok:
                console.print("[green]✅ Test réussi![/green]")
                console.print(f"[dim]{exec_msg}[/dim]")
            else:
                console.print(f"[red]❌ Échec du test: {exec_msg}[/red]")
                sys.exit(1)
        else:
            console.print(f"[red]❌ {test_msg}[/red]")
            sys.exit(1)

        sys.exit(0)

    # Vérifier si Claude Code est disponible
    if not claude.is_available:
        console.print("[red]❌ Claude Code n'est pas installé ou introuvable[/red]")
        console.print()
        console.print("[yellow]Pour installer Claude Code:[/yellow]")
        console.print("  npm install -g @anthropic-ai/claude-code")
        console.print()

        if not Confirm.ask("Voulez-vous continuer avec l'analyse seule?"):
            console.print("[yellow]Opération annulée.[/yellow]")
            sys.exit(1)

        # Continuer en mode analyse seule
        console.print("[yellow]Mode analyse seule activé (sans correction).[/yellow]")
        args.analyze_first = True

    # Chemin du cache des bugs
    bugs_cache_path = os.path.join(output_dir, ".bugs_cache.json")

    # Si --analyze-first ou pas de cache, lancer l'analyse
    if args.analyze_first or not os.path.exists(bugs_cache_path):
        console.print("[cyan]🔍 Lancement de l'analyse...[/cyan]")
        console.print()

        # Créer un args mock pour l'analyse
        class AnalysisArgs:
            changed_files_only = False
            files = None
            pr_number = None

        await run_analysis(AnalysisArgs())

        # Vérifier si le cache a été créé
        if not os.path.exists(bugs_cache_path):
            console.print("[yellow]⚠️  Aucun bug détecté ou cache non créé.[/yellow]")
            sys.exit(0)

    # Charger les bugs depuis le cache
    try:
        with open(bugs_cache_path, "r", encoding="utf-8") as f:
            bugs_data = json.load(f)
    except (json.JSONDecodeError, IOError) as e:
        console.print(f"[red]❌ Erreur lors de la lecture du cache: {e}[/red]")
        console.print(
            "[yellow]Lancez 'aetheris fix --analyze-first' pour régénérer le cache.[/yellow]"
        )
        sys.exit(1)

    # Convertir les données en objets BugIdentifier
    all_bugs: List[BugIdentifier] = []
    fixed_count = 0
    for bug_data in bugs_data:
        bug = BugIdentifier(
            id=bug_data["id"],
            file_path=bug_data["file_path"],
            line_number=bug_data.get("line_number"),
            bug_type=bug_data["bug_type"],
            severity=bug_data["severity"],
            description=bug_data["description"],
            recommendation=bug_data.get("recommendation", ""),
            impacted_files=bug_data.get("impacted_files", []),
            fixed=bug_data.get("fixed", False),
            fixed_at=bug_data.get("fixed_at"),
            fix_summary=bug_data.get("fix_summary"),
        )
        all_bugs.append(bug)
        if bug.fixed:
            fixed_count += 1

    # Filtrer les bugs déjà corrigés
    bugs = [b for b in all_bugs if not b.fixed]

    if fixed_count > 0:
        console.print(f"[dim]ℹ️  {fixed_count} bug(s) déjà corrigé(s) (ignorés)[/dim]")

    if not bugs:
        console.print("[green]✅ Aucun bug à corriger![/green]")
        sys.exit(0)

    # Filtrer par sévérité
    bugs = claude.filter_bugs_by_severity(bugs, args.severity)

    if not bugs:
        console.print(f"[yellow]⚠️  Aucun bug avec sévérité >= {args.severity}[/yellow]")
        sys.exit(0)

    console.print(f"[cyan]🐛 {len(bugs)} bug(s) trouvé(s) avec sévérité >= {args.severity}[/cyan]")
    console.print()

    # Vérifier si Claude Code est disponible pour la correction
    if not claude.is_available:
        console.print("[yellow]⚠️  Mode analyse seule - Affichage des bugs sans correction[/yellow]")
        claude.display_bugs_table(bugs)
        sys.exit(0)

    # Callback pour sauvegarder le cache après chaque correction
    def save_bugs_cache(fixed_bug: BugIdentifier):
        """Sauvegarde le cache des bugs avec le statut mis à jour"""
        # Mettre à jour le bug dans all_bugs
        for i, b in enumerate(all_bugs):
            if b.id == fixed_bug.id:
                all_bugs[i] = fixed_bug
                break

        # Sauvegarder tout le cache
        cache_data = []
        for b in all_bugs:
            bug_dict = {
                "id": b.id,
                "file_path": b.file_path,
                "line_number": b.line_number,
                "bug_type": b.bug_type,
                "severity": b.severity,
                "description": b.description,
                "recommendation": b.recommendation,
                "impacted_files": b.impacted_files,
                "fixed": b.fixed,
                "fixed_at": b.fixed_at,
                "fix_summary": b.fix_summary,
            }
            cache_data.append(bug_dict)

        with open(bugs_cache_path, "w", encoding="utf-8") as f:
            json.dump(cache_data, f, indent=2, ensure_ascii=False)

    # Lancer la session de correction
    batch_size = getattr(args, "batch_size", 1)
    timeout = getattr(args, "timeout", 300)

    if batch_size > 1:
        # Mode parallèle avec batching
        console.print(f"[cyan]⚡ Mode parallèle activé: {batch_size} corrections simultanées[/cyan]")
        console.print()
        stats = await claude.run_fix_session_parallel(
            bugs,
            batch_size=batch_size,
            timeout=timeout,
            on_bug_fixed=save_bugs_cache,
        )
    else:
        # Mode séquentiel classique
        # Par défaut mode direct (arrière-plan), --terminal pour ouvrir une fenêtre
        direct_mode = not getattr(args, "terminal", False)
        stats = claude.run_fix_session(
            bugs,
            auto_mode=args.auto,
            direct_mode=direct_mode,
            on_bug_fixed=save_bugs_cache,
        )

    console.print()
    console.rule("[bold green]🎉 Session de correction terminée")

    if stats["fixed"] == stats["total"]:
        console.print("[green]✅ Tous les bugs ont été corrigés![/green]")
    elif stats["fixed"] > 0:
        console.print(f"[yellow]⚠️  {stats['fixed']}/{stats['total']} bug(s) corrigé(s)[/yellow]")
    else:
        console.print("[red]❌ Aucun bug n'a été corrigé[/red]")


def setup_user_story_parser(subparsers):
    """Configure le parser pour la sous-commande user-story"""
    us_parser = subparsers.add_parser(
        "user-story",
        help="Analyser les flux de user stories",
        description="Analyse les flux frontend ↔ backend et détecte les incohérences",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Exemples:
  # Analyser depuis une description textuelle
  aetheris user-story --text "Quand l'utilisateur clique sur Login..."

  # Analyser depuis un fichier markdown
  aetheris user-story --file user-stories.md

  # Analyser depuis un point d'entrée code
  aetheris user-story --entry src/components/LoginButton.tsx

  # Auto-détecter tous les flux
  aetheris user-story --auto

  # Options de sortie
  aetheris user-story --auto --output-format json
  aetheris user-story --auto --diagram mermaid
        """,
    )

    # Mode d'entrée (mutuellement exclusif)
    input_group = us_parser.add_mutually_exclusive_group(required=True)
    input_group.add_argument(
        "--text",
        type=str,
        help="User story en description textuelle",
    )
    input_group.add_argument(
        "--file",
        type=str,
        help="Chemin vers un fichier markdown avec les user stories",
    )
    input_group.add_argument(
        "--entry",
        type=str,
        help="Fichier de code comme point d'entrée (ex: LoginButton.tsx)",
    )
    input_group.add_argument(
        "--auto",
        action="store_true",
        help="Auto-détecter tous les flux utilisateur",
    )

    # Options de sortie
    us_parser.add_argument(
        "--output-format",
        choices=["markdown", "json", "both"],
        default="both",
        help="Format de sortie (défaut: both)",
    )
    us_parser.add_argument(
        "--diagram",
        choices=["mermaid", "plantuml", "both", "none"],
        default="mermaid",
        help="Format de diagramme (défaut: mermaid)",
    )
    us_parser.add_argument(
        "--output-file",
        type=str,
        help="Fichier de sortie (défaut: docs/user-stories/analysis.md)",
    )
    us_parser.add_argument(
        "--severity",
        choices=["critical", "high", "medium", "low", "info"],
        default="low",
        help="Sévérité minimale pour les issues (défaut: low = tout afficher)",
    )

    return us_parser


async def run_user_story(args):
    """Exécute l'analyse de user stories"""
    console = Console()

    console.print()
    console.rule("[bold blue]📖 Aetheris User Story Analysis")
    console.print()

    # Charger le fichier .env
    env_vars = load_env_file(".env")

    # Déterminer le provider AI
    ai_provider = (env_vars.get("AI_PROVIDER") or os.getenv("AI_PROVIDER", "gemini")).lower()

    # Récupérer la clé API
    api_key_env_vars = {
        "gemini": "GEMINI_API_KEY",
        "openai": "OPENAI_API_KEY",
        "claude": "ANTHROPIC_API_KEY",
    }
    api_key_name = api_key_env_vars.get(ai_provider, "GEMINI_API_KEY")
    ai_api_key = env_vars.get(api_key_name) or os.getenv(api_key_name)

    if not ai_api_key:
        console.print(f"[red]❌ Variable d'environnement {api_key_name} non définie[/red]")
        sys.exit(1)

    # Configuration
    from src.core.config import get_default_model

    default_model = get_default_model(ai_provider)
    ai_model = env_vars.get("AI_MODEL") or os.getenv("AI_MODEL", default_model)

    root_dir = env_vars.get("ROOT_DIR") or os.getenv("ROOT_DIR", ".")
    output_dir = env_vars.get("USER_STORY_OUTPUT_DIR") or os.getenv(
        "USER_STORY_OUTPUT_DIR", "docs/user-stories"
    )

    config = AnalysisConfig(
        ai_provider=ai_provider,
        ai_api_key=ai_api_key,
        ai_model=ai_model,
        root_dir=root_dir,
        user_story_output_dir=output_dir,
        user_story_min_severity=args.severity,
        user_story_diagram_format=args.diagram if args.diagram != "none" else "mermaid",
        enable_structured_outputs=True,
    )

    console.print(f"[cyan]🤖 Provider: {ai_provider.upper()} ({ai_model})[/cyan]")
    console.print(f"[cyan]📁 Répertoire: {root_dir}[/cyan]")
    console.print()

    # Déterminer le mode d'entrée
    if args.text:
        input_mode = "text"
        input_value = args.text
        console.print(f"[cyan]📝 Mode: Description textuelle[/cyan]")
        console.print(
            f"[dim]{args.text[:100]}...[/dim]"
            if len(args.text) > 100
            else f"[dim]{args.text}[/dim]"
        )
    elif args.file:
        input_mode = "file"
        input_value = args.file
        console.print(f"[cyan]📄 Mode: Fichier markdown ({args.file})[/cyan]")
    elif args.entry:
        input_mode = "entry"
        input_value = args.entry
        console.print(f"[cyan]🎯 Mode: Point d'entrée code ({args.entry})[/cyan]")
    else:
        input_mode = "auto"
        input_value = ""
        console.print("[cyan]🔍 Mode: Auto-détection des flux[/cyan]")

    console.print()

    # Lister les fichiers du projet
    from pathspec import PathSpec

    all_files: List[str] = []
    gitignore_path = os.path.join(root_dir, ".gitignore")
    gitignore_spec = None

    if os.path.exists(gitignore_path):
        try:
            with open(gitignore_path, "r", encoding="utf-8") as f:
                gitignore_spec = PathSpec.from_lines("gitwildmatch", f)
        except Exception:
            pass

    for dirpath, dirnames, filenames in os.walk(root_dir):
        # Filtrer les dossiers
        dirnames[:] = [
            d
            for d in dirnames
            if not d.startswith(".")
            and d not in ["node_modules", "__pycache__", "dist", "build", ".git"]
        ]

        for filename in filenames:
            filepath = os.path.relpath(os.path.join(dirpath, filename), root_dir)

            # Appliquer gitignore
            if gitignore_spec and gitignore_spec.match_file(filepath):
                continue

            all_files.append(filepath)

    console.print(f"[dim]📊 {len(all_files)} fichiers dans le projet[/dim]")
    console.print()

    # Créer le ProgressManager
    progress_manager = ProgressManager(enabled=True)

    # Importer et créer l'agent
    from src.agents.user_story_analysis_agent import UserStoryAnalysisAgent
    from src.services.diagram_generator import DiagramGenerator

    agent = UserStoryAnalysisAgent(
        config=config,
        root_dir=root_dir,
        all_files=all_files,
        progress_manager=progress_manager,
    )

    # Lancer l'analyse
    console.print("[cyan]🔄 Analyse en cours...[/cyan]")
    console.print()

    result = await agent.analyze_all_flows(input_mode, input_value)

    # Afficher les résultats
    console.print()
    console.rule("[bold green]📊 Résultats de l'analyse")
    console.print()

    console.print(f"[bold]📈 Statistiques:[/bold]")
    console.print(f"  • Flux analysés: {result.total_flows}")
    console.print(f"  • Étapes totales: {result.total_steps}")
    console.print(f"  • Appels API: {result.total_api_calls}")
    console.print(f"  • Endpoints: {result.total_endpoints}")
    console.print()

    console.print(f"[bold]🐛 Issues par sévérité:[/bold]")
    if result.critical_count > 0:
        console.print(f"  • [red]Critical: {result.critical_count}[/red]")
    if result.high_count > 0:
        console.print(f"  • [yellow]High: {result.high_count}[/yellow]")
    if result.medium_count > 0:
        console.print(f"  • [blue]Medium: {result.medium_count}[/blue]")
    if result.low_count > 0:
        console.print(f"  • [dim]Low: {result.low_count}[/dim]")

    if result.orphan_endpoints:
        console.print()
        console.print(
            f"[yellow]⚠️  {len(result.orphan_endpoints)} endpoint(s) orphelin(s) détecté(s)[/yellow]"
        )
        for ep in result.orphan_endpoints[:5]:
            console.print(f"  • {ep.method} {ep.path} ({ep.file_path})")
        if len(result.orphan_endpoints) > 5:
            console.print(f"  ... et {len(result.orphan_endpoints) - 5} autres")

    if result.missing_handlers:
        console.print()
        console.print(f"[red]❌ {len(result.missing_handlers)} appel(s) API sans handler[/red]")
        for call in result.missing_handlers[:5]:
            console.print(f"  • {call.method} {call.url_pattern} ({call.file_path})")

    # Générer les diagrammes
    if args.diagram != "none":
        console.print()
        console.print("[cyan]📊 Génération des diagrammes...[/cyan]")

        diagram_gen = DiagramGenerator(output_dir)
        files_created = diagram_gen.save_diagrams(result, args.diagram)

        console.print(f"[green]✅ {len(files_created)} fichier(s) créé(s):[/green]")
        for f in files_created[:5]:
            console.print(f"  • {f}")

    # Sauvegarder le rapport JSON si demandé
    if args.output_format in ["json", "both"]:
        os.makedirs(output_dir, exist_ok=True)
        json_path = os.path.join(output_dir, "analysis-result.json")

        report = {
            "analysis_id": result.analysis_id,
            "timestamp": result.timestamp,
            "summary": {
                "total_flows": result.total_flows,
                "total_steps": result.total_steps,
                "total_api_calls": result.total_api_calls,
                "total_endpoints": result.total_endpoints,
                "issues": {
                    "critical": result.critical_count,
                    "high": result.high_count,
                    "medium": result.medium_count,
                    "low": result.low_count,
                },
            },
            "flows": [
                {
                    "id": flow.id,
                    "title": flow.title,
                    "entry_point": flow.entry_point,
                    "steps_count": len(flow.steps),
                    "issues_count": len(flow.issues),
                }
                for flow in result.flows
            ],
            "orphan_endpoints": [
                {"method": ep.method, "path": ep.path, "file": ep.file_path}
                for ep in result.orphan_endpoints
            ],
            "issues": [
                {
                    "id": issue.id,
                    "severity": issue.severity,
                    "type": issue.issue_type,
                    "title": issue.title,
                    "description": issue.description,
                }
                for issue in result.all_issues
            ],
        }

        with open(json_path, "w", encoding="utf-8") as f:
            json.dump(report, f, indent=2, ensure_ascii=False)

        console.print(f"[green]📄 Rapport JSON: {json_path}[/green]")

    console.print()
    console.rule("[bold green]✅ Analyse terminée")


def setup_pr_parser(subparsers):
    """Configure le parser pour la sous-commande pr (review-pr)"""
    pr_parser = subparsers.add_parser(
        "pr",
        aliases=["review-pr"],
        help="Analyser une Pull Request GitHub",
        description="Analyse une PR avec plusieurs providers AI et poste des commentaires",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Exemples:
  # Analyser une PR par URL
  aetheris pr --url https://github.com/owner/repo/pull/123

  # Analyser une PR par repo et numéro
  aetheris pr --repo owner/repo --pr 123

  # Multi-provider avec consensus
  aetheris pr --url https://... --providers gemini,claude

  # Désactiver le mode consensus (voir tous les issues)
  aetheris pr --url https://... --providers gemini,claude --no-consensus

  # Mode dry-run (analyse sans poster de commentaires)
  aetheris pr --url https://... --dry-run

  # Sauvegarder le rapport dans un fichier
  aetheris pr --url https://... --output review.md
        """,
    )

    # Source de la PR (mutuellement exclusif)
    source_group = pr_parser.add_mutually_exclusive_group(required=True)
    source_group.add_argument(
        "--url",
        type=str,
        help="URL complète de la PR (ex: https://github.com/owner/repo/pull/123)",
    )
    source_group.add_argument(
        "--repo",
        type=str,
        help="Repository au format owner/repo (requiert --pr)",
    )

    pr_parser.add_argument(
        "--pr",
        type=int,
        help="Numéro de PR (requis avec --repo)",
    )

    # AI providers
    pr_parser.add_argument(
        "--providers",
        type=str,
        default="gemini",
        help="Providers AI séparés par virgules (ex: gemini,claude,openai). Défaut: gemini",
    )

    # Consensus mode
    pr_parser.add_argument(
        "--consensus",
        action="store_true",
        default=True,
        help="Mode consensus: reporter uniquement les issues trouvés par TOUS les providers (défaut)",
    )
    pr_parser.add_argument(
        "--no-consensus",
        action="store_true",
        help="Désactiver le mode consensus (reporter tous les issues)",
    )

    # Output options
    pr_parser.add_argument(
        "--dry-run",
        action="store_true",
        help="Analyser sans poster de commentaires sur la PR",
    )
    pr_parser.add_argument(
        "--severity",
        choices=["critical", "high", "medium", "low", "info"],
        default="low",
        help="Sévérité minimale des issues à reporter (défaut: low = tout)",
    )
    pr_parser.add_argument(
        "--no-inline",
        action="store_true",
        help="Ne pas poster de commentaires inline, seulement le résumé",
    )
    pr_parser.add_argument(
        "--output",
        type=str,
        help="Sauvegarder le rapport dans un fichier (markdown)",
    )
    pr_parser.add_argument(
        "--auto-fix",
        action="store_true",
        help="Si c'est votre PR, proposer de corriger les issues avec Claude Code",
    )

    return pr_parser


async def run_pr(args):
    """Exécute l'analyse de Pull Request"""
    console = Console()

    console.print()
    console.rule("[bold blue]Aetheris PR Review")
    console.print()

    # Valider les arguments
    if args.repo and not args.pr:
        console.print("[red]Erreur: --pr est requis avec --repo[/red]")
        sys.exit(1)

    # Charger l'environnement
    env_vars = load_env_file(".env")

    # Parser les providers
    providers = [p.strip().lower() for p in args.providers.split(",")]

    # Mapping des clés API
    api_key_env_vars = {
        "gemini": "GEMINI_API_KEY",
        "openai": "OPENAI_API_KEY",
        "claude": "ANTHROPIC_API_KEY",
    }

    # Valider les clés API pour chaque provider
    api_keys = {}
    for provider in providers:
        key_name = api_key_env_vars.get(provider)
        if key_name:
            key = env_vars.get(key_name) or os.getenv(key_name)
            if not key:
                console.print(f"[red]Erreur: {key_name} non trouvée pour le provider {provider}[/red]")
                console.print(f"[dim]Définissez la variable dans .env ou l'environnement[/dim]")
                sys.exit(1)
            api_keys[provider] = key

    if not api_keys:
        console.print("[red]Erreur: Aucun provider valide configuré[/red]")
        sys.exit(1)

    # Initialiser le service GitHub
    from src.services.github_service import GitHubService, GitHubServiceError

    gh_service = GitHubService()

    if not gh_service.is_available:
        console.print("[red]Erreur: gh CLI n'est pas disponible ou non authentifié[/red]")
        console.print("[dim]Installez gh: https://cli.github.com/[/dim]")
        console.print("[dim]Authentifiez-vous: gh auth login[/dim]")
        sys.exit(1)

    # Parser l'URL ou utiliser repo/pr
    try:
        if args.url:
            repo, pr_number = gh_service.parse_pr_url(args.url)
        else:
            repo = args.repo
            pr_number = args.pr
    except GitHubServiceError as e:
        console.print(f"[red]Erreur: {e}[/red]")
        sys.exit(1)

    console.print(f"[cyan]Repository:[/cyan] {repo}")
    console.print(f"[cyan]PR #:[/cyan] {pr_number}")
    console.print(f"[cyan]Providers:[/cyan] {', '.join(providers)}")
    consensus_mode = not args.no_consensus
    console.print(f"[cyan]Mode consensus:[/cyan] {'Oui' if consensus_mode else 'Non'}")
    console.print()

    # Récupérer les infos de la PR
    console.print("[cyan]Récupération des informations de la PR...[/cyan]")
    try:
        pr_info = await gh_service.fetch_pr_info(repo, pr_number)
    except GitHubServiceError as e:
        console.print(f"[red]Erreur lors de la récupération de la PR: {e}[/red]")
        sys.exit(1)

    # Vérifier la propriété
    is_own_pr = gh_service.is_own_pr(pr_info)
    if is_own_pr:
        console.print(f"[green]C'est VOTRE PR[/green]")
    else:
        console.print(f"[yellow]PR par:[/yellow] {pr_info.author_login}")

    console.print(f"[dim]Titre: {pr_info.title}[/dim]")
    console.print(f"[dim]Branche: {pr_info.head_branch} -> {pr_info.base_branch}[/dim]")
    console.print()

    # Récupérer le diff et les fichiers
    console.print("[cyan]Récupération du diff...[/cyan]")
    try:
        diff_content = await gh_service.fetch_pr_diff(repo, pr_number)
        files_changed = await gh_service.fetch_pr_files(repo, pr_number)
    except GitHubServiceError as e:
        console.print(f"[red]Erreur lors de la récupération du diff: {e}[/red]")
        sys.exit(1)

    console.print(f"[dim]{len(files_changed)} fichiers modifiés, +{pr_info.additions}/-{pr_info.deletions}[/dim]")
    console.print()

    # Créer la config et lancer l'analyse
    config = AnalysisConfig(
        ai_api_key=list(api_keys.values())[0],
        ai_provider=providers[0],
        root_dir=os.getcwd(),
        timeout_seconds=120,
        max_retries=3,
    )

    progress_manager = ProgressManager(enabled=True)

    from src.agents.pr_review_agent import PRReviewAgent

    agent = PRReviewAgent(
        config=config,
        providers=providers,
        api_keys=api_keys,
        consensus_mode=consensus_mode,
        progress_manager=progress_manager,
    )

    # Lancer l'analyse
    console.print("[cyan]Analyse en cours...[/cyan]")
    console.print()

    result = await agent.analyze_pr_diff(pr_info, diff_content, files_changed)
    result.is_own_pr = is_own_pr

    # Afficher les résultats
    console.print()
    console.rule("[bold]Résultats de l'analyse")
    console.print()

    # Filtrer par sévérité
    severity_order = ["critical", "high", "medium", "low", "info"]
    min_severity_idx = severity_order.index(args.severity)
    filtered_issues = [
        issue for issue in result.issues
        if severity_order.index(issue.severity.value) <= min_severity_idx
    ]

    console.print(f"[bold]Score de risque:[/bold] {result.risk_score}/10")
    console.print(f"[bold]Recommandation:[/bold] {result.approval_recommendation}")
    console.print(f"[bold]Issues trouvées:[/bold] {len(result.issues)} ({len(filtered_issues)} après filtre)")

    if result.consensus_mode and len(providers) > 1:
        console.print(f"[dim]Mode consensus: {result.total_issues_before_consensus} issues détectées, {len(result.issues)} en consensus[/dim]")

    console.print()

    # Afficher les issues
    if filtered_issues:
        from rich.table import Table

        table = Table(title="Issues détectées")
        table.add_column("Sév.", style="bold")
        table.add_column("Type")
        table.add_column("Fichier")
        table.add_column("Ligne")
        table.add_column("Titre")

        emoji_map = {
            "critical": "[red]CRIT[/red]",
            "high": "[orange1]HIGH[/orange1]",
            "medium": "[yellow]MED[/yellow]",
            "low": "[blue]LOW[/blue]",
            "info": "[dim]INFO[/dim]",
        }

        for issue in filtered_issues:
            table.add_row(
                emoji_map.get(issue.severity.value, issue.severity.value),
                issue.issue_type.value,
                issue.file_path[:30] + "..." if len(issue.file_path) > 30 else issue.file_path,
                str(issue.line_number) if issue.line_number else "-",
                issue.title[:40] + "..." if len(issue.title) > 40 else issue.title,
            )

        console.print(table)
        console.print()

    # Sauvegarder le rapport si demandé
    if args.output:
        summary_comment = agent.generate_summary_comment(result)
        with open(args.output, "w", encoding="utf-8") as f:
            f.write(summary_comment)
        console.print(f"[green]Rapport sauvegardé: {args.output}[/green]")
        console.print()

    # Gérer le posting des commentaires ou la correction
    if not args.dry_run and filtered_issues:
        if is_own_pr and args.auto_fix:
            # Proposer de corriger
            if Confirm.ask("Voulez-vous corriger ces issues avec Claude Code ?"):
                console.print("[yellow]Fonctionnalité de correction à implémenter...[/yellow]")
                # TODO: Intégration avec ClaudeCodeIntegration
        else:
            # Proposer de poster les commentaires
            if Confirm.ask(f"Poster {len(filtered_issues)} commentaires sur la PR ?"):
                console.print("[cyan]Publication des commentaires...[/cyan]")

                # Poster le résumé
                summary_comment = agent.generate_summary_comment(result)
                success = await gh_service.post_review_comment(
                    repo=repo,
                    pr_number=pr_number,
                    body=summary_comment,
                    event="COMMENT",
                )

                if success:
                    console.print("[green]Commentaire de résumé posté avec succès[/green]")
                else:
                    console.print("[red]Erreur lors de la publication du résumé[/red]")

                # Poster les commentaires inline si activé
                if not args.no_inline and pr_info.head_sha:
                    inline_count = 0
                    for comment in result.inline_comments:
                        # Filtrer par sévérité aussi
                        issue = next(
                            (i for i in filtered_issues
                             if i.file_path == comment.file_path and i.line_number == comment.line_number),
                            None
                        )
                        if issue:
                            success = await gh_service.post_inline_comment(
                                repo=repo,
                                pr_number=pr_number,
                                commit_id=pr_info.head_sha,
                                file_path=comment.file_path,
                                line_number=comment.line_number,
                                body=comment.body,
                            )
                            if success:
                                inline_count += 1

                    console.print(f"[green]{inline_count} commentaires inline postés[/green]")

    elif args.dry_run:
        console.print("[dim]Mode dry-run: aucun commentaire posté[/dim]")

    console.print()
    console.rule("[bold green]Analyse terminée")


def get_version():
    """Retourne la version depuis pyproject.toml"""
    try:
        import importlib.metadata

        return importlib.metadata.version("adryserage-aetheris")
    except Exception:
        return "2.3.4"  # Fallback


def create_parser():
    """Crée le parser principal avec sous-commandes"""
    parser = argparse.ArgumentParser(
        prog="aetheris",
        description="Aetheris by Adryan - Système d'analyse de code multi-agents",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Pour plus d'informations sur une commande spécifique:
  aetheris <commande> --help

Exemples:
  aetheris analysis                    # Analyse complète
  aetheris fix                         # Corriger les bugs avec Claude Code
  aetheris fix --test                  # Tester la connexion à Claude Code
  aetheris fix --analyze-first         # Analyser puis corriger
  aetheris fix --severity high         # Corriger uniquement bugs critiques/high
  aetheris pr --url https://...        # Analyser une PR GitHub
  aetheris pr --repo owner/repo --pr 1 # Analyser par repo et numéro
  aetheris pr --providers gemini,claude # Multi-provider avec consensus
        """,
    )

    parser.add_argument("-v", "--version", action="version", version=f"%(prog)s {get_version()}")

    subparsers = parser.add_subparsers(
        dest="command", help="Commandes disponibles", metavar="COMMANDE"
    )

    # Ajouter la sous-commande analysis
    setup_analysis_parser(subparsers)

    # Ajouter la sous-commande fix
    setup_fix_parser(subparsers)

    # Ajouter la sous-commande user-story
    setup_user_story_parser(subparsers)

    # Ajouter la sous-commande pr (review-pr)
    setup_pr_parser(subparsers)

    return parser


def main():
    """Point d'entrée principal de la CLI"""
    parser = create_parser()
    args = parser.parse_args()

    if not args.command:
        parser.print_help()
        sys.exit(1)

    if args.command == "analysis":
        asyncio.run(run_analysis(args))
    elif args.command == "fix":
        asyncio.run(run_fix(args))
    elif args.command == "user-story":
        asyncio.run(run_user_story(args))
    elif args.command in ["pr", "review-pr"]:
        asyncio.run(run_pr(args))
    else:
        parser.print_help()
        sys.exit(1)


if __name__ == "__main__":
    main()
