"""Control matching engine: map resources to framework controls."""

from __future__ import annotations

from typing import TYPE_CHECKING, Any

from pydantic import BaseModel, ConfigDict

from complyform.core.assessor.check_evaluator import CheckEvaluator

if TYPE_CHECKING:
    from complyform.core.profiles.models import (
        CrossMappingIndex,
        MappingFile,
    )
    from complyform.core.scanner.state_parser import ResourceRecord


class Finding(BaseModel):
    """Single compliance finding."""

    model_config = ConfigDict(strict=True, frozen=True)

    control_id: str
    framework: str
    resource_address: str
    resource_type: str
    status: str  # PASS | FAIL | MANUAL_REVIEW | NOT_ASSESSED
    severity: str  # CRITICAL | HIGH | MEDIUM | LOW
    finding: str
    remediation: str
    checkov_id: str | None
    cross_mappings: list[dict[str, Any]] | None
    native_ids: dict[str, Any] | None
    confidence: str  # verified | high | inferred
    not_assessed_reason: str | None = None


def _get_cross_mappings(
    framework: str,
    control_id: str,
    index: CrossMappingIndex | None,
) -> list[dict[str, str]] | None:
    if index is None:
        return None
    results = []
    for m in index.mappings:
        if m.source_framework == framework and m.source_control == control_id:
            results.append(
                {
                    "framework": m.target_framework,
                    "control_id": m.target_control,
                }
            )
    return results or None


class ControlMatcher:
    """Match resources against framework mapping definitions."""

    def __init__(self) -> None:
        self._evaluator = CheckEvaluator()

    def match(
        self,
        resources: list[ResourceRecord],
        mapping: MappingFile,
        cross_mapping_index: CrossMappingIndex | None = None,
    ) -> list[Finding]:
        """Evaluate all applicable checks from mapping against resources."""
        findings: list[Finding] = []
        framework = mapping.framework_id

        type_to_mappings: dict[str, list[Any]] = {}
        for rm in mapping.mappings:
            for rt in rm.resource_types:
                type_to_mappings.setdefault(rt, []).append(rm)

        for resource in resources:
            applicable = type_to_mappings.get(resource.resource_type, [])
            for rm in applicable:
                cross = _get_cross_mappings(
                    framework, rm.control_id, cross_mapping_index
                )
                native = {"native_id": rm.native_id} if rm.native_id else None

                if not rm.checks:
                    findings.append(
                        Finding(
                            control_id=rm.control_id,
                            framework=framework,
                            resource_address=resource.address,
                            resource_type=resource.resource_type,
                            status="MANUAL_REVIEW",
                            severity="MEDIUM",
                            finding=(
                                f"Control {rm.control_id} requires manual"
                                f" review for {resource.resource_type}"
                            ),
                            remediation=f"Manually verify {rm.control_id} compliance",
                            checkov_id=rm.check_id,
                            cross_mappings=cross,
                            native_ids=native,
                            confidence=rm.confidence,
                        )
                    )
                    continue

                for check in rm.checks:
                    result = self._evaluator.evaluate(check, resource.attributes)
                    status = "PASS" if result.passed else "FAIL"
                    severity = "MEDIUM"
                    if not result.passed:
                        severity = "HIGH"

                    findings.append(
                        Finding(
                            control_id=rm.control_id,
                            framework=framework,
                            resource_address=resource.address,
                            resource_type=resource.resource_type,
                            status=status,
                            severity=severity,
                            finding=result.message
                            if not result.passed
                            else f"Control {rm.control_id} passed",
                            remediation=check.failure_message
                            if not result.passed
                            else "",
                            checkov_id=rm.check_id,
                            cross_mappings=cross,
                            native_ids=native,
                            confidence=rm.confidence,
                        )
                    )

        return findings
