"""Health Scanner - Codebase health orchestrator.

Orchestrates all available scanners and produces unified health report.
Routes findings to appropriate agents for action.

Usage:
    from sef_agents.tools.health_scanner import scan_health
    result = scan_health("/path/to/project")

Dependencies:
    - All scanner modules in sef_agents.tools
    - structlog for logging
"""

import json
from dataclasses import dataclass, field
from datetime import datetime, timezone
from enum import Enum
from pathlib import Path

import structlog

from sef_agents.tools.report_utils import write_report

logger = structlog.get_logger(__name__)


def _generate_tech_debt_md(findings: list, directory: Path) -> Path | None:
    """Generate docs/TECH_DEBT.md from findings.

    Args:
        findings: List of HealthFinding objects.
        directory: Project root directory.

    Returns:
        Path to generated file or None if failed.
    """
    debt_findings = [f for f in findings if f.scanner == "debt"]
    if not debt_findings:
        return None

    docs_dir = directory / "docs"
    docs_dir.mkdir(exist_ok=True)

    content = """# Technical Debt Registry

## Overview

This document tracks technical debt identified during codebase analysis.

| Severity | Count |
|----------|-------|
"""
    severity_counts = {}
    for f in debt_findings:
        sev = f.severity.value
        severity_counts[sev] = severity_counts.get(sev, 0) + 1

    for sev in ["critical", "high", "medium", "low"]:
        count = severity_counts.get(sev, 0)
        if count > 0:
            content += f"| {sev.capitalize()} | {count} |\n"

    content += "\n## Debt Items\n\n"
    content += "| ID | File | Line | Type | Severity | Description |\n"
    content += "|----|------|------|------|----------|-------------|\n"

    for i, f in enumerate(debt_findings[:100], 1):  # Limit to 100
        file_name = Path(f.file).name
        content += f"| D-{i:03d} | `{file_name}` | {f.line} | {f.rule} | {f.severity.value} | {f.message[:50]}... |\n"

    if len(debt_findings) > 100:
        content += f"\n*...and {len(debt_findings) - 100} more items*\n"

    content += "\n---\n*Auto-generated by health scanner. See handoff_log.jsonl for full details.*\n"

    debt_path = docs_dir / "TECH_DEBT.md"
    try:
        debt_path.write_text(content, encoding="utf-8")
        return debt_path
    except OSError:
        return None


def _relocate_artifacts(directory: Path) -> list[str]:
    """Relocate misplaced artifacts to docs/.

    Args:
        directory: Project root directory.

    Returns:
        List of relocation messages.
    """
    relocations: list[str] = []
    docs_dir = directory / "docs"

    # Files to relocate: root -> docs/
    to_relocate = {
        "ARCHITECTURE.md": "ARCHITECTURE.md",
        "EXTERNAL_DEPS.md": "EXTERNAL_DEPS.md",
        "EXTERNAL_APIS.md": "EXTERNAL_DEPS.md",  # Rename
    }

    for src_name, dst_name in to_relocate.items():
        src_path = directory / src_name
        dst_path = docs_dir / dst_name

        if src_path.exists() and not dst_path.exists():
            docs_dir.mkdir(exist_ok=True)
            try:
                # Copy content (don't move to preserve git history option)
                content = src_path.read_text(encoding="utf-8")
                dst_path.write_text(content, encoding="utf-8")
                relocations.append(f"Copied `{src_name}` → `docs/{dst_name}`")
            except OSError:
                pass

    return relocations


# Common directories to skip in all scanners
SKIP_DIRECTORIES = frozenset(
    {
        ".git",
        "__pycache__",
        ".venv",
        "venv",
        "node_modules",
        "dist",
        "build",
        "target",
        ".tox",
        ".mypy_cache",
        ".pytest_cache",
    }
)


def _should_skip_path(path: Path | str) -> bool:
    """Check if path should be skipped during scanning.

    Args:
        path: File or directory path.

    Returns:
        True if path contains any skip directories.
    """
    path_str = str(path)
    return any(skip in path_str for skip in SKIP_DIRECTORIES)


