"""
CVE Extractor CLI application.

Command-line interface for CVE data mining, downloading, and analysis.
"""

import csv
import logging
import shutil
import tempfile
from pathlib import Path

import typer
from rich.console import Console
from rich.table import Table

from ..core import (
    analyze_distribution,
    download_and_extract_cve_data,
    extract_cves_to_csv,
    extract_github_patch,
    load_cves_from_directory,
    verify_patch_code_type,
)
from ..core.downloader import DEFAULT_COMMIT_HASH

# Initialize Typer app
app = typer.Typer(
    name="cve-extractor",
    help="Extract, filter, and analyze CVE data from the official CVE List",
    no_args_is_help=True,
)

# Rich console for enhanced output
console = Console()

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)
logger = logging.getLogger(__name__)


@app.command()
def mine(
    commit_hash: str = typer.Option(
        DEFAULT_COMMIT_HASH,
        "--commit",
        "-c",
        help="CVEProject/cvelistV5 GitHub commit hash (default: 2026-02-10_baseline)",
    ),
    output: Path = typer.Option(
        Path("cve_results.csv"),
        "--output",
        "-o",
        help="Output CSV file path",
    ),
    cache_dir: Path = typer.Option(
        Path(".cache"),
        "--cache-dir",
        help="Cache directory for downloaded archives",
    ),
    cwe_types: str = typer.Option(
        "",
        "--cwe-types",
        help="Comma-separated CWE types to filter (e.g., 'CWE-79,CWE-89')",
    ),
    keywords: str = typer.Option(
        "",
        "--keywords",
        help="Comma-separated keywords to filter in description/products",
    ),
    language: str = typer.Option(
        "",
        "--language",
        help="Programming language for code type verification (e.g., 'php', 'python')",
    ),
    github_token: str = typer.Option(
        "",
        "--github-token",
        envvar="GITHUB_TOKEN",
        help="GitHub personal access token to avoid rate limits (or use GITHUB_TOKEN env var)",
    ),
) -> None:
    """
    Mine CVE data with filters and extract to CSV.

    Downloads CVE data from CVEProject/cvelistV5 at specified commit hash,
    applies filters (CWE type, keywords, GitHub patches, code type),
    and exports matching CVEs to CSV.
    """
    console.print("[bold blue]Starting CVE mining...[/bold blue]")

    try:
        # Set GitHub token in environment if provided
        if github_token:
            import os
            os.environ["GITHUB_TOKEN"] = github_token
            console.print("[green]Using provided GitHub token[/green]")
        # Step 1: Download and extract CVE data
        with tempfile.TemporaryDirectory() as temp_dir:
            temp_path = Path(temp_dir)
            cve_dir = temp_path / "cves"

            console.print(f"Downloading CVEProject/cvelistV5 commit: {commit_hash}")
            download_and_extract_cve_data(commit_hash, cache_dir, cve_dir)

            # Step 2: Load CVE records
            console.print("Loading CVE records...")
            cves = load_cves_from_directory(cve_dir)
            console.print(f"Loaded {len(cves)} CVE records")

            if not cves:
                console.print("[yellow]No CVEs found. Exiting.[/yellow]")
                return

            # Step 3: Filter by CWE type
            if cwe_types:
                cwe_set = {cwe.strip() for cwe in cwe_types.split(",") if cwe.strip()}
                cves = [cve for cve in cves if cve.matches_cwe_type(cwe_set)]
                console.print(f"After CWE filter: {len(cves)} CVEs")

            # Step 4: Filter by keywords
            if keywords:
                keyword_set = {kw.strip() for kw in keywords.split(",") if kw.strip()}
                cves = [cve for cve in cves if cve.matches_keywords(keyword_set)]
                console.print(f"After keyword filter: {len(cves)} CVEs")

            # Step 5: Extract GitHub patches
            console.print("Extracting GitHub patches...")
            patches_by_cve = {
                cve.cve_id: patch
                for cve in cves
                if (patch := extract_github_patch(cve)) is not None
            }
            cves = [cve for cve in cves if cve.cve_id in patches_by_cve]
            console.print(f"After GitHub patch extraction: {len(cves)} CVEs")

            # Step 6: Verify code type
            if language:
                verified_cve_ids = {
                    cve_id
                    for cve_id, patch in patches_by_cve.items()
                    if verify_patch_code_type(patch, language.lower())
                }
                cves = [cve for cve in cves if cve.cve_id in verified_cve_ids]
                console.print(f"After {language} code type verification: {len(cves)} CVEs")

            if not cves:
                console.print("[yellow]No CVEs match all filters. Exiting.[/yellow]")
                return

            # Step 7: Export to CSV
            console.print(f"Exporting to: {output}")
            # Pass cwe_types to show only matching CWEs in output
            cwe_set_for_export = {cwe.strip() for cwe in cwe_types.split(",") if cwe.strip()} if cwe_types else None
            extract_cves_to_csv(cves, output, cwe_set_for_export)

            console.print(
                f"[bold green]Success! Exported {len(cves)} CVEs to {output}[/bold green]"
            )

    except Exception as e:
        console.print(f"[bold red]Error: {e}[/bold red]")
        logger.exception("Mining failed")
        raise typer.Exit(code=1) from e


