"""Integration tests using the synthetic MME fixture.

Tests the full pipeline: read MME -> transform -> criteria -> protocol scoring.
"""

from pathlib import Path

import numpy as np
import pytest

from impakt.criteria import chest_deflection, clip_3ms, femur_load, hic15, nij, tibia_index
from impakt.io.mme import MMEReader
from impakt.protocol import euro_ncap, iihs, us_ncap
from impakt.transform.cfc import cfc_filter

FIXTURE_DIR = Path(__file__).parent / "fixtures" / "sample_mme"


@pytest.fixture(scope="module")
def test_data():
    """Load the synthetic MME fixture."""
    reader = MMEReader()
    assert reader.supports(FIXTURE_DIR), f"MME reader should support {FIXTURE_DIR}"
    return reader.read(FIXTURE_DIR)


class TestMMEFixtureLoading:
    def test_metadata(self, test_data):
        assert test_data.test_id == "IMPAKT_SYNTH_001"
        assert test_data.metadata.vehicle.make == "Synthetic"
        assert test_data.metadata.vehicle.model == "TestCar"
        assert test_data.metadata.dummy.dummy_type == "Hybrid III 50th Percentile Male"
        assert test_data.metadata.impact.test_type == "Full Frontal Rigid Barrier"
        assert test_data.metadata.impact.speed_kmh == 56.3

    def test_channel_count(self, test_data):
        assert len(test_data) == 26

    def test_head_channels_found(self, test_data):
        head_accel = test_data.find("11HEAD0000AC*")
        assert len(head_accel) == 3  # X, Y, Z

    def test_channel_properties(self, test_data):
        ch = test_data.get("11HEAD0000ACXA")
        assert ch.unit == "g"
        assert ch.sample_rate == 20000.0
        assert ch.n_samples == 4200
        assert ch.cfc_class == 1000

    def test_auto_grouping(self, test_data):
        groups = test_data.groups()
        # Should have groups for: head accel, head ang vel, chest accel, pelvis accel
        group_keys = list(groups.keys())
        head_groups = [k for k in group_keys if "HEAD" in k and "AC" in k]
        assert len(head_groups) == 1

    def test_channel_tree(self, test_data):
        tree = test_data.channel_tree()
        assert "Driver (1st Row Left)" in tree
        assert "Vehicle Structure" in tree


class TestTransformPipeline:
    def test_cfc_filter_on_fixture(self, test_data):
        ch = test_data.get("11HEAD0000ACXA")
        filtered = cfc_filter(ch, 600)
        assert filtered.cfc_class == 600
        assert filtered.peak <= ch.peak  # Filter reduces peak
        assert filtered.peak > 30.0  # But retains signal

    def test_resultant_from_group(self, test_data):
        group = test_data.group("HEAD0000AC")
        resultant = group.resultant()
        assert resultant.code.direction == "R"
        # Resultant should be >= any individual component
        assert resultant.peak >= test_data.get("11HEAD0000ACXA").peak


class TestInjuryCriteriaPipeline:
    def test_hic15(self, test_data):
        group = test_data.group("HEAD0000AC")
        result = hic15(group)
        assert result.criterion == "HIC15"
        assert result.value > 0
        assert result.body_region == "Head"
        # With a ~45g half-sine pulse, HIC15 should be meaningful
        assert result.value > 50
        assert result.value < 5000

    def test_3ms_clip(self, test_data):
        group = test_data.group("CHST0000AC")
        result = clip_3ms(group)
        assert result.criterion == "3ms Clip"
        assert result.value > 0
        assert result.value < 100  # Reasonable for ~38g chest pulse

    def test_nij(self, test_data):
        fz = test_data.get("11NECKUP00FOZA")
        my = test_data.get("11NECKUP00MOYA")
        result = nij(fz_channel=fz, my_channel=my, dummy=test_data.metadata.dummy)
        assert result.criterion == "Nij"
        assert result.value > 0
        assert result.value < 5.0  # Should be reasonable

    def test_chest_deflection(self, test_data):
        ch = test_data.get("11CHST0000DCXA")
        result = chest_deflection(channel=ch)
        assert result.criterion == "Chest Deflection"
        assert result.unit == "mm"
        assert 30.0 < result.value < 45.0  # ~36mm peak in fixture

    def test_femur_load(self, test_data):
        ch_left = test_data.get("11FEMRLE00FOZA")
        result = femur_load(channel=ch_left, side="left")
        assert result.criterion == "Femur Load Left"
        assert result.unit == "kN"
        assert 3.0 < result.value < 7.0  # ~4.8kN peak in fixture

    def test_tibia_index(self, test_data):
        fz = test_data.get("11TIBILEUOFOZA")
        mx = test_data.get("11TIBILEUOMOXA")
        my = test_data.get("11TIBILEUOMOYA")
        result = tibia_index(fz_channel=fz, mx_channel=mx, my_channel=my)
        assert result.value > 0


class TestProtocolScoringPipeline:
    @pytest.fixture
    def all_criteria(self, test_data):
        """Compute all criteria from the fixture data."""
        head_group = test_data.group("HEAD0000AC")
        chest_group = test_data.group("CHST0000AC")

        results = {}
        results["HIC15"] = hic15(head_group)
        results["3ms Clip"] = clip_3ms(chest_group)
        results["Nij"] = nij(
            fz_channel=test_data.get("11NECKUP00FOZA"),
            my_channel=test_data.get("11NECKUP00MOYA"),
            dummy=test_data.metadata.dummy,
        )
        results["Chest Deflection"] = chest_deflection(channel=test_data.get("11CHST0000DCXA"))
        results["Femur Load Left"] = femur_load(
            channel=test_data.get("11FEMRLE00FOZA"), side="left"
        )
        results["Femur Load Right"] = femur_load(
            channel=test_data.get("11FEMRRI00FOZA"), side="right"
        )
        results["Tibia Index"] = tibia_index(
            fz_channel=test_data.get("11TIBILEUOFOZA"),
            mx_channel=test_data.get("11TIBILEUOMOXA"),
            my_channel=test_data.get("11TIBILEUOMOYA"),
        )
        return results

    def test_euro_ncap_scoring(self, all_criteria):
        result = euro_ncap.evaluate(all_criteria)
        assert result.protocol == "Euro NCAP"
        assert result.stars is not None
        assert 0 <= result.stars <= 5
        assert len(result.region_scores) > 0
        # Check that all region scores have colors
        for rs in result.region_scores:
            assert rs.color is not None

    def test_us_ncap_scoring(self, all_criteria):
        result = us_ncap.evaluate(all_criteria)
        assert result.protocol == "US NCAP"
        assert result.stars is not None
        assert 1 <= result.stars <= 5

    def test_iihs_scoring(self, all_criteria):
        result = iihs.evaluate(all_criteria)
        assert result.protocol == "IIHS"
        assert result.overall_rating in ("GOOD", "ACCEPTABLE", "MARGINAL", "POOR")
        assert len(result.region_scores) > 0

    def test_protocol_summary_printable(self, all_criteria):
        for scorer in [euro_ncap, us_ncap, iihs]:
            result = scorer.evaluate(all_criteria)
            summary = result.summary()
            assert len(summary) > 0
            assert result.protocol in summary