def _determine_complexity_severity(value: int, threshold: int) -> "Severity":
    """Determine severity based on value vs threshold.

    Args:
        value: Current value (complexity, LOC, etc.).
        threshold: Threshold from config.

    Returns:
        Severity level.
    """
    if value > threshold * 2.5:
        return Severity.CRITICAL
    if value > threshold * 1.5:
        return Severity.HIGH
    return Severity.LOW


def _deduplicate_findings(findings: list["HealthFinding"]) -> list["HealthFinding"]:
    """Deduplicate findings based on file, line, and message.

    Args:
        findings: List of findings to deduplicate.

    Returns:
        List of unique findings.
    """
    seen: set[tuple[str, int, str]] = set()
    unique_findings: list["HealthFinding"] = []

    for finding in findings:
        key = (finding.file, finding.line, finding.message[:50])
        if key not in seen:
            seen.add(key)
            unique_findings.append(finding)

    return unique_findings


class ProjectType(str, Enum):
    """Detected project type."""

    PYTHON = "python"
    JAVASCRIPT = "javascript"
    TYPESCRIPT = "typescript"
    JAVA = "java"
    UNKNOWN = "unknown"


class Severity(str, Enum):
    """Issue severity levels."""

    CRITICAL = "critical"
    HIGH = "high"
    MEDIUM = "medium"
    LOW = "low"


@dataclass
class HealthFinding:
    """Single health finding from any scanner."""

    scanner: str
    file: str
    line: int
    message: str
    severity: Severity
    owner: str
    rule: str = ""

    def to_dict(self) -> dict:
        """Convert to dictionary."""
        return {
            "scanner": self.scanner,
            "file": self.file,
            "line": self.line,
            "message": self.message,
            "severity": self.severity.value,
            "owner": self.owner,
            "rule": self.rule,
        }


@dataclass
class HealthReport:
    """Aggregated health report."""

    directory: str
    project_type: ProjectType
    timestamp: str = field(
        default_factory=lambda: datetime.now(timezone.utc).isoformat()
    )
    files_scanned: int = 0
    findings: list[HealthFinding] = field(default_factory=list)
    scanner_errors: dict[str, str] = field(default_factory=dict)

    @property
    def critical_count(self) -> int:
        """Count critical findings."""
        return sum(1 for f in self.findings if f.severity == Severity.CRITICAL)

    @property
    def high_count(self) -> int:
        """Count high severity findings."""
        return sum(1 for f in self.findings if f.severity == Severity.HIGH)

    @property
    def medium_count(self) -> int:
        """Count medium severity findings."""
        return sum(1 for f in self.findings if f.severity == Severity.MEDIUM)

    @property
    def low_count(self) -> int:
        """Count low severity findings."""
        return sum(1 for f in self.findings if f.severity == Severity.LOW)

    def by_owner(self) -> dict[str, list[HealthFinding]]:
        """Group findings by owner agent."""
        grouped: dict[str, list[HealthFinding]] = {}
        for finding in self.findings:
            if finding.owner not in grouped:
                grouped[finding.owner] = []
            grouped[finding.owner].append(finding)
        return grouped

    def by_scanner(self) -> dict[str, list[HealthFinding]]:
        """Group findings by scanner."""
        grouped: dict[str, list[HealthFinding]] = {}
        for finding in self.findings:
            if finding.scanner not in grouped:
                grouped[finding.scanner] = []
            grouped[finding.scanner].append(finding)
        return grouped

    def to_json(self) -> str:
        """Convert to JSON string."""
        return json.dumps(
            {
                "directory": self.directory,
                "project_type": self.project_type.value,
                "timestamp": self.timestamp,
                "files_scanned": self.files_scanned,
                "summary": {
                    "critical": self.critical_count,
                    "high": self.high_count,
                    "medium": self.medium_count,
                    "low": self.low_count,
                    "total": len(self.findings),
                },
                "findings": [f.to_dict() for f in self.findings],
                "errors": self.scanner_errors,
            },
            indent=2,
        )


