"""
Unit tests: Layer 1 training profile resolver scaffold.

No database; pure template + algorithm + resolver behavior.
"""

import pytest

from data_layer.training_profile import (
    CalculationTemplate,
    DimensionSpec,
    FocusAreaMapping,
    TrainingEvaluationResult,
    resolve_for_base_profile,
    resolve_training_evaluation,
)
from data_layer.training_profile.algorithms.registry import (
    get_algorithm,
    list_algorithm_ids,
    register_algorithm,
)
from data_layer.training_profile.models import AlgorithmRunResult
from data_layer.training_profile.profiles.registry import get_training_base_profile
from data_layer.training_profile.templates.registry import get_calculation_template


class TestAlgorithmRegistry:
    def test_builtin_algorithms_registered(self):
        ids = list_algorithm_ids()
        assert "threshold_band" in ids
        assert "linear_range" in ids

    def test_get_algorithm_runs_threshold(self):
        fn = get_algorithm("threshold_band")
        r = fn(
            inputs={"avg_hr": 130.0},
            params={
                "value_key": "avg_hr",
                "bands": [
                    {"max": 120, "score": 0.2},
                    {"max": 150, "score": 0.8},
                    {"max": None, "score": 1.0},
                ],
            },
        )
        assert r.normalized_score == 0.8

    def test_duplicate_register_raises(self):
        def dummy(*, inputs, params):
            return AlgorithmRunResult(0.0, 0.0, [])

        with pytest.raises(ValueError, match="already registered"):
            register_algorithm("threshold_band", dummy)


class TestResolver:
    def test_example_template_resolves(self):
        tpl = get_calculation_template("scaffold_example_aerobic_v1")
        result = resolve_training_evaluation(
            activity_inputs={
                "avg_hr": 135.0,
                "duration_min": 45.0,
                "distance_km": 10.0,
            },
            template=tpl,
        )
        assert isinstance(result, TrainingEvaluationResult)
        assert result.template_id == "scaffold_example_aerobic_v1"
        assert result.confidence == "high"
        assert "aerobic_endurance" in result.focus_area_contributions
        assert len(result.dimension_results) == 2
        for dr in result.dimension_results:
            assert dr.missing_inputs == []

    def test_missing_required_input_skips_dimension(self):
        tpl = get_calculation_template("scaffold_example_aerobic_v1")
        result = resolve_training_evaluation(
            activity_inputs={"avg_hr": 135.0},
            template=tpl,
        )
        assert result.confidence in ("medium", "low", "insufficient")
        skipped = [d for d in result.dimension_results if d.evidence.get("skipped")]
        assert len(skipped) >= 1

    def test_base_profile_filters_dimensions(self):
        profile = get_training_base_profile("scaffold_strength_base")
        tpl = get_calculation_template(profile.default_template_id)
        result = resolve_training_evaluation(
            activity_inputs={"duration_min": 50.0},
            template=tpl,
            base_profile=profile,
        )
        assert len(result.dimension_results) == 1
        assert result.dimension_results[0].dimension_key == "effort"

    def test_resolve_for_base_profile_convenience(self):
        result = resolve_for_base_profile(
            activity_inputs={"duration_min": 40.0},
            base_profile_key="scaffold_strength_base",
            include_trace=True,
        )
        assert result.base_profile_key == "scaffold_strength_base"
        assert result.trace is not None
        assert "effort" in result.trace

    def test_to_serializable(self):
        tpl = get_calculation_template("scaffold_example_strength_v1")
        r = resolve_training_evaluation(
            activity_inputs={"duration_min": 45.0},
            template=tpl,
        )
        d = r.to_serializable()
        assert d["template_id"] == tpl.id
        assert "focus_area_contributions" in d
        assert isinstance(d["dimension_results"], list)


class TestCustomTemplate:
    def test_unknown_algorithm_raises(self):
        bad = CalculationTemplate(
            id="bad",
            version="1",
            label="bad",
            dimensions=(
                DimensionSpec(
                    key="x",
                    algorithm_id="does_not_exist",
                    inputs=("a",),
                    params={},
                    maps_to=(FocusAreaMapping("strength", 1.0),),
                ),
            ),
        )
        with pytest.raises(KeyError):
            resolve_training_evaluation(
                activity_inputs={"a": 1.0},
                template=bad,
            )
