"""US NCAP (NHTSA) scoring engine.

NHTSA 5-star system is based on probability of serious injury (AIS 3+).
Individual injury risk functions convert measured criteria values to
injury probability, which are then combined.
"""

from __future__ import annotations

import math
from typing import Any

from impakt.criteria.base import CriterionResult
from impakt.protocol.base import BodyRegionScore, ProtocolResult, Rating


def _logistic_risk(value: float, beta0: float, beta1: float) -> float:
    """Logistic injury risk function.

    P(AIS 3+) = 1 / (1 + exp(-(beta0 + beta1 * value)))
    """
    z = beta0 + beta1 * value
    # Clamp to avoid overflow
    z = max(-500, min(500, z))
    return 1.0 / (1.0 + math.exp(-z))


# Risk function coefficients (beta0, beta1) for H3 50th
# These are approximate — actual NHTSA values are published in FR notices
RISK_COEFFICIENTS: dict[str, tuple[float, float]] = {
    "HIC15": (-7.45231, 0.00690),  # Head
    "3ms Clip": (-8.7635, 0.1058),  # Chest acceleration
    "Chest Deflection": (-5.7606, 0.0861),  # Chest deflection (mm)
    "Nij": (-3.227, 2.885),  # Neck
    "Femur Load": (-5.0952, 0.4897),  # Femur (kN)
}


def _stars_from_probability(p: float) -> int:
    """Convert probability to star rating."""
    if p <= 0.10:
        return 5
    elif p <= 0.20:
        return 4
    elif p <= 0.35:
        return 3
    elif p <= 0.45:
        return 2
    else:
        return 1


class USNCAP:
    """US NCAP (NHTSA) scorer."""

    def __init__(self, version: str = "2023") -> None:
        self._version = version

    @property
    def protocol_name(self) -> str:
        return "US NCAP"

    @property
    def version(self) -> str:
        return self._version

    def evaluate(
        self,
        criteria: dict[str, CriterionResult],
    ) -> ProtocolResult:
        """Score criteria using NHTSA injury risk functions."""
        region_scores: list[BodyRegionScore] = []
        probabilities: list[float] = []

        for criterion_name, (beta0, beta1) in RISK_COEFFICIENTS.items():
            result = criteria.get(criterion_name)
            if result is None:
                for key, res in criteria.items():
                    if criterion_name.lower() in key.lower():
                        result = res
                        break

            if result is None:
                continue

            p = _logistic_risk(result.value, beta0, beta1)
            probabilities.append(p)

            # Determine rating based on individual probability
            if p <= 0.10:
                rating = Rating.GOOD
            elif p <= 0.20:
                rating = Rating.ACCEPTABLE
            elif p <= 0.35:
                rating = Rating.MARGINAL
            else:
                rating = Rating.POOR

            region_scores.append(
                BodyRegionScore(
                    region=result.body_region or criterion_name,
                    criterion=criterion_name,
                    value=result.value,
                    unit=result.unit,
                    rating=rating,
                    details={"injury_probability": p},
                )
            )

        # Combined probability (simplified — assume independence)
        # P(any injury) = 1 - product(1 - Pi)
        if probabilities:
            combined_p = 1.0 - math.prod(1.0 - p for p in probabilities)
        else:
            combined_p = 0.0

        stars = _stars_from_probability(combined_p)

        return ProtocolResult(
            protocol=self.protocol_name,
            version=self._version,
            overall_rating=f"{stars} stars",
            stars=stars,
            percentage=(1.0 - combined_p) * 100.0,
            region_scores=region_scores,
            details={
                "combined_injury_probability": combined_p,
                "individual_probabilities": {
                    rs.criterion: rs.details.get("injury_probability", 0) for rs in region_scores
                },
            },
        )


def evaluate(
    criteria: dict[str, CriterionResult],
    version: str = "2023",
) -> ProtocolResult:
    """Convenience function for US NCAP evaluation."""
    return USNCAP(version=version).evaluate(criteria)
