"""Tests against real ISO 13499 MME data.

These tests validate the reader against actual crash test data from
multiple labs (NHTSA/Calspan, BASt, UTAC, Volkswagen).
"""

from pathlib import Path

import numpy as np
import pytest

from impakt.channel.code import ChannelCode
from impakt.io.mme import MMEReader

MME_DATA_DIR = Path(__file__).parent / "mme_data"

# Skip all tests if real data directory is missing
pytestmark = pytest.mark.skipif(
    not MME_DATA_DIR.exists(),
    reason="Real MME test data not available",
)


class TestNHTSA3239:
    """NHTSA test 3239: 2000 Volkswagen Passat frontal barrier, 133 channels."""

    TEST_DIR = MME_DATA_DIR / "3239"

    def test_supports(self):
        reader = MMEReader()
        assert reader.supports(self.TEST_DIR)

    def test_metadata(self):
        reader = MMEReader()
        meta = reader.metadata(self.TEST_DIR)
        assert meta.test_number == "3239"
        assert "CALSPAN" in meta.test_facility.upper()
        assert meta.test_date is not None
        assert meta.test_date.year == 1999

    def test_read_all_channels(self):
        reader = MMEReader()
        data = reader.read(self.TEST_DIR)
        assert data.test_id == "3239"
        assert len(data) == 133

    def test_channel_code_parsing(self):
        reader = MMEReader()
        data = reader.read(self.TEST_DIR)
        # First channel: 11HEAD0000H3ACXP
        ch = data.get("11HEAD0000H3ACXP")
        assert ch.code.is_valid
        assert ch.code.test_object == "11"
        assert ch.code.main_location == "HEAD"
        assert ch.code.fine_location == "0000"
        assert ch.code.dummy_type == "H3"
        assert ch.code.measurement == "AC"
        assert ch.code.direction == "X"
        assert ch.code.sense == "P"

    def test_channel_data_properties(self):
        reader = MMEReader()
        data = reader.read(self.TEST_DIR)
        ch = data.get("11HEAD0000H3ACXP")
        assert ch.unit == "m/s²"
        assert ch.sample_rate == 10000.0
        assert ch.n_samples == 3312
        assert len(ch.time) == 3312
        # First sample time should be negative (pre-trigger)
        assert ch.time[0] < 0

    def test_channel_data_realistic(self):
        reader = MMEReader()
        data = reader.read(self.TEST_DIR)
        ch = data.get("11HEAD0000H3ACXP")
        # Head X accel in a frontal crash should have a significant negative peak
        # (m/s² units, so a 40g crash = ~400 m/s²)
        assert ch.peak > 100.0  # At least 10g peak

    def test_auto_grouping(self):
        reader = MMEReader()
        data = reader.read(self.TEST_DIR)
        groups = data.groups()
        # Should have head accel group for driver (11) and rear passenger (13)
        head_groups = [k for k in groups if "HEAD" in k and "AC" in k]
        assert len(head_groups) >= 2  # 11HEAD and 13HEAD at minimum

    def test_find_driver_head_channels(self):
        reader = MMEReader()
        data = reader.read(self.TEST_DIR)
        head_channels = data.find("11HEAD0000H3AC*")
        assert len(head_channels) == 3  # X, Y, Z

    def test_resultant_computation(self):
        reader = MMEReader()
        data = reader.read(self.TEST_DIR)
        head_channels = data.find("11HEAD0000H3AC*")
        assert len(head_channels) == 3
        # Compute resultant
        from impakt.transform.resultant import resultant_from_channels

        res = resultant_from_channels(*head_channels)
        assert res.code.direction == "R"
        assert res.peak > 0

    def test_barrier_load_cells(self):
        reader = MMEReader()
        data = reader.read(self.TEST_DIR)
        barrier_channels = data.find("B0FBAR*")
        assert len(barrier_channels) > 0  # Should have load cell barrier data

    def test_cfc_filter_on_real_data(self):
        from impakt.transform.cfc import cfc_filter

        reader = MMEReader()
        data = reader.read(self.TEST_DIR)
        ch = data.get("11HEAD0000H3ACXP")
        filtered = cfc_filter(ch, 1000)
        assert filtered.cfc_class == 1000
        assert filtered.peak <= ch.peak


class TestBASt_AK3T02FO:
    """BASt test AK3T02FO: Frontal offset 40% deformable barrier, 97 channels."""

    TEST_DIR = MME_DATA_DIR / "AK3T02FO"

    def test_supports(self):
        reader = MMEReader()
        assert reader.supports(self.TEST_DIR)

    def test_metadata(self):
        reader = MMEReader()
        meta = reader.metadata(self.TEST_DIR)
        assert meta.test_number == "AK3T02FO"
        assert "BASt" in meta.test_facility

    def test_read_all_channels(self):
        reader = MMEReader()
        data = reader.read(self.TEST_DIR)
        assert data.test_id == "AK3T02FO"
        assert len(data) == 97

    def test_multiple_occupants(self):
        """This test has driver (11), rear passenger (13), and child (14)."""
        reader = MMEReader()
        data = reader.read(self.TEST_DIR)
        driver_chs = data.find("11*")
        rear_chs = data.find("13*")
        assert len(driver_chs) > 0
        assert len(rear_chs) > 0