def detect_project_type(directory: Path) -> ProjectType:
    """Detect project type from directory contents.

    Args:
        directory: Path to project directory.

    Returns:
        Detected project type.
    """
    if (directory / "pyproject.toml").exists() or (directory / "setup.py").exists():
        return ProjectType.PYTHON
    if (directory / "package.json").exists():
        if (directory / "tsconfig.json").exists():
            return ProjectType.TYPESCRIPT
        return ProjectType.JAVASCRIPT
    if (directory / "pom.xml").exists() or (directory / "build.gradle").exists():
        return ProjectType.JAVA
    # Check for Python files
    if list(directory.rglob("*.py")):
        return ProjectType.PYTHON
    return ProjectType.UNKNOWN


def run_code_quality_scanner(directory: Path, report: HealthReport) -> None:
    """Run code quality scanner and add findings to report.

    Args:
        directory: Directory to scan.
        report: Health report to add findings to.
    """
    try:
        from sef_agents.tools.code_quality_scanner import scan_directory

        result = scan_directory(directory)
        report.files_scanned = max(report.files_scanned, result.files_scanned)

        for issue in result.issues:
            report.findings.append(
                HealthFinding(
                    scanner="code_quality",
                    file=issue.file,
                    line=issue.line,
                    message=issue.message,
                    severity=Severity(issue.severity.value),
                    owner=issue.owner.value,
                    rule=issue.rule,
                )
            )
    except Exception as e:
        logger.error("scanner_error", scanner="code_quality", error=str(e))
        report.scanner_errors["code_quality"] = str(e)
        raise


def run_complexity_scanner(directory: Path, report: HealthReport) -> None:
    """Run complexity scanner and add findings to report.

    Supports Python, TypeScript, JavaScript, Java.
    Uses ConfigLoader to get language-specific thresholds.

    Args:
        directory: Directory to scan.
        report: Health report to add findings to.
    """
    try:
        from sef_agents.core.config_loader import ConfigLoader
        from sef_agents.core.language_detector import LANGUAGE_EXTENSIONS
        from sef_agents.tools.complexity import analyze_file

        config_loader = ConfigLoader()

        # Collect all supported language files
        supported_files: list[Path] = []
        for extensions in LANGUAGE_EXTENSIONS.values():
            for ext in extensions:
                supported_files.extend(directory.rglob(f"*{ext}"))

        files_counted = 0
        for file_path in supported_files:
            if _should_skip_path(file_path):
                continue

            result = analyze_file(file_path, config_loader)
            if result.get("error"):
                continue

            files_counted += 1
            complexity = result.get("complexity", 0)
            loc = result.get("loc", 0)
            thresholds = result.get("thresholds", {})
            complexity_threshold = thresholds.get("complexity", 20)
            file_length_threshold = thresholds.get("file_length", 500)

            # Check file-level complexity using config thresholds
            severity = _determine_complexity_severity(complexity, complexity_threshold)

            if severity in (Severity.HIGH, Severity.CRITICAL):
                report.findings.append(
                    HealthFinding(
                        scanner="complexity",
                        file=str(file_path),
                        line=1,
                        message=f"File complexity {complexity} (threshold: {complexity_threshold}, LOC: {loc})",
                        severity=severity,
                        owner="architect",
                        rule="CC001",
                    )
                )

            # Check LOC using config threshold
            if loc > file_length_threshold:
                report.findings.append(
                    HealthFinding(
                        scanner="complexity",
                        file=str(file_path),
                        line=1,
                        message=f"File too large: {loc} lines (threshold: {file_length_threshold})",
                        severity=Severity.HIGH,
                        owner="architect",
                        rule="LOC001",
                    )
                )
        report.files_scanned = max(report.files_scanned, files_counted)
    except Exception as e:
        logger.error("scanner_error", scanner="complexity", error=str(e))
        report.scanner_errors["complexity"] = str(e)
        raise


