"""
CHRONOS Code Scanner CLI Commands
==================================

Commands for AST-based cryptographic code analysis, dependency scanning,
and security recommendations with SARIF output.
"""

import json
from pathlib import Path
from typing import Optional

import typer
from rich.console import Console
from rich.table import Table
from rich.panel import Panel
from rich.progress import Progress
from rich.syntax import Syntax

from chronos.core.defend.code_scanner import (
    CodeScanner,
    analyze_project,
)

app = typer.Typer(help="AST-based cryptographic code scanning and analysis")
console = Console()


@app.command()
def scan(
    path: str = typer.Argument(
        ".",
        help="File or directory to scan"
    ),
    output_format: str = typer.Option(
        "console",
        "--format",
        help="Output format: console, json, sarif"
    ),
    output_file: Optional[str] = typer.Option(
        None,
        "--output",
        help="Output file path"
    ),
    verbose: bool = typer.Option(
        False,
        "--verbose",
        "-v",
        help="Verbose output"
    ),
):
    """Scan code for cryptographic patterns and vulnerabilities."""
    path_obj = Path(path)
    
    if not path_obj.exists():
        console.print(f"[red]✗ Path not found: {path}[/red]")
        raise typer.Exit(1)
    
    console.print(f"\n[cyan]Scanning: {path}[/cyan]\n")
    
    if path_obj.is_file():
        # Single file scan
        scanner = CodeScanner(verbose=verbose)
        with Progress() as progress:
            progress.add_task("Scanning file...", total=1)
            result = scanner.scan_file(path)
        
        if not result:
            console.print(f"[yellow]⚠ Unsupported file type: {path}[/yellow]")
            return
        
        results = [result]
    else:
        # Directory scan
        scanner = CodeScanner(verbose=verbose)
        with Progress() as progress:
            progress.add_task("Scanning directory...", total=None)
            results = scanner.scan_directory(path)
    
    # Format output
    if output_format == "json":
        _output_json(results, output_file)
    elif output_format == "sarif":
        _output_sarif(results, output_file)
    else:
        _output_console(results)
    
    console.print(f"\n[green]✓ Scan complete[/green]")


def _output_console(results):
    """Output results to console."""
    total_usages = sum(len(r.crypto_usages) for r in results)
    total_files = len(results)
    avg_risk = sum(r.total_risk_score for r in results) / len(results) if results else 0
    
    # Summary
    console.print("[bold cyan]Scan Summary[/bold cyan]\n")
    summary_table = Table(show_header=False)
    summary_table.add_row("Files scanned:", f"[green]{total_files}[/green]")
    summary_table.add_row("Crypto usages found:", f"[yellow]{total_usages}[/yellow]")
    summary_table.add_row("Average risk score:", f"[red]{avg_risk:.1f}/100[/red]")
    console.print(summary_table)
    
    # Results by file
    for result in results:
        console.print(f"\n[bold cyan]File: {result.file_path}[/bold cyan]")
        console.print(f"Language: {result.language} | Risk Score: {result.total_risk_score:.1f}/100")
        
        if not result.crypto_usages:
            console.print("[dim]No cryptographic usages detected[/dim]")
            continue
        
        # Crypto usages table
        table = Table(show_header=True, header_style="bold magenta")
        table.add_column("Line", style="cyan", width=6)
        table.add_column("Algorithm", style="yellow")
        table.add_column("Type", width=20)
        table.add_column("Risk", width=10)
        table.add_column("Recommendation", width=40)
        
        for usage in result.crypto_usages:
            risk_color = {
                'critical': 'red',
                'high': 'red',
                'medium': 'yellow',
                'low': 'green',
                'info': 'blue',
            }.get(usage.risk_level.value, 'white')
            
            rec = usage.recommendation or "-"
            table.add_row(
                str(usage.location.line),
                usage.algorithm.value,
                usage.usage_type,
                f"[{risk_color}]{usage.risk_level.value.upper()}[/{risk_color}]",
                rec[:40] + "..." if len(rec) > 40 else rec
            )
        
        console.print(table)
        
        # Migration plans
        if result.migration_plans:
            console.print(f"\n[bold cyan]Migration Plans ({len(result.migration_plans)})[/bold cyan]")
            plan_table = Table(show_header=True, header_style="bold magenta")
            plan_table.add_column("From", style="cyan")
            plan_table.add_column("To", style="green")
            plan_table.add_column("Priority", width=10)
            plan_table.add_column("Effort", width=12)
            
            for plan in result.migration_plans:
                priority_color = {
                    'critical': 'red',
                    'high': 'orange1',
                    'medium': 'yellow',
                    'low': 'green',
                }.get(plan.priority, 'white')
                
                plan_table.add_row(
                    plan.from_algorithm,
                    plan.to_algorithm,
                    f"[{priority_color}]{plan.priority.upper()}[/{priority_color}]",
                    f"{plan.effort_hours:.0f}h (~{plan.timeline_months:.1f}m)"
                )
            
            console.print(plan_table)


