"""Deterministic MCP red-team orchestration for mcts pentest."""

from __future__ import annotations

from mcts.core.config import ScanConfig
from mcts.core.scanner import Scanner
from mcts.fuzz.runner import FuzzRunner
from mcts.pentest.models import PentestLimits, PentestPhase, PentestReport
from mcts.reporting.models import Finding, ScanReport, Severity


def run_pentest(config: ScanConfig, *, run_fuzz: bool = True) -> PentestReport:
    """Run static recon, attack-chain review, and optional protocol fuzz."""
    phases: list[PentestPhase] = []

    static_report = Scanner(config).run()
    metadata_hits = [
        finding
        for finding in static_report.findings
        if finding.analyzer in {"prompt_injection", "metadata_integrity", "sigma_metadata", "schema_surface"}
    ]
    phases.append(
        PentestPhase(
            name="static_recon",
            status="complete",
            findings=len(static_report.findings),
            details={"tools": len(static_report.server.tools)},
        )
    )
    phases.append(
        PentestPhase(
            name="metadata_poisoning",
            status="complete",
            findings=len(metadata_hits),
            details={"analyzers": sorted({f.analyzer for f in metadata_hits})},
        )
    )

    attack_graph = static_report.attack_graph or {}
    attack_paths = list(attack_graph.get("paths") or [])
    has_tools = bool(static_report.server.tools)
    if has_tools:
        phases.append(
            PentestPhase(
                name="attack_chains",
                status="complete",
                findings=len(attack_paths),
                details={"nodes": len(attack_graph.get("nodes") or [])},
            )
        )
    else:
        phases.append(
            PentestPhase(
                name="attack_chains",
                status="skipped",
                details={
                    "reason": "No MCP tools discovered — attack graph requires a tool surface",
                },
            )
        )

    fuzz_rows: list[Finding] = []
    if run_fuzz and config.live and config.live_consent:
        fuzz_config = config.model_copy(update={"fuzz_level": "safe", "fuzz_consent": False})
        fuzz_result = FuzzRunner(fuzz_config).run()
        fuzz_rows = list(fuzz_result.findings)
        phases.append(
            PentestPhase(
                name="protocol_fuzz",
                status="complete",
                findings=len(fuzz_rows),
                details={"probes_run": fuzz_result.probes_run, "level": fuzz_result.level.value},
            )
        )
    else:
        phases.append(
            PentestPhase(
                name="protocol_fuzz",
                status="skipped",
                details={"reason": "Pass --live --i-understand-live-risk to include safe protocol fuzz"},
            )
        )

    combined = _rank_findings(static_report, fuzz_rows)
    recommendations = _recommendations(static_report, fuzz_rows, attack_paths)
    verdict = _verdict(static_report, fuzz_rows)

    limits = PentestLimits(
        tools_discovered=len(static_report.server.tools),
        attack_chains_available=has_tools,
        coverage="full" if has_tools else "static-only",
    )
    return PentestReport(
        target=str(config.target),
        verdict=verdict,
        score=static_report.score.overall,
        absolute_risk=static_report.score_v2.absolute_risk if static_report.score_v2 else None,
        phases=phases,
        attack_paths=attack_paths[:20],
        top_findings=[row.model_dump(mode="json") for row in combined[:15]],
        fuzz_findings=[row.model_dump(mode="json") for row in fuzz_rows],
        recommendations=recommendations,
        static_report=static_report.model_dump(mode="json"),
        pentest_limits=limits,
    )


def _rank_findings(static_report: ScanReport, fuzz_rows: list[Finding]) -> list[Finding]:
    order = {
        Severity.CRITICAL: 0,
        Severity.HIGH: 1,
        Severity.MEDIUM: 2,
        Severity.LOW: 3,
    }
    combined = list(static_report.findings) + fuzz_rows
    return sorted(combined, key=lambda row: (order.get(row.severity, 99), row.title))


def _verdict(static_report: ScanReport, fuzz_rows: list[Finding]) -> str:
    if static_report.score_v2 is not None:
        if any(f.severity == Severity.CRITICAL for f in fuzz_rows):
            return "critical"
        return static_report.score_v2.risk_level
    if static_report.summary.critical:
        return "critical"
    if static_report.summary.high or any(f.severity == Severity.HIGH for f in fuzz_rows):
        return "high"
    if static_report.summary.medium or static_report.findings or fuzz_rows:
        return "medium"
    return "pass"


def _recommendations(
    static_report: ScanReport,
    fuzz_rows: list[Finding],
    attack_paths: list[dict],
) -> list[str]:
    recs: list[str] = []
    if attack_paths:
        recs.append("Break or gate attack-chain paths between high-risk tool capabilities.")
    if static_report.summary.critical or static_report.summary.high:
        recs.append("Remediate critical/high findings before exposing this server to agents.")
    if any(f.analyzer == "prompt_injection" for f in static_report.findings):
        recs.append("Sanitize tool descriptions and remove instruction-like content from metadata.")
    if fuzz_rows:
        recs.append("Harden protocol handling — fuzz probes produced suspicious responses.")
    if static_report.score.overall < 70:
        recs.append("Re-run `mcts scan` after fixes and compare scores with `mcts-mcp compare_baselines`.")
    if not recs:
        recs.append("No high-priority remediation items detected by the default pentest phases.")
    return recs