def run_dead_code_scanner(directory: Path, report: HealthReport) -> None:
    """Run dead code scanner and add findings to report.

    Args:
        directory: Directory to scan.
        report: Health report to add findings to.
    """
    try:
        from sef_agents.tools.dead_code_scanner import DeadCodeScanner

        scanner = DeadCodeScanner(project_root=directory)
        result = scanner.scan_directory(directory)

        report.files_scanned = max(report.files_scanned, result.files_scanned)

        for item in result.unused_imports:
            report.findings.append(
                HealthFinding(
                    scanner="dead_code",
                    file=item.location,
                    line=item.line_number or 0,
                    message=item.description,
                    severity=Severity.MEDIUM,
                    owner="developer",
                    rule="DC001",
                )
            )

        for item in result.orphan_files:
            report.findings.append(
                HealthFinding(
                    scanner="dead_code",
                    file=item.location,
                    line=1,
                    message=item.description,
                    severity=Severity.MEDIUM,
                    owner="developer",
                    rule="DC002",
                )
            )
    except Exception as e:
        logger.error("scanner_error", scanner="dead_code", error=str(e))
        report.scanner_errors["dead_code"] = str(e)
        raise


def run_debt_scanner(directory: Path, report: HealthReport) -> None:
    """Run tech debt scanner and add findings to report.

    Args:
        directory: Directory to scan.
        report: Health report to add findings to.
    """
    try:
        from sef_agents.tools.debt_scanner import scan_directory

        result = scan_directory(directory)

        report.files_scanned = max(report.files_scanned, result.files_scanned)

        for item in result.items:
            # Map debt severity to health severity
            severity_map = {
                "critical": Severity.CRITICAL,
                "high": Severity.HIGH,
                "medium": Severity.MEDIUM,
                "low": Severity.LOW,
            }
            severity = severity_map.get(item.severity.lower(), Severity.MEDIUM)

            # Route based on debt type and severity
            # Code quality issues -> developer
            # Architecture/debt issues -> architect
            # Security issues -> security_owner
            owner = "developer"

            # Code quality patterns (regex-based for TS/JS/Java)
            code_quality_patterns = {
                "any_type",
                "ts_ignore",
                "console_log",
                "empty_catch",
                "missing_return_type",
                "unused_import",
                "implicit_any",
                "non_null_assertion",
                "any_in_generic",
                "missing_async_await",
                "direct_mutation",
                "var_usage",
                "loose_equality",
                "typeof_undefined",
                "with_statement",
                "eval_usage",
                "innerhtml_assignment",
                "document_write",
                "sync_xhr",
                "catch_exception",
                "catch_throwable",
                "system_out",
                "printStackTrace",
                "raw_types",
                "public_fields",
                "string_concat_loop",
                "missing_javadoc",
                "null_pointer_risk",
                "missing_override",
                "equals_without_hashcode",
                "final_mutable_collections",
                "string_concatenation",
                "magic_string",
            }

            # Security issues
            security_patterns = {"eval_usage", "innerhtml_assignment"}

            # Architecture/complexity issues
            architecture_patterns = {
                "deprecated",
                "complexity",
                "long_function",
                "high_complexity",
            }

            if item.debt_type in security_patterns:
                owner = "security_owner"
            elif item.debt_type in architecture_patterns:
                owner = "architect"
            elif item.debt_type in code_quality_patterns:
                owner = "developer"
            # Default to developer for unknown patterns

            report.findings.append(
                HealthFinding(
                    scanner="debt",
                    file=item.location,
                    line=item.line_number or 0,
                    message=item.description,
                    severity=severity,
                    owner=owner,
                    rule=f"DEBT-{item.debt_type.upper()[:3]}",
                )
            )
    except Exception as e:
        logger.error("scanner_error", scanner="debt", error=str(e))
        report.scanner_errors["debt"] = str(e)
        raise