def _output_json(results, output_file: Optional[str]):
    """Output results as JSON."""
    data = {
        'scan_date': results[0].scan_time.isoformat() if results else None,
        'total_files': len(results),
        'files': []
    }
    
    for result in results:
        file_data = {
            'file_path': result.file_path,
            'language': result.language,
            'risk_score': result.total_risk_score,
            'crypto_usages': [
                {
                    'line': u.location.line,
                    'column': u.location.column,
                    'algorithm': u.algorithm.value,
                    'type': u.usage_type,
                    'risk_level': u.risk_level.value,
                    'recommendation': u.recommendation,
                    'code_snippet': u.location.code_snippet[:100] + "..." if len(u.location.code_snippet) > 100 else u.location.code_snippet,
                }
                for u in result.crypto_usages
            ],
            'migration_plans': [
                {
                    'from': p.from_algorithm,
                    'to': p.to_algorithm,
                    'priority': p.priority,
                    'effort_hours': p.effort_hours,
                    'timeline_months': p.timeline_months,
                }
                for p in result.migration_plans
            ]
        }
        data['files'].append(file_data)
    
    if output_file:
        with open(output_file, 'w') as f:
            json.dump(data, f, indent=2)
        console.print(f"[green]✓[/green] JSON report: {output_file}")
    else:
        console.print(json.dumps(data, indent=2))


def _output_sarif(results, output_file: Optional[str]):
    """Output results in SARIF format."""
    scanner = CodeScanner()
    sarif = scanner.generate_sarif_report(results, output_file)
    
    if output_file:
        console.print(f"[green]✓[/green] SARIF report: {output_file}")
    else:
        console.print(json.dumps(sarif, indent=2))


@app.command()
def dependencies(
    path: str = typer.Argument(
        ".",
        help="Path to project with requirements.txt"
    ),
    output_format: str = typer.Option(
        "console",
        "--format",
        help="Output format: console, json"
    ),
):
    """Analyze project dependencies for cryptographic libraries."""
    req_file = Path(path) / "requirements.txt"
    
    if not req_file.exists():
        # Try finding it
        if Path(path).is_file() and path.endswith("requirements.txt"):
            req_file = Path(path)
        else:
            console.print(f"[red]✗ requirements.txt not found in {path}[/red]")
            raise typer.Exit(1)
    
    console.print(f"\n[cyan]Analyzing dependencies: {req_file}[/cyan]\n")
    
    scanner = CodeScanner()
    deps = scanner.analyze_dependencies(str(req_file))
    
    if output_format == "json":
        data = {
            'file': str(req_file),
            'total_dependencies': len(deps),
            'crypto_libraries': [
                {
                    'name': d.name,
                    'version': d.version,
                    'is_crypto': d.is_crypto,
                    'has_pqc': d.has_pqc,
                }
                for d in deps if d.is_crypto
            ],
            'all_dependencies': [
                {
                    'name': d.name,
                    'version': d.version,
                    'source': d.source,
                }
                for d in deps
            ]
        }
        console.print(json.dumps(data, indent=2))
    else:
        # Console output
        table = Table(show_header=True, header_style="bold magenta")
        table.add_column("Package", style="cyan")
        table.add_column("Version", style="yellow")
        table.add_column("Crypto", width=8)
        table.add_column("PQC Support", width=12)
        
        for dep in deps:
            crypto_str = "[green]✓[/green]" if dep.is_crypto else "[dim]-[/dim]"
            pqc_str = "[green]✓[/green]" if dep.has_pqc else "[dim]-[/dim]"
            
            table.add_row(
                dep.name,
                dep.version or "unknown",
                crypto_str,
                pqc_str
            )
        
        console.print(table)
        console.print(f"\n[cyan]Total dependencies:[/cyan] {len(deps)}")
        crypto_count = sum(1 for d in deps if d.is_crypto)
        console.print(f"[cyan]Cryptographic libraries:[/cyan] {crypto_count}")


@app.command()
def suggest(
    path: str = typer.Argument(
        ".",
        help="File or directory to analyze"
    ),
    output_file: Optional[str] = typer.Option(
        None,
        "--output",
        help="Output file for suggestions"
    ),
):
    """Generate code refactoring suggestions with line numbers."""
    path_obj = Path(path)
    
    if not path_obj.exists():
        console.print(f"[red]✗ Path not found: {path}[/red]")
        raise typer.Exit(1)
    
    console.print(f"\n[cyan]Generating suggestions: {path}[/cyan]\n")
    
    scanner = CodeScanner()
    
    if path_obj.is_file():
        result = scanner.scan_file(path)
        results = [result] if result else []
    else:
        results = scanner.scan_directory(path)
    
    suggestions = scanner.generate_refactoring_suggestions(results)
    
    if not suggestions:
        console.print("[yellow]No refactoring suggestions found[/yellow]")
        return
    
    console.print(f"[bold cyan]Refactoring Suggestions ({len(suggestions)})[/bold cyan]\n")
    
    for i, suggestion in enumerate(suggestions, 1):
        console.print(f"[bold cyan]Suggestion {i}[/bold cyan]")
        console.print(f"File: [yellow]{suggestion['file']}[/yellow] (Line {suggestion['line']})")
        console.print(f"From: [red]{suggestion['from_algorithm']}[/red] → To: [green]{suggestion['to_algorithm']}[/green]")
        console.print(f"Priority: [bold]{suggestion['priority'].upper()}[/bold] | Effort: {suggestion['effort_hours']:.0f}h")
        
        # Show code pattern
        pattern = suggestion['refactoring_pattern']
        if pattern.get('old'):
            console.print("\n[dim]Old pattern:[/dim]")
            console.print(Syntax(pattern['old'], "python", theme="monokai", line_numbers=False))
        
        if pattern.get('new'):
            console.print("[dim]New pattern:[/dim]")
            console.print(Syntax(pattern['new'], "python", theme="monokai", line_numbers=False))
        
        # Show steps
        if suggestion['steps']:
            console.print("[dim]Migration steps:[/dim]")
            for step in suggestion['steps']:
                console.print(f"  {step}")
        
        console.print()
    
    # Save to file if requested
    if output_file:
        with open(output_file, 'w') as f:
            json.dump(suggestions, f, indent=2)
        console.print(f"[green]✓[/green] Suggestions saved to {output_file}")


