"""Compliance CLI commands — report generation and starter kits."""

from __future__ import annotations

from pathlib import Path
from typing import Annotated

import typer
from rich.console import Console

from apiposture_pro.licensing.manager import ProLicenseManager
from apiposture_pro.licensing.models import LicenseErrorCode, LicenseValidationResult

app = typer.Typer(help="Compliance report and starter-kit commands")
console = Console()


@app.command("report")
def report(
    path: Annotated[
        Path,
        typer.Argument(
            help="Path to scan (defaults to current directory)",
            exists=True,
            file_okay=False,
            dir_okay=True,
        ),
    ] = Path("."),
    framework: Annotated[
        str,
        typer.Option(
            "--framework",
            help="Compliance framework: soc2 | iso27001 | all",
        ),
    ] = "all",
    output: Annotated[
        str,
        typer.Option(
            "--output",
            help="Output format: pdf | html | both",
        ),
    ] = "both",
    operator: Annotated[
        str | None,
        typer.Option(
            "--operator",
            help="Operator name to embed in the report",
        ),
    ] = None,
    output_dir: Annotated[
        Path,
        typer.Option(
            "--output-dir",
            help="Directory to write report files (defaults to current directory)",
        ),
    ] = Path("."),
) -> None:
    """
    Generate a compliance report (PDF and/or HTML) from the latest scan of PATH.

    Requires enterprise license tier.
    """
    license_manager = ProLicenseManager()
    validation = license_manager.validate()
    if not validation.is_valid:
        _print_license_error(validation)
        raise typer.Exit(1)

    context = validation.context
    if not context or not context.is_enterprise_or_higher:
        console.print(
            "[red]✗[/red] 'compliance report' requires an enterprise license tier."
        )
        raise typer.Exit(1)

    if framework not in ("soc2", "iso27001", "all"):
        console.print("[red]✗[/red] --framework must be soc2, iso27001, or all")
        raise typer.Exit(1)

    if output not in ("pdf", "html", "both"):
        console.print("[red]✗[/red] --output must be pdf, html, or both")
        raise typer.Exit(1)

    # Run a fresh scan to get findings
    console.print(f"[cyan]Scanning:[/cyan] {path.absolute()}")
    try:
        import time

        from apiposture_pro.analysis.pro_analyzer import ProAnalyzer
        from apiposture_pro.enterprise.compliance.compliance_score import calculate_score
        from apiposture_pro.enterprise.compliance.report_generator import (
            build_report,
            generate_html,
            generate_pdf,
        )
        from apiposture_pro.features.historical_tracking.store import HistoryStore

        analyzer = ProAnalyzer()
        start = time.time()
        result = analyzer.analyze(path)
        duration_ms = int((time.time() - start) * 1000)

        # Count severities
        from collections import Counter

        sev_counts = Counter(
            (f.severity.value if hasattr(f.severity, "value") else str(f.severity)).lower()
            for f in result.findings
        )

        # Compute compliance score — look for previous scan of same path for trend
        store = HistoryStore()
        prev_scans = store.list_scans(
            project_path=str(path.absolute()), limit=1
        )
        prev_score = prev_scans[0].compliance_score if prev_scans else None

        compliance_score = calculate_score(
            critical=sev_counts.get("critical", 0),
            high=sev_counts.get("high", 0),
            medium=sev_counts.get("medium", 0),
            low=sev_counts.get("low", 0),
            previous_score=prev_score,
        )

        # Build raw findings list
        raw_findings = []
        for f in result.findings:
            fd = {
                "rule_id": f.rule_id,
                "severity": f.severity.value if hasattr(f.severity, "value") else str(f.severity),
                "message": f.message,
            }
            if hasattr(f, "endpoint") and f.endpoint:
                fd["location"] = f"{f.endpoint.file_path}:{f.endpoint.line_number}"
            else:
                fd["location"] = ""
            raw_findings.append(fd)

        # Git commit
        import subprocess

        git_commit = None
        try:
            r = subprocess.run(
                ["git", "rev-parse", "--short", "HEAD"],
                capture_output=True,
                text=True,
                cwd=str(path),
                timeout=5,
            )
            if r.returncode == 0:
                git_commit = r.stdout.strip()
        except Exception:
            pass

        from datetime import datetime

        now = datetime.now()
        # Save scan with operator + compliance_score
        scan_id_int = store.save_scan(
            result,
            duration_ms=duration_ms,
            risk_score=getattr(result, "risk_score", 0),
            operator=operator,
            compliance_score=compliance_score.score,
        )

        report_obj = build_report(
            scan_id=str(scan_id_int),
            timestamp=now,
            scanned_path=str(path.absolute()),
            raw_findings=raw_findings,
            total_endpoints=len(result.endpoints),
            compliance_score=compliance_score,
            framework=framework,
            operator=operator,
            git_commit=git_commit,
        )

        output_dir.mkdir(parents=True, exist_ok=True)
        timestamp_str = now.strftime("%Y%m%d-%H%M%S")
        framework_slug = framework.replace("_", "")

        created = []
        if output in ("html", "both"):
            html_path = output_dir / f"compliance-report-{framework_slug}-{timestamp_str}.html"
            generate_html(report_obj, html_path)
            created.append(html_path)

        if output in ("pdf", "both"):
            pdf_path = output_dir / f"compliance-report-{framework_slug}-{timestamp_str}.pdf"
            generate_pdf(report_obj, pdf_path)
            created.append(pdf_path)

        console.print(
            f"\n[green]✓[/green] Compliance score: "
            f"[bold]{compliance_score.score}/100[/bold]  "
            f"trend: {compliance_score.trend.upper()}"
        )
        for p in created:
            console.print(f"[green]✓[/green] Report written: {p}")

    except Exception as e:
        console.print(f"\n[red]✗[/red] Failed to generate report: {e}")
        raise typer.Exit(1) from e