@app.command()
def download(
    csv_file: Path = typer.Argument(
        ...,
        help="CSV file with CVE data (from mine command)",
    ),
    output_dir: Path = typer.Option(
        Path("repositories"),
        "--output",
        "-o",
        help="Output directory for downloaded repositories",
    ),
    language: str = typer.Option(
        "",
        "--language",
        help="Programming language for code type verification (if CSV not filtered)",
    ),
) -> None:
    """
    Download repositories for CVEs from CSV file.

    Note: This command requires re-mining CVE data as CSV lacks GitHub URLs.
    Use the mine command output to ensure proper filtering before download.
    """
    console.print("[bold blue]Starting repository download preparation...[/bold blue]")

    if not csv_file.exists():
        console.print(f"[bold red]CSV file not found: {csv_file}[/bold red]")
        raise typer.Exit(code=1)

    try:
        # Read CVE IDs from CSV
        cve_ids = []
        with open(csv_file, encoding="utf-8") as f:
            reader = csv.DictReader(f)
            for row in reader:
                cve_id = row.get("CVE ID")
                if cve_id:
                    cve_ids.append(cve_id)

        console.print(f"Found {len(cve_ids)} CVEs in CSV")

        # Create output directory
        output_dir.mkdir(parents=True, exist_ok=True)
        console.print(f"Output directory: {output_dir}")

        # Display configuration
        if language:
            console.print(f"Language filter: {language}")

        # Note about implementation
        console.print("\n[yellow]Note: Full repository download requires:[/yellow]")
        console.print("[yellow]1. Re-loading original CVE JSON files[/yellow]")
        console.print("[yellow]2. Extracting GitHub patch URLs[/yellow]")
        console.print("[yellow]3. Resolving pre-patch commits[/yellow]")
        console.print("[yellow]4. Downloading repository archives via GitHub API[/yellow]")
        console.print("\n[yellow]Current limitation: CSV does not store GitHub URLs.[/yellow]")
        console.print(
            "[yellow]Recommendation: Use mine command output path to access full CVE data.[/yellow]"
        )

        console.print(
            f"\n[bold green]Prepared for {len(cve_ids)} CVE repository downloads[/bold green]"
        )

    except Exception as e:
        console.print(f"[bold red]Error: {e}[/bold red]")
        logger.exception("Download preparation failed")
        raise typer.Exit(code=1) from e


@app.command()
def analyze(
    csv_file: Path = typer.Argument(
        ...,
        help="CSV file with CVE data (from mine command)",
    ),
    min_count: int = typer.Option(
        1,
        "--min-count",
        help="Minimum count to include CWE type in output",
    ),
) -> None:
    """
    Analyze CVE distribution from CSV file.

    Parses CSV and displays statistics: total CVEs, distribution by CWE type,
    year range, and most common CWE.
    """
    console.print("[bold blue]Analyzing CVE distribution...[/bold blue]")

    if not csv_file.exists():
        console.print(f"[bold red]CSV file not found: {csv_file}[/bold red]")
        raise typer.Exit(code=1)

    try:
        result = analyze_distribution(csv_file, min_count)

        # Display results
        console.print(f"\n[bold]Total CVEs:[/bold] {result['total_cves']}")

        year_range = result.get("year_range")
        if year_range:
            console.print(f"[bold]Year Range:[/bold] {year_range[0]} - {year_range[1]}")

        most_common = result.get("most_common")
        if most_common:
            console.print(
                f"[bold]Most Common:[/bold] {most_common[0]} " f"({most_common[1]} occurrences)"
            )

        # Display distribution table
        if result["stats"]:
            table = Table(title="CVE Distribution by CWE Type")
            table.add_column("CWE Type", style="cyan")
            table.add_column("Count", style="green", justify="right")
            table.add_column("Percentage", style="yellow", justify="right")

            total = result["total_cves"]
            sorted_stats = sorted(result["stats"].items(), key=lambda x: x[1], reverse=True)

            for cwe_type, count in sorted_stats:
                percentage = (count / total * 100) if total > 0 else 0
                table.add_row(cwe_type, str(count), f"{percentage:.1f}%")

            console.print(table)
        else:
            console.print("[yellow]No CWE types meet minimum count threshold.[/yellow]")

    except Exception as e:
        console.print(f"[bold red]Error: {e}[/bold red]")
        logger.exception("Analysis failed")
        raise typer.Exit(code=1) from e


@app.command()
def clean(
    cache_dir: Path = typer.Option(
        Path(".cache"),
        "--cache-dir",
        help="Cache directory to clean",
    ),
) -> None:
    """
    Clean temporary and cache files.

    Removes all cached downloads and temporary files.
    """
    console.print("[bold blue]Cleaning cache...[/bold blue]")

    try:
        if cache_dir.exists():
            shutil.rmtree(cache_dir)
            console.print(f"[bold green]Cleaned: {cache_dir}[/bold green]")
        else:
            console.print(f"[yellow]Cache directory does not exist: {cache_dir}[/yellow]")

    except Exception as e:
        console.print(f"[bold red]Error cleaning cache: {e}[/bold red]")
        raise typer.Exit(code=1) from e


if __name__ == "__main__":
    app()
