"""AISA compliance report generator.

Produces both terminal (rich) and markdown reports from scan/assessment results.
"""

from pathlib import Path
from datetime import datetime

from ..scanner.analyzer import ScanResult, LayerResult
from .recommendations import get_recommendation, get_priority


# ---------------------------------------------------------------------------
# Terminal Report (plain text with ASCII art)
# ---------------------------------------------------------------------------

def print_terminal_report(result: ScanResult) -> None:
    """Print a formatted compliance report to the terminal."""
    print()
    _print_header(result)
    _print_frameworks(result)
    _print_layer_bars(result)
    _print_contract_bars(result)
    _print_overall_score(result)
    _print_top_gaps(result)
    _print_gap_details(result)
    print()


def _print_header(result: ScanResult) -> None:
    width = 60
    print("=" * width)
    print("  AISA COMPLIANCE REPORT")
    print(f"  Agentic AI Systems Architecture Checker v0.1.0")
    print("=" * width)
    print(f"  Project:  {result.project_path}")
    print(f"  Date:     {datetime.now().strftime('%Y-%m-%d %H:%M')}")
    if result.files_scanned > 0:
        print(f"  Files:    {result.files_scanned} Python files ({result.total_lines:,} lines)")
    print("=" * width)


def _print_frameworks(result: ScanResult) -> None:
    if not result.frameworks_detected:
        return
    print()
    print("  DETECTED FRAMEWORKS:")
    for name, confidence in sorted(result.frameworks_detected.items(), key=lambda x: -x[1]):
        bar_len = int(confidence * 10)
        bar = "#" * bar_len + "." * (10 - bar_len)
        print(f"    [{bar}] {name} ({confidence:.0%})")
    print()


def _make_bar(score: float, width: int = 20) -> str:
    """Create a text progress bar."""
    filled = int(score / 100 * width)
    empty = width - filled
    return "█" * filled + "░" * empty


def _score_label(score: float) -> str:
    if score >= 80:
        return "Strong"
    elif score >= 50:
        return "Partial"
    elif score >= 20:
        return "Minimal"
    else:
        return "Absent"


def _print_layer_bars(result: ScanResult) -> None:
    print("  LAYER COVERAGE:")
    print("  " + "-" * 56)
    for lr in result.layer_results:
        score = lr.coverage_score
        bar = _make_bar(score)
        label = _score_label(score)
        layer_name = lr.name.replace("Layer", "").strip()
        if len(layer_name) > 22:
            layer_name = layer_name[:22]
        print(f"  {lr.id} {layer_name:<22} {bar} {score:5.1f}%  [{label}]")
    print("  " + "-" * 56)


def _print_contract_bars(result: ScanResult) -> None:
    print()
    print("  CROSS-LAYER CONTRACTS:")
    print("  " + "-" * 56)
    for cr in result.contract_results:
        score = cr.coverage_score
        bar = _make_bar(score)
        label = _score_label(score)
        name = cr.name.replace("Contract", "").strip()
        if len(name) > 22:
            name = name[:22]
        print(f"  {cr.id} {name:<22} {bar} {score:5.1f}%  [{label}]")
    print("  " + "-" * 56)


def _print_overall_score(result: ScanResult) -> None:
    score = result.overall_score
    print()
    print(f"  OVERALL AISA SCORE: {score:.0f}/100")
    print(f"  {_make_bar(score, 40)}")
    print()

    if score >= 80:
        grade = "A - Production Ready"
    elif score >= 60:
        grade = "B - Maturing"
    elif score >= 40:
        grade = "C - Developing"
    elif score >= 20:
        grade = "D - Early Stage"
    else:
        grade = "F - Significant Gaps"
    print(f"  GRADE: {grade}")
    print()


def _print_top_gaps(result: ScanResult) -> None:
    gaps = _collect_gaps(result)
    if not gaps:
        print("  No gaps detected!")
        return

    print("  TOP GAPS (by priority):")
    print("  " + "-" * 56)
    # Sort by priority: Critical > High > Medium
    priority_order = {"Critical": 0, "High": 1, "Medium": 2}
    gaps.sort(key=lambda g: (priority_order.get(g[0], 3), g[1]))

    shown = 0
    for priority, component_id, criterion_id, criterion_name in gaps:
        if shown >= 10:
            remaining = len(gaps) - shown
            if remaining > 0:
                print(f"  ... and {remaining} more gaps")
            break
        print(f"  [{priority:>8}] {criterion_id}: {criterion_name}")
        shown += 1
    print()


def _print_gap_details(result: ScanResult) -> None:
    gaps = _collect_gaps(result)
    if not gaps:
        return

    print("  RECOMMENDATIONS:")
    print("  " + "-" * 56)
    shown = 0
    for priority, component_id, criterion_id, criterion_name in gaps:
        if shown >= 5:
            break
        rec = get_recommendation(criterion_id)
        if rec:
            print(f"  {criterion_id}: {rec}")
            print()
            shown += 1


