"""Coverage score and gap detection engine."""

from __future__ import annotations

from sqlalchemy.orm import Session

from app.models.event import EventPhase
from app.schemas.coverage import CoverageSummaryRead
from app.services.coverage import sync_coverage_from_events
from app.services.engagements import get_engagement_by_id

PHASE_RECOMMENDATIONS: dict[str, str] = {
    EventPhase.RECON.value: "Consider expanding reconnaissance to map attack surface",
    EventPhase.ENUMERATION.value: "Consider deeper service enumeration on discovered assets",
    EventPhase.INITIAL_ACCESS.value: "Consider testing authentication mechanisms",
    EventPhase.PRIV_ESC.value: "Privilege escalation vectors not explored",
    EventPhase.POST_EXPLOITATION.value: "Consider post-exploitation validation and lateral movement paths",
    EventPhase.CLEANUP.value: "Consider validating cleanup and artifact handling steps",
}


def coverage_score(phase_status: dict[str, str]) -> int:
    """Calculate percentage of completed phases."""
    total_phases = len(phase_status)
    if total_phases == 0:
        return 0

    done_phases = sum(1 for status in phase_status.values() if status == "done")
    return int(round((done_phases / total_phases) * 100))


def build_recommendations(gaps: list[str]) -> list[str]:
    """Return hardcoded recommendations for uncovered phases."""
    recommendations: list[str] = []
    for phase in gaps:
        recommendation = PHASE_RECOMMENDATIONS.get(
            phase,
            f"Consider covering pending phase: {phase}",
        )
        recommendations.append(recommendation)
    return recommendations


def get_coverage(db: Session, engagement_id: int) -> CoverageSummaryRead:
    """Compute coverage status, score, gaps, and recommendations."""
    if get_engagement_by_id(db, engagement_id) is None:
        raise LookupError("engagement not found")

    coverage = sync_coverage_from_events(db, engagement_id)
    db.commit()
    db.refresh(coverage)

    phases = {
        phase.value: (coverage.phase_status or {}).get(phase.value, "not_started")
        for phase in EventPhase
    }
    gaps = [phase for phase, status in phases.items() if status == "not_started"]

    return CoverageSummaryRead(
        engagement_id=engagement_id,
        coverage_score=coverage_score(phases),
        phases=phases,
        gaps=gaps,
        recommendations=build_recommendations(gaps),
    )