@app.command()
def analyze(
    path: str = typer.Argument(
        ".",
        help="Project path to analyze"
    ),
    full_report: bool = typer.Option(
        False,
        "--full",
        "-f",
        help="Generate full analysis report"
    ),
):
    """Full project analysis with code and dependencies."""
    path_obj = Path(path)
    
    if not path_obj.exists():
        console.print(f"[red]✗ Path not found: {path}[/red]")
        raise typer.Exit(1)
    
    console.print(f"\n[cyan]Analyzing project: {path}[/cyan]\n")
    
    with Progress() as progress:
        progress.add_task("Analyzing project...", total=None)
        analysis = analyze_project(path)
    
    # Summary
    console.print("[bold cyan]Project Analysis Summary[/bold cyan]\n")
    
    summary_table = Table(show_header=False)
    summary_table.add_row("Code files scanned:", f"[green]{len(analysis['code_results'])}[/green]")
    summary_table.add_row("Crypto usages found:", f"[yellow]{analysis['total_usages']}[/yellow]")
    summary_table.add_row("Overall risk score:", f"[red]{analysis['total_risk_score']:.1f}/100[/red]")
    summary_table.add_row("Dependencies analyzed:", f"[cyan]{len(analysis['dependencies'])}[/cyan]")
    summary_table.add_row("Migration plans created:", f"[magenta]{len(analysis['migration_plans'])}[/magenta]")
    console.print(summary_table)
    
    # Dependencies
    if analysis['dependencies']:
        console.print(f"\n[bold cyan]Cryptographic Dependencies[/bold cyan]")
        crypto_deps = [d for d in analysis['dependencies'] if d.is_crypto]
        for dep in crypto_deps:
            pqc_str = "[green]PQC-ready[/green]" if dep.has_pqc else "[yellow]Standard[/yellow]"
            console.print(f"  • {dep.name} ({dep.version or '?'}) - {pqc_str}")
    
    # Migration plans
    if analysis['migration_plans']:
        console.print(f"\n[bold cyan]Migration Plans ({len(analysis['migration_plans'])})[/bold cyan]")
        critical = sum(1 for p in analysis['migration_plans'] if p.priority == 'critical')
        high = sum(1 for p in analysis['migration_plans'] if p.priority == 'high')
        medium = sum(1 for p in analysis['migration_plans'] if p.priority == 'medium')
        
        console.print(f"  [red]Critical: {critical}[/red] | [orange1]High: {high}[/orange1] | [yellow]Medium: {medium}[/yellow]")
    
    # Detailed report
    if full_report:
        console.print(f"\n[bold cyan]Detailed Analysis[/bold cyan]\n")
        
        for result in analysis['code_results']:
            console.print(f"[cyan]{result.file_path}[/cyan]")
            for usage in result.crypto_usages:
                console.print(f"  Line {usage.location.line}: {usage.algorithm.value} ({usage.usage_type})")
                if usage.recommendation:
                    console.print(f"    → {usage.recommendation}")


@app.command()
def sarif(
    path: str = typer.Argument(
        ".",
        help="File or directory to scan"
    ),
    output: str = typer.Option(
        "scan-results.sarif",
        "--output",
        "-o",
        help="Output SARIF file path"
    ),
):
    """Generate SARIF report for IDE integration."""
    path_obj = Path(path)
    
    if not path_obj.exists():
        console.print(f"[red]✗ Path not found: {path}[/red]")
        raise typer.Exit(1)
    
    console.print(f"\n[cyan]Generating SARIF report: {output}[/cyan]\n")
    
    scanner = CodeScanner()
    
    if path_obj.is_file():
        result = scanner.scan_file(path)
        results = [result] if result else []
    else:
        results = scanner.scan_directory(path)
    
    scanner.generate_sarif_report(results, output)
    
    console.print(f"[green]✓ SARIF report generated: {output}[/green]")
    console.print("[dim]Compatible with VSCode SARIF Viewer and other SARIF-aware tools[/dim]")


if __name__ == "__main__":
    app()