def _collect_gaps(result: ScanResult) -> list[tuple[str, str, str, str]]:
    """Collect all uncovered criteria as (priority, component_id, criterion_id, name)."""
    gaps = []
    for lr in result.layer_results + result.contract_results:
        priority = get_priority(lr.id)
        for cr in lr.criteria_results:
            if not cr.covered:
                gaps.append((priority, lr.id, cr.criterion_id, cr.criterion_name))
    return gaps


# ---------------------------------------------------------------------------
# Markdown Report
# ---------------------------------------------------------------------------

def generate_markdown_report(result: ScanResult, output_path: Path | None = None) -> str:
    """Generate a detailed markdown compliance report.

    Args:
        result: The scan or assessment result.
        output_path: If provided, write the report to this file.

    Returns:
        The markdown report as a string.
    """
    lines: list[str] = []

    lines.append("# AISA Compliance Report")
    lines.append("")
    lines.append(f"**Project:** {result.project_path}")
    lines.append(f"**Date:** {datetime.now().strftime('%Y-%m-%d %H:%M')}")
    lines.append(f"**AISA Checker Version:** 0.1.0")
    if result.files_scanned > 0:
        lines.append(f"**Files Scanned:** {result.files_scanned} Python files ({result.total_lines:,} lines)")
    lines.append("")

    # Frameworks
    if result.frameworks_detected:
        lines.append("## Detected Frameworks")
        lines.append("")
        lines.append("| Framework | Confidence |")
        lines.append("|-----------|:----------:|")
        for name, conf in sorted(result.frameworks_detected.items(), key=lambda x: -x[1]):
            lines.append(f"| {name} | {conf:.0%} |")
        lines.append("")

    # Overall Score
    score = result.overall_score
    grade = _get_grade(score)
    lines.append("## Overall Score")
    lines.append("")
    lines.append(f"**Score: {score:.0f}/100 | Grade: {grade}**")
    lines.append("")

    # Layer Scores Table
    lines.append("## Layer Coverage")
    lines.append("")
    lines.append("| Layer | Coverage | Status |")
    lines.append("|-------|:--------:|:------:|")
    for lr in result.layer_results:
        s = lr.coverage_score
        status = _score_label(s)
        lines.append(f"| {lr.id}: {lr.name} | {s:.0f}% | {status} |")
    lines.append("")

    # Contract Scores Table
    lines.append("## Cross-Layer Contract Coverage")
    lines.append("")
    lines.append("| Contract | Coverage | Status |")
    lines.append("|----------|:--------:|:------:|")
    for cr in result.contract_results:
        s = cr.coverage_score
        status = _score_label(s)
        lines.append(f"| {cr.id}: {cr.name} | {s:.0f}% | {status} |")
    lines.append("")

    # Detailed Layer Breakdown
    lines.append("## Detailed Breakdown")
    lines.append("")

    all_results = result.layer_results + result.contract_results
    for lr in all_results:
        priority = get_priority(lr.id)
        lines.append(f"### {lr.id}: {lr.name}")
        lines.append(f"**Coverage: {lr.coverage_score:.0f}% | Priority: {priority}**")
        lines.append("")
        lines.append("| Criterion | Status | Confidence | Evidence |")
        lines.append("|-----------|:------:|:----------:|----------|")
        for cr in lr.criteria_results:
            status = "Covered" if cr.covered else "**GAP**"
            conf = f"{cr.confidence:.0%}" if cr.covered else "-"
            evidence = ", ".join(cr.evidence[:3]) if cr.evidence else "-"
            lines.append(f"| {cr.criterion_id}: {cr.criterion_name} | {status} | {conf} | {evidence} |")
        lines.append("")

        # Recommendations for gaps
        gaps_in_layer = [cr for cr in lr.criteria_results if not cr.covered]
        if gaps_in_layer:
            lines.append("**Recommendations:**")
            lines.append("")
            for cr in gaps_in_layer:
                rec = get_recommendation(cr.criterion_id)
                if rec:
                    lines.append(f"- **{cr.criterion_id}:** {rec}")
            lines.append("")

    # Summary
    lines.append("## Summary")
    lines.append("")
    total_criteria = sum(lr.total_count for lr in all_results)
    total_covered = sum(lr.covered_count for lr in all_results)
    total_gaps = total_criteria - total_covered
    lines.append(f"- **Total criteria evaluated:** {total_criteria}")
    lines.append(f"- **Criteria covered:** {total_covered}")
    lines.append(f"- **Gaps identified:** {total_gaps}")
    lines.append(f"- **Overall compliance:** {score:.0f}%")
    lines.append("")
    lines.append("---")
    lines.append("*Generated by AISA Compliance Checker (https://github.com/AISA-Framework/aisa)*")
    lines.append("*Based on the AISA reference architecture by Nacar, Alquffari & Alkhalifa (2026)*")

    report = "\n".join(lines)

    if output_path:
        output_path.write_text(report)

    return report


def _get_grade(score: float) -> str:
    if score >= 80:
        return "A - Production Ready"
    elif score >= 60:
        return "B - Maturing"
    elif score >= 40:
        return "C - Developing"
    elif score >= 20:
        return "D - Early Stage"
    else:
        return "F - Significant Gaps"