def run_docs_scanner(directory: Path, report: HealthReport) -> None:
    """Run documentation scanner and add findings to report.

    Args:
        directory: Directory to scan.
        report: Health report to add findings to.
    """
    try:
        from sef_agents.tools.docs_scanner import scan_docs

        result = scan_docs(directory)

        report.files_scanned = max(report.files_scanned, result.files_scanned)

        for issue in result.issues:
            # Map doc severity
            severity_map = {
                "critical": Severity.CRITICAL,
                "high": Severity.HIGH,
                "medium": Severity.MEDIUM,
                "low": Severity.LOW,
            }
            severity = severity_map.get(issue.severity.lower(), Severity.MEDIUM)

            report.findings.append(
                HealthFinding(
                    scanner="docs",
                    file=issue.file_path,
                    line=issue.line,
                    message=issue.message,
                    severity=severity,
                    owner="docs_curator",
                    rule=issue.issue_type,
                )
            )
    except Exception as e:
        logger.error("scanner_error", scanner="docs", error=str(e))
        report.scanner_errors["docs"] = str(e)
        raise


def scan_health_impl(directory: str) -> HealthReport:
    """Orchestrate all scanners and build health report.

    Args:
        directory: Path to directory to scan.

    Returns:
        Aggregated health report.
    """
    dir_path = Path(directory)

    if not dir_path.exists():
        report = HealthReport(directory=directory, project_type=ProjectType.UNKNOWN)
        report.scanner_errors["init"] = f"Directory not found: {directory}"
        return report

    project_type = detect_project_type(dir_path)
    report = HealthReport(directory=directory, project_type=project_type)

    logger.info(
        "health_scan_start", directory=directory, project_type=project_type.value
    )

    # Run multi-language scanners for all project types
    run_debt_scanner(dir_path, report)
    run_docs_scanner(dir_path, report)
    run_complexity_scanner(dir_path, report)
    run_dead_code_scanner(dir_path, report)

    # Run Python-specific scanners only for Python projects
    if project_type == ProjectType.PYTHON:
        run_code_quality_scanner(dir_path, report)

    # Sort findings by severity
    severity_order = {
        Severity.CRITICAL: 0,
        Severity.HIGH: 1,
        Severity.MEDIUM: 2,
        Severity.LOW: 3,
    }
    report.findings.sort(key=lambda x: (severity_order[x.severity], x.file, x.line))

    # Deduplicate similar findings (same file:line from multiple scanners)
    report.findings = _deduplicate_findings(report.findings)

    logger.info(
        "health_scan_complete",
        files_scanned=report.files_scanned,
        total_findings=len(report.findings),
        critical=report.critical_count,
    )

    return report