@app.command("starter-kit")
def starter_kit(
    framework: Annotated[
        str,
        typer.Argument(
            help="Framework: soc2 or iso27001",
        ),
    ],
    output_dir: Annotated[
        Path,
        typer.Argument(
            help="Directory to emit starter kit files (created if missing)",
        ),
    ] = Path("./apiposture-starter-kit"),
) -> None:
    """
    Emit a compliance starter kit (policy JSON, sample report, GH Actions, README).

    Requires enterprise license tier.
    """
    license_manager = ProLicenseManager()
    validation = license_manager.validate()
    if not validation.is_valid:
        _print_license_error(validation)
        raise typer.Exit(1)

    context = validation.context
    if not context or not context.is_enterprise_or_higher:
        console.print(
            "[red]✗[/red] 'compliance starter-kit' requires an enterprise license tier."
        )
        raise typer.Exit(1)

    if framework not in ("soc2", "iso27001"):
        console.print("[red]✗[/red] Framework must be 'soc2' or 'iso27001'")
        raise typer.Exit(1)

    try:
        from apiposture_pro.enterprise.starter_kits.generator import emit_starter_kit

        created = emit_starter_kit(framework, output_dir)
        console.print(f"\n[green]✓[/green] Starter kit written to: {output_dir.absolute()}")
        for p in created:
            rel = p.relative_to(output_dir)
            console.print(f"  • {rel}")

    except Exception as e:
        console.print(f"\n[red]✗[/red] Failed to generate starter kit: {e}")
        raise typer.Exit(1) from e


def _print_license_error(result: LicenseValidationResult) -> None:
    """Print error-code-specific license error message."""
    messages = {
        LicenseErrorCode.EXPIRED: f"[red]✗[/red] Your license has expired. {result.error_message}",
        LicenseErrorCode.MACHINE_MISMATCH: "[red]✗[/red] License is bound to a different machine.",
        LicenseErrorCode.REVOKED: "[red]✗[/red] Your license has been revoked.",
        LicenseErrorCode.TOO_MANY_ACTIVATIONS: "[red]✗[/red] Too many activations.",
        LicenseErrorCode.NETWORK_ERROR: "[red]✗[/red] License could not be validated offline.",
        LicenseErrorCode.GRACE_PERIOD_EXPIRED: "[red]✗[/red] License expired and grace period ended.",
        LicenseErrorCode.INVALID_SIGNATURE: "[red]✗[/red] License key has an invalid signature.",
    }
    msg = messages.get(
        result.error_code,
        f"[red]✗[/red] No valid license found. {result.error_message}",
    )
    console.print(msg)
    console.print("\nActivate your license with: apiposture-pro activate <key>")
