"""DebtMap CLI — Technical Debt Intelligence from the command line.

Usage:
    debtmap scan [PATH]           Scan a codebase for technical debt
    debtmap report [PATH]         Generate an executive report
    debtmap prioritize [PATH]     Get ROI-ranked priorities
    debtmap trend [PATH]          Show debt trend over time
    debtmap hotspots [PATH]       Find worst code hotspots
    debtmap cost [PATH]           Estimate $ cost of debt
"""

from __future__ import annotations

import json
import sys
from pathlib import Path

try:
    import click
    from rich.console import Console
    from rich.table import Table
    from rich.panel import Panel
except ImportError:
    print("DebtMap CLI requires 'click' and 'rich'. Install with: pip install click rich")
    sys.exit(1)

from .core.scanner import Scanner
from .core.prioritizer import Prioritizer
from .core.trend_analyzer import TrendAnalyzer
from .core.report_generator import ReportGenerator
from .store import DebtStore

console = Console()


def _get_store() -> DebtStore:
    return DebtStore()


@click.group()
@click.version_option(version="0.1.0", prog_name="debtmap")
def main() -> None:
    """DebtMap — Technical Debt Intelligence.

    Connect code quality to business impact.
    """
    pass


@main.command()
@click.argument("path", default=".", type=click.Path(exists=True))
@click.option("--exclude", "-e", multiple=True, help="Directories to exclude")
@click.option("--skip-deps", is_flag=True, help="Skip dependency version check")
@click.option("--json-output", "-j", is_flag=True, help="Output as JSON")
def scan(path: str, exclude: tuple, skip_deps: bool, json_output: bool) -> None:
    """Scan a codebase for technical debt."""
    store = _get_store()
    scanner = Scanner(store=store)

    exclude_list = list(exclude) if exclude else None

    with console.status("[bold blue]Scanning codebase...") as status:
        result = scanner.scan(
            directory=path,
            exclude_patterns=exclude_list,
            skip_dependency_check=skip_deps,
        )

    if json_output:
        click.echo(json.dumps(result, indent=2, default=str))
        return

    # Rich output
    score = result["total_score"]
    grade = _score_to_grade(score)
    color = _grade_color(grade)

    console.print(Panel(
        f"[bold {color}]Debt Score: {score:.1f}/100  Grade: {grade}[/]\n"
        f"Files: {result['file_count']}  Issues: {result['total_issues']}  "
        f"Time: {result['elapsed_seconds']:.1f}s",
        title="[bold]DebtMap Scan Results[/]",
        subtitle=f"Scan ID: {result['scan_id']}",
    ))

    summary = result.get("summary", {})
    table = Table(title="Analysis Summary")
    table.add_column("Category", style="cyan")
    table.add_column("Key Metric", style="white")
    table.add_column("Status", style="white")

    cx = summary.get("complexity", {})
    bad_funcs = cx.get("complex_count", 0) + cx.get("very_complex_count", 0) + cx.get("unmaintainable_count", 0)
    table.add_row("Complexity", f"{cx.get('total_functions', 0)} functions", _status_badge(bad_funcs, 5, 15))

    cp = summary.get("coupling", {})
    table.add_row("Coupling", f"{cp.get('total_modules', 0)} modules", _status_badge(cp.get("highly_coupled_count", 0), 3, 10))

    dp = summary.get("duplication", {})
    table.add_row("Duplication", f"{dp.get('duplicate_block_count', 0)} blocks", _status_badge(dp.get("duplicate_block_count", 0), 10, 30))

    dc = summary.get("dead_code", {})
    table.add_row("Dead Code", f"{dc.get('total_dead_code_items', 0)} items", _status_badge(dc.get("total_dead_code_items", 0), 10, 30))

    tc = summary.get("test_coverage", {})
    table.add_row("Test Gaps", f"{tc.get('total_uncovered_items', 0)} uncovered", _status_badge(tc.get("total_uncovered_items", 0), 20, 50))

    doc = summary.get("documentation", {})
    table.add_row("Doc Gaps", f"{doc.get('total_missing_docs', 0)} missing", _status_badge(doc.get("total_missing_docs", 0), 20, 50))

    dep = summary.get("dependencies", {})
    table.add_row("Dependencies", f"{dep.get('outdated_count', 0)}/{dep.get('total_dependencies', 0)} outdated", _status_badge(dep.get("critical_outdated", 0), 1, 3))

    console.print(table)

    # Show top hotspots
    hotspots = result.get("details", {}).get("complexity_hotspots", [])[:5]
    if hotspots:
        console.print("\n[bold]Top Complexity Hotspots:[/]")
        for h in hotspots:
            console.print(
                f"  [red]{h['complexity']}[/] {h['name']} "
                f"[dim]({h['file_path'].split('/')[-1]}:{h['line_number']})[/]"
            )