def format_health_report(report: HealthReport) -> str:
    """Format health report as Markdown.

    Args:
        report: Health report to format.

    Returns:
        Markdown formatted report.
    """
    lines = [
        "# Codebase Health Report",
        "",
        f"**Generated:** {report.timestamp}",
        f"**Directory:** `{report.directory}`",
        f"**Project Type:** {report.project_type.value}",
        f"**Files Scanned:** {report.files_scanned}",
        "",
        "## Summary",
        "",
        "| Severity | Count | Status |",
        "|----------|-------|--------|",
        f"| Critical | {report.critical_count} | {'🛑 Block' if report.critical_count > 0 else '✅'} |",
        f"| High | {report.high_count} | {'⚠️ Fix soon' if report.high_count > 0 else '✅'} |",
        f"| Medium | {report.medium_count} | {'📋 Backlog' if report.medium_count > 0 else '✅'} |",
        f"| Low | {report.low_count} | {'ℹ️ Info' if report.low_count > 0 else '✅'} |",
        "",
    ]

    # Critical issues
    critical = [f for f in report.findings if f.severity == Severity.CRITICAL]
    if critical:
        lines.extend(
            [
                "## 🛑 Critical Issues (Action Required)",
                "",
                "| Scanner | File | Line | Message | Owner |",
                "|---------|------|------|---------|-------|",
            ]
        )
        for finding in critical:
            lines.append(
                f"| {finding.scanner} | `{Path(finding.file).name}` | {finding.line} | {finding.message} | {finding.owner} |"
            )
        lines.append("")

    # High priority
    high = [f for f in report.findings if f.severity == Severity.HIGH]
    if high:
        lines.extend(
            [
                "## ⚠️ High Priority",
                "",
                "| Scanner | File | Line | Message | Owner |",
                "|---------|------|------|---------|-------|",
            ]
        )
        for finding in high[:15]:
            lines.append(
                f"| {finding.scanner} | `{Path(finding.file).name}` | {finding.line} | {finding.message} | {finding.owner} |"
            )
        if len(high) > 15:
            lines.append(f"| ... | ... | ... | *({len(high) - 15} more)* | ... |")
        lines.append("")

    # By owner
    by_owner = report.by_owner()
    if by_owner:
        lines.append("## By Owner")
        lines.append("")
        for owner, findings in sorted(by_owner.items()):
            crit = sum(1 for f in findings if f.severity == Severity.CRITICAL)
            high_c = sum(1 for f in findings if f.severity == Severity.HIGH)
            lines.append(
                f"### {owner} ({len(findings)} issues: {crit} critical, {high_c} high)"
            )
            lines.append("")
            for finding in findings[:8]:
                marker = (
                    "🛑"
                    if finding.severity == Severity.CRITICAL
                    else "⚠️"
                    if finding.severity == Severity.HIGH
                    else "•"
                )
                lines.append(
                    f"- {marker} `{Path(finding.file).name}:{finding.line}` - {finding.message}"
                )
            if len(findings) > 8:
                lines.append(f"- *... and {len(findings) - 8} more*")
            lines.append("")

    # Recommended actions
    lines.extend(
        [
            "## Recommended Actions",
            "",
        ]
    )
    if report.critical_count > 0:
        lines.append("1. 🛑 **BLOCK:** Fix critical issues before any deployment")
    if "developer" in by_owner:
        lines.append(
            f"2. Activate `developer` → Fix {len(by_owner.get('developer', []))} code issues"
        )
    if "architect" in by_owner:
        lines.append(
            f"3. Activate `architect` → Review {len(by_owner.get('architect', []))} architectural issues"
        )
    if "docs_curator" in by_owner:
        lines.append(
            f"4. Activate `pr_reviewer` → Validate documentation ({len(by_owner.get('docs_curator', []))} issues)"
        )
    lines.append("")

    # Scanner errors
    if report.scanner_errors:
        lines.extend(["## Scanner Errors", ""])
        for scanner, error in report.scanner_errors.items():
            lines.append(f"- **{scanner}:** {error}")
        lines.append("")

    return "\n".join(lines)


def _log_dashboard_event(report: HealthReport) -> None:
    """Log findings to dashboard via handoff logger."""
    try:
        from sef_agents.utils import handoff_logger
        import uuid

        # Log Critical and High issues as debt
        to_log = [
            f
            for f in report.findings
            if f.severity in (Severity.CRITICAL, Severity.HIGH)
        ]
        for finding in to_log:
            debt_id = f"DEBT-{str(uuid.uuid4())[:8]}"
            handoff_logger.log_debt_found(
                debt_id=debt_id,
                location=f"{finding.file}:{finding.line}",
                debt_type=finding.rule or finding.scanner,
                severity=finding.severity.value,
                story_id=None,
            )

        # Log phase completion
        handoff_logger.log_phase_complete(
            story_id="health-check",
            phase="health_scan",
            outcome=f"Completed with {report.critical_count} critical, {report.high_count} high issues",
            next_phase="remediation" if report.critical_count > 0 else "development",
        )
    except Exception as e:
        logger.warning("failed_to_log_to_dashboard", error=str(e))
        raise


