"""
linear_range: map a value linearly from [min_value, max_value] to [0, 1], clamped.

params:
  value_key: str
  min_value: float
  max_value: float
  invert: bool (optional) — if True, high values map to 0

Missing value → score 0 and missing_inputs.
"""

from __future__ import annotations

from typing import Any, Mapping

from data_layer.training_profile.models import AlgorithmRunResult
from data_layer.utils import safe_float


ALGORITHM_ID = "linear_range"


def linear_range_score(
    *,
    inputs: Mapping[str, Any],
    params: Mapping[str, Any],
) -> AlgorithmRunResult:
    value_key = str(params.get("value_key", ""))
    if not value_key:
        return AlgorithmRunResult(
            raw_score=0.0,
            normalized_score=0.0,
            missing_inputs=["__param_value_key__"],
            detail={"error": "params.value_key required"},
        )

    if value_key not in inputs or inputs[value_key] is None:
        return AlgorithmRunResult(
            raw_score=0.0,
            normalized_score=0.0,
            missing_inputs=[value_key],
            detail={"value_key": value_key},
        )

    raw = safe_float(inputs[value_key])
    lo = safe_float(params.get("min_value"))
    hi = safe_float(params.get("max_value"))
    invert = bool(params.get("invert", False))

    if hi <= lo:
        return AlgorithmRunResult(
            raw_score=raw,
            normalized_score=0.0,
            missing_inputs=[],
            detail={"error": "max_value must be > min_value", "min": lo, "max": hi},
        )

    t = (raw - lo) / (hi - lo)
    t = max(0.0, min(1.0, t))
    if invert:
        t = 1.0 - t

    return AlgorithmRunResult(
        raw_score=raw,
        normalized_score=t,
        missing_inputs=[],
        detail={"value_key": value_key, "min": lo, "max": hi, "invert": invert},
    )