@main.command()
@click.argument("path", default=".", type=click.Path(exists=True))
@click.option("--format", "-f", "fmt", type=click.Choice(["text", "json"]), default="text")
def report(path: str, fmt: str) -> None:
    """Generate an executive-ready debt report."""
    store = _get_store()
    scanner = Scanner(store=store)
    prioritizer = Prioritizer()
    trend_analyzer = TrendAnalyzer(store=store)
    reporter = ReportGenerator()

    with console.status("[bold blue]Running full analysis..."):
        scan_result = scanner.scan(directory=path, skip_dependency_check=True)
        items = prioritizer.prioritize(scan_result)
        trend = trend_analyzer.analyze(str(Path(path).resolve()))

    if fmt == "json":
        output = reporter.generate_json_report(
            scan_result,
            [i.to_dict() for i in items],
            trend.to_dict(),
        )
    else:
        output = reporter.generate_executive_summary(
            scan_result,
            [i.to_dict() for i in items],
            trend.to_dict(),
        )

    click.echo(output)


@main.command()
@click.argument("path", default=".", type=click.Path(exists=True))
@click.option("--limit", "-n", default=20, help="Max items to show")
@click.option("--json-output", "-j", is_flag=True, help="Output as JSON")
def prioritize(path: str, limit: int, json_output: bool) -> None:
    """Get ROI-ranked list of what to fix first."""
    store = _get_store()
    scanner = Scanner(store=store)
    prioritizer = Prioritizer()

    with console.status("[bold blue]Analyzing priorities..."):
        scan_result = scanner.scan(directory=path, skip_dependency_check=True)
        items = prioritizer.prioritize(scan_result, limit=limit)

    if json_output:
        click.echo(json.dumps([i.to_dict() for i in items], indent=2))
        return

    table = Table(title="Priority Queue (by ROI)")
    table.add_column("#", style="bold")
    table.add_column("Type", style="cyan")
    table.add_column("Severity", style="white")
    table.add_column("Fix (hrs)", justify="right")
    table.add_column("Saves/yr", justify="right", style="green")
    table.add_column("ROI (mo)", justify="right")
    table.add_column("Description", style="dim")

    for item in items:
        sev_color = {"critical": "red", "high": "yellow", "medium": "blue", "low": "dim"}.get(item.severity, "white")
        table.add_row(
            str(item.priority_rank),
            item.issue_type,
            f"[{sev_color}]{item.severity}[/]",
            f"{item.fix_effort_hours:.0f}",
            f"${item.annual_savings_usd:,.0f}",
            f"{item.roi_months:.1f}",
            item.description[:60],
        )

    console.print(table)

    summary = prioritizer.summary(items)
    console.print(f"\n[bold]Quick wins (ROI < 2 months):[/] {summary['quick_wins']}")
    console.print(f"[bold]Total annual savings:[/] [green]${summary['total_annual_savings_usd']:,.0f}[/]")


@main.command()
@click.argument("path", default=".", type=click.Path(exists=True))
def trend(path: str) -> None:
    """Show debt trend over time."""
    store = _get_store()
    analyzer = TrendAnalyzer(store=store)

    repo_path = str(Path(path).resolve())
    result = analyzer.analyze(repo_path)

    if result.direction == "insufficient_data":
        console.print("[yellow]Not enough scan history. Run more scans to see trends.[/]")
        return

    arrow = {"improving": "[green]v[/]", "degrading": "[red]^[/]", "stable": "[blue]-[/]"}.get(
        result.direction, "?"
    )
    console.print(Panel(
        f"Direction: {result.direction} {arrow}\n"
        f"Score change: {result.score_change:+.1f} ({result.score_change_pct:+.1f}%)\n"
        f"Issue change: {result.issue_change:+d}\n"
        f"Period: {result.period_days:.0f} days\n"
        f"Data points: {len(result.data_points)}",
        title="[bold]Debt Trend[/]",
    ))