def _populate_context_graph(directory: str) -> str:
    """Populate context graph and return status string."""
    try:
        from sef_agents.tools.brownfield_populator import BrownfieldPopulator

        populator = BrownfieldPopulator(Path(directory))
        stats = populator.populate(levels=["L1", "L2"])
        logger.info(
            "context_graph_populated",
            nodes=stats["total_nodes"],
            edges=stats["total_edges"],
        )
        return (
            f"\n**Context Graph:** {stats['total_nodes']} nodes, "
            f"{stats['total_edges']} edges"
        )
    except Exception as e:
        logger.warning("context_graph_population_failed", error=str(e))
        # Don't fail the scan for graph population, just warn
        return f"\n**Context Graph:** ⚠️ Failed ({str(e)[:50]})"


def _generate_tech_debt(report: HealthReport, directory: str) -> str:
    """Generate TECH_DEBT.md and return status string."""
    try:
        debt_path = _generate_tech_debt_md(report.findings, Path(directory))
        if debt_path:
            logger.info("tech_debt_md_generated", path=str(debt_path))
            return f"\n**Tech Debt:** `{debt_path}`"
        return ""
    except Exception as e:
        logger.warning("tech_debt_generation_failed", error=str(e))
        raise


def _relocate_misplaced_artifacts(directory: str) -> str:
    """Relocate artifacts and return status string."""
    try:
        relocations = _relocate_artifacts(Path(directory))
        if relocations:
            logger.info("artifacts_relocated", count=len(relocations))
            return "\n**Relocated:** " + ", ".join(relocations)
        return ""
    except Exception as e:
        logger.warning("artifact_relocation_failed", error=str(e))
        raise


def _build_status_message(
    report: HealthReport,
    context_graph_status: str,
    tech_debt_status: str,
    relocation_status: str,
    md_path: Path,
    json_path: Path,
) -> str:
    """Build final status message."""
    status = (
        "🛑 CRITICAL"
        if report.critical_count > 0
        else "⚠️ ISSUES"
        if report.high_count > 0
        else "✅ HEALTHY"
    )

    return (
        f"{status} Health scan complete.\n\n"
        f"**Project:** {report.project_type.value} | "
        f"**Files:** {report.files_scanned}\n"
        f"**Critical:** {report.critical_count} | "
        f"**High:** {report.high_count} | "
        f"**Medium:** {report.medium_count} | "
        f"**Low:** {report.low_count}"
        f"{context_graph_status}"
        f"{tech_debt_status}"
        f"{relocation_status}\n\n"
        f"Reports:\n"
        f"- Markdown: `{md_path}`\n"
        f"- JSON: `{json_path}`"
    )


def scan_health(directory: str) -> str:
    """MCP tool: Scan codebase health across all dimensions.

    Orchestrates multiple scanners:
    - Code Quality (pe_rules)
    - Complexity
    - Dead Code
    - Tech Debt
    - Documentation

    Routes findings to appropriate agents for action.

    Args:
        directory: Path to directory to scan.

    Returns:
        Status message with summary and path to reports.
    """
    report = scan_health_impl(directory)
    md_content = format_health_report(report)
    json_content = report.to_json()

    # Log findings to dashboard
    _log_dashboard_event(report)

    # Write reports
    try:
        md_path = write_report(
            agent="platform_engineer",
            report_name="health_report",
            content=md_content,
            title="Codebase Health Report",
        )

        # Write JSON for CI
        json_path = Path(md_path).with_suffix(".json")
        json_path.write_text(json_content, encoding="utf-8")

        # Post-scan actions
        context_graph_status = _populate_context_graph(directory)
        tech_debt_status = _generate_tech_debt(report, directory)
        relocation_status = _relocate_misplaced_artifacts(directory)

        return _build_status_message(
            report,
            context_graph_status,
            tech_debt_status,
            relocation_status,
            md_path,
            json_path,
        )

    except OSError as e:
        return f"Scan complete but failed to save report: {e}\n\n{md_content}"