class TestBASt_AK3T02SI:
    """BASt test AK3T02SI: Side impact, 97 channels."""

    TEST_DIR = MME_DATA_DIR / "AK3T02SI"

    def test_supports(self):
        reader = MMEReader()
        assert reader.supports(self.TEST_DIR)

    def test_read_all_channels(self):
        reader = MMEReader()
        data = reader.read(self.TEST_DIR)
        assert len(data) > 0


class TestVW_Pedestrian:
    """Volkswagen test VW1FGS15: Pedestrian headform impactor, 10 channels."""

    TEST_DIR = MME_DATA_DIR / "VW1FGS15"

    def test_supports(self):
        reader = MMEReader()
        assert reader.supports(self.TEST_DIR)

    def test_metadata(self):
        reader = MMEReader()
        meta = reader.metadata(self.TEST_DIR)
        assert meta.test_date is not None
        assert meta.test_date.year == 2006

    def test_read_all_channels(self):
        reader = MMEReader()
        data = reader.read(self.TEST_DIR)
        assert len(data) == 10

    def test_pedestrian_channel_codes(self):
        """This test uses D0 (impactor) test object prefix."""
        reader = MMEReader()
        data = reader.read(self.TEST_DIR)
        # All channels should start with D0
        for ch in data:
            assert ch.code.test_object == "D0"

    def test_has_resultant_channel(self):
        """This test includes a pre-computed resultant (direction=R)."""
        reader = MMEReader()
        data = reader.read(self.TEST_DIR)
        resultants = [ch for ch in data if ch.code.direction == "R"]
        assert len(resultants) >= 1


class TestUTAC_98_7707:
    """UTAC test 98/7707: Vehicle-to-vehicle frontal, no channel data."""

    TEST_DIR = MME_DATA_DIR / "98_7707"

    def test_supports(self):
        """Has .mme file so should be supported even without channels."""
        reader = MMEReader()
        assert reader.supports(self.TEST_DIR)

    def test_metadata(self):
        reader = MMEReader()
        meta = reader.metadata(self.TEST_DIR)
        assert meta.test_number == "M5533" or meta.test_number == "98/7707"
        assert meta.test_date is not None
        assert meta.test_date.year == 1998

    def test_read_empty_channels(self):
        """This test has no Channel/ directory — should load with 0 channels."""
        reader = MMEReader()
        data = reader.read(self.TEST_DIR)
        assert len(data) == 0


class TestChannelCodeRealWorld:
    """Test ChannelCode parsing against all codes found in real data."""

    @pytest.mark.parametrize(
        "code,expected_meas,expected_dir",
        [
            ("11HEAD0000H3ACXP", "AC", "X"),
            ("11HEAD0000H3ACYP", "AC", "Y"),
            ("11HEAD0000H3ACZP", "AC", "Z"),
            ("11NECKUP00H3FOXP", "FO", "X"),
            ("11NECKUP00H3FOZP", "FO", "Z"),
            ("11NECKUP00H3MOYP", "MO", "Y"),
            ("11CHST0000H3DSXP", "DS", "X"),
            ("11FEMRLE00H3FOZP", "FO", "Z"),
            ("11TIBILEUPH3MOXP", "MO", "X"),
            ("B0FBAR010100FOXP", "FO", "X"),
            ("D0HEAD0000PCACXA", "AC", "X"),
            ("D0HEAD0000PCACRA", "AC", "R"),
            ("D0HEAD0000PCDSXV", "DS", "X"),
            ("D0HEAD0000PCANXV", "AN", "X"),
            ("14HEAD0000P3ACXP", "AC", "X"),
        ],
    )
    def test_parse_real_codes(self, code, expected_meas, expected_dir):
        parsed = ChannelCode.parse(code)
        assert parsed.is_valid, f"Failed to parse: {code}"
        assert parsed.measurement == expected_meas, (
            f"{code}: expected meas={expected_meas}, got {parsed.measurement}"
        )
        assert parsed.direction == expected_dir, (
            f"{code}: expected dir={expected_dir}, got {parsed.direction}"
        )

    @pytest.mark.parametrize(
        "code,expected_dummy",
        [
            ("11HEAD0000H3ACXP", "H3"),
            ("14HEAD0000P3ACXP", "P3"),
            ("D0HEAD0000PCACXA", "PC"),
        ],
    )
    def test_dummy_type_extraction(self, code, expected_dummy):
        parsed = ChannelCode.parse(code)
        assert parsed.dummy_type == expected_dummy

    def test_group_key_consistency(self):
        """X/Y/Z channels with same prefix should share a group key."""
        cx = ChannelCode.parse("11HEAD0000H3ACXP")
        cy = ChannelCode.parse("11HEAD0000H3ACYP")
        cz = ChannelCode.parse("11HEAD0000H3ACZP")
        assert cx.group_key() == cy.group_key() == cz.group_key()

    def test_group_key_separates_locations(self):
        head = ChannelCode.parse("11HEAD0000H3ACXP")
        chest = ChannelCode.parse("11CHST0000H3ACXP")
        assert head.group_key() != chest.group_key()

    def test_group_key_separates_occupants(self):
        driver = ChannelCode.parse("11HEAD0000H3ACXP")
        rear = ChannelCode.parse("13HEAD0000H3ACXP")
        assert driver.group_key() != rear.group_key()
