"""Cross-validation: match ComplyForm findings against native CSPM findings.

Contract:
- match_findings() correlates ComplyForm assessor output with cloud-native
  security findings (SCC, Security Hub, Defender).
- CrossValidationResult extends CloudIntelligenceResult with confirmed,
  complyform_only, native_only categorization and a 0-100 score.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import TYPE_CHECKING

from complyform.core.cloud_intelligence import CloudIntelligenceResult

if TYPE_CHECKING:
    from collections.abc import Callable

    from complyform.core.assessor.control_matcher import Finding

__all__ = [
    "CrossValidationMatch",
    "CrossValidationResult",
    "NativeFinding",
    "format_native_only_gaps",
    "match_findings",
]


@dataclass
class NativeFinding:
    """Single finding from a cloud-native CSPM tool."""

    native_id: str
    resource_id: str
    resource_type: str
    severity: str  # "CRITICAL" | "HIGH" | "MEDIUM" | "LOW"
    title: str
    status: str


@dataclass
class CrossValidationMatch:
    """A confirmed match between ComplyForm and native findings."""

    complyform_finding: Finding
    native_finding: NativeFinding
    match_type: str  # "resource_match" | "control_match" | "both"


@dataclass
class CrossValidationResult(CloudIntelligenceResult):
    """Result of cross-validating ComplyForm findings against native CSPM."""

    total_complyform_findings: int = 0
    total_native_findings: int = 0
    confirmed: list[CrossValidationMatch] = field(default_factory=list)
    complyform_only: list[Finding] = field(default_factory=list)
    native_only: list[NativeFinding] = field(default_factory=list)
    cross_validation_score: int = 0  # 0-100
    native_tool: str = ""  # "scc" | "security_hub" | "defender"


def match_findings(
    complyform_findings: list[Finding],
    native_findings: list[NativeFinding],
    normalize_fn: Callable[[str, str], str | None] | None = None,
) -> CrossValidationResult:
    """Match ComplyForm findings against native CSPM findings.

    For each pair, checks resource match (normalized resource_id == resource_address)
    and control match (native_id in ComplyForm native_ids dict values).
    """

    confirmed: list[CrossValidationMatch] = []
    matched_cf_indices: set[int] = set()
    matched_native_indices: set[int] = set()

    # Pre-normalize native resource IDs
    normalized_native: list[str | None] = []
    for nf in native_findings:
        if normalize_fn is not None:
            normalized_native.append(normalize_fn(nf.resource_id, nf.resource_type))
        else:
            normalized_native.append(None)

    for ci, cf in enumerate(complyform_findings):
        for ni, nf in enumerate(native_findings):
            # Resource match
            norm_id = normalized_native[ni]
            resource_match = norm_id is not None and norm_id == cf.resource_address

            # Control match: native_id found in ComplyForm native_ids values
            control_match = False
            if cf.native_ids:
                native_id_values: set[str] = set()
                for v in cf.native_ids.values():
                    if isinstance(v, str):
                        native_id_values.add(v)
                    elif isinstance(v, list):
                        native_id_values.update(str(item) for item in v)
                control_match = nf.native_id in native_id_values

            if resource_match and control_match:
                match_type = "both"
            elif resource_match:
                match_type = "resource_match"
            elif control_match:
                match_type = "control_match"
            else:
                continue

            confirmed.append(
                CrossValidationMatch(
                    complyform_finding=cf,
                    native_finding=nf,
                    match_type=match_type,
                )
            )
            matched_cf_indices.add(ci)
            matched_native_indices.add(ni)

    complyform_only = [
        cf for i, cf in enumerate(complyform_findings) if i not in matched_cf_indices
    ]
    native_only = [
        nf for i, nf in enumerate(native_findings) if i not in matched_native_indices
    ]

    total_cf = len(complyform_findings)
    total_native = len(native_findings)
    denominator = max(total_cf, total_native)
    score = int(len(confirmed) / denominator * 100) if denominator > 0 else 0

    return CrossValidationResult(
        feature="cross_validation",
        cloud="",
        success=True,
        error=None,
        duration_ms=0,
        timestamp="",
        total_complyform_findings=total_cf,
        total_native_findings=total_native,
        confirmed=confirmed,
        complyform_only=complyform_only,
        native_only=native_only,
        cross_validation_score=score,
    )


def format_native_only_gaps(native_only: list[NativeFinding], cloud: str) -> str:
    """Format native-only findings as profile gap warnings."""
    lines: list[str] = []
    for nf in native_only:
        lines.append(
            f"Profile Gap Detected: {cloud.upper()} finding {nf.native_id}"
            f" has no ComplyForm mapping"
        )
        lines.append(f"  Resource type: {nf.resource_type}")
        lines.append(
            f"  Action: Add check to profiles/mappings/{cloud}/{{framework}}.yaml"
        )
        lines.append("")
    return "\n".join(lines)