@main.command()
@click.argument("path", default=".", type=click.Path(exists=True))
@click.option("--limit", "-n", default=10, help="Max hotspots to show")
def hotspots(path: str, limit: int) -> None:
    """Find worst code hotspots."""
    store = _get_store()
    scanner = Scanner(store=store)

    with console.status("[bold blue]Finding hotspots..."):
        scan_result = scanner.scan(directory=path, skip_dependency_check=True)

    spots = scan_result.get("details", {}).get("complexity_hotspots", [])[:limit]

    if not spots:
        console.print("[green]No complexity hotspots found.[/]")
        return

    table = Table(title="Code Hotspots")
    table.add_column("Complexity", justify="right", style="red bold")
    table.add_column("Rating", style="yellow")
    table.add_column("Function", style="cyan")
    table.add_column("File", style="dim")
    table.add_column("Lines", justify="right")

    for h in spots:
        table.add_row(
            str(h["complexity"]),
            h["rating"],
            h["name"],
            h["file_path"].split("/")[-1] + f":{h['line_number']}",
            str(h.get("line_count", "?")),
        )

    console.print(table)


@main.command()
@click.argument("path", default=".", type=click.Path(exists=True))
@click.option("--hourly-rate", default=75.0, help="Engineer hourly rate (USD)")
@click.option("--team-size", default=5, help="Team size")
def cost(path: str, hourly_rate: float, team_size: int) -> None:
    """Estimate the $ cost of technical debt."""
    from .business.cost_estimator import CostEstimator

    store = _get_store()
    scanner = Scanner(store=store)
    estimator = CostEstimator(hourly_rate_usd=hourly_rate, team_size=team_size)

    with console.status("[bold blue]Estimating costs..."):
        scan_result = scanner.scan(directory=path, skip_dependency_check=True)

    # Build per-file estimates from scan data
    estimates = []
    details = scan_result.get("details", {})

    # Get complexity per file
    file_complexity: dict[str, float] = {}
    for h in details.get("complexity_hotspots", []):
        fp = h["file_path"]
        file_complexity[fp] = max(file_complexity.get(fp, 0), h["complexity"])

    for fp, cx in file_complexity.items():
        est = estimator.estimate_file(file_path=fp, complexity_score=cx)
        estimates.append(est)

    project = estimator.estimate_project(estimates)

    console.print(Panel(
        f"[bold red]Monthly Cost:[/] ${project['total_monthly_cost_usd']:,.0f}\n"
        f"[bold red]Annual Cost:[/] ${project['total_annual_cost_usd']:,.0f}\n"
        f"[bold green]Fix Cost:[/] ${project['total_fix_cost_usd']:,.0f}\n"
        f"[bold]Avg ROI:[/] {project['avg_roi_months']:.1f} months\n"
        f"[bold]Team Capacity Waste:[/] {project.get('team_capacity_waste_pct', 0):.1f}%",
        title="[bold]Technical Debt Cost Estimate[/]",
        subtitle=f"Rate: ${hourly_rate}/hr | Team: {team_size}",
    ))


def _score_to_grade(score: float) -> str:
    if score <= 10:
        return "A"
    elif score <= 25:
        return "B"
    elif score <= 50:
        return "C"
    elif score <= 75:
        return "D"
    return "F"


def _grade_color(grade: str) -> str:
    return {"A": "green", "B": "green", "C": "yellow", "D": "red", "F": "red bold"}.get(grade, "white")


def _status_badge(count: int, warn_threshold: int, error_threshold: int) -> str:
    if count >= error_threshold:
        return f"[red bold]{count} issues[/]"
    elif count >= warn_threshold:
        return f"[yellow]{count} issues[/]"
    return f"[green]OK ({count})[/]"


if __name__ == "__main__":
    main()
