"""Tests for Priority 2 web UI features.

Covers: templates, per-channel overrides, corridors, channel values, math expressions.
"""

from pathlib import Path
from typing import Any

import numpy as np
import pytest

from impakt.io.mme import MMEReader
from impakt.web.state import AppState

FIXTURE_DATA = Path(__file__).parent.parent / "fixtures" / "sample_mme"
MME_DATA = Path(__file__).parent.parent / "mme_data"


class TestTemplateManagement:
    def test_save_and_apply_template(self, tmp_path):
        from impakt.template.library import TemplateLibrary

        state = AppState()
        state._template_library = TemplateLibrary(tmp_path / "templates")
        state.load_test(FIXTURE_DATA)

        # Save current state as template
        selected = [
            "IMPAKT_SYNTH_001::11HEAD0000ACXA",
            "IMPAKT_SYNTH_001::11HEAD0000ACYA",
            "IMPAKT_SYNTH_001::11HEAD0000ACZA",
        ]
        template = state.save_as_template(
            name="Test Head Analysis",
            description="Head acceleration channels",
            selected_keys=selected,
            cfc_value="1000",
            x1=0.0,
            x2=0.05,
        )

        assert template.name == "Test Head Analysis"
        assert template.default_cfc == 1000
        assert len(template.plots) == 1
        assert len(template.plots[0].channel_patterns) == 3

        # Apply the template
        resolved_keys, transforms = state.apply_template("test_head_analysis")
        assert len(resolved_keys) == 3
        assert transforms["cfc"] == "1000"
        assert state.active_template is not None
        assert state.active_template.name == "Test Head Analysis"

    def test_template_names(self, tmp_path):
        from impakt.template.library import TemplateLibrary

        state = AppState()
        state._template_library = TemplateLibrary(tmp_path / "templates")
        assert state.template_names == []

        state.load_test(FIXTURE_DATA)
        state.save_as_template(
            name="Test",
            description="",
            selected_keys=["IMPAKT_SYNTH_001::11HEAD0000ACXA"],
            cfc_value="none",
            x1=None,
            x2=None,
        )
        assert "test" in state.template_names


class TestPerChannelOverrides:
    def test_set_and_read_override(self):
        state = AppState()
        state.load_test(FIXTURE_DATA)

        key = "IMPAKT_SYNTH_001::11HEAD0000ACXA"
        state.channel_overrides[key] = {"cfc": "600"}

        assert state.channel_overrides[key]["cfc"] == "600"

    def test_override_clears(self):
        state = AppState()
        state.channel_overrides["key"] = {"cfc": "180"}
        state.channel_overrides.pop("key", None)
        assert "key" not in state.channel_overrides


class TestCorridors:
    def test_add_corridor(self):
        state = AppState()
        corridor = {
            "name": "Test Corridor",
            "time": [0.0, 0.01, 0.02, 0.03],
            "lower": [-10, -20, -20, -10],
            "upper": [10, 20, 20, 10],
            "visible": True,
        }
        state.corridors.append(corridor)
        assert len(state.corridors) == 1
        assert state.corridors[0]["name"] == "Test Corridor"


class TestChannelValues:
    def test_build_channel_values_data(self):
        from impakt.web.components.channel_values import build_channel_values_data

        state = AppState()
        state.load_test(FIXTURE_DATA)

        channels = []
        for name in ["11HEAD0000ACXA", "11HEAD0000ACYA"]:
            ch = state.get_channel("IMPAKT_SYNTH_001", name)
            if ch:
                channels.append((ch.code.short_label, ch))

        rows = build_channel_values_data(channels, hover_x=0.05, x1=0.0, x2=0.1)
        assert len(rows) == 2

        for row in rows:
            assert "ch_num" in row
            assert "iso_code" in row
            assert "description" in row
            assert "unit" in row
            assert "min" in row
            assert "min_time" in row
            assert "max" in row
            assert "max_time" in row
            assert "x1" in row
            assert "x2" in row
            assert "cursor" in row

        # Channel numbers should be sequential starting at 1
        assert rows[0]["ch_num"] == 1
        assert rows[1]["ch_num"] == 2

        # Values should be parseable as floats
        for row in rows:
            float(row["min"])
            float(row["max"])
            float(row["x1"])
            float(row["x2"])
            float(row["cursor"])

    def test_channel_values_no_hover(self):
        from impakt.web.components.channel_values import build_channel_values_data

        state = AppState()
        state.load_test(FIXTURE_DATA)

        ch = state.get_channel("IMPAKT_SYNTH_001", "11HEAD0000ACXA")
        rows = build_channel_values_data([("Head X", ch)], hover_x=None, x1=0.0, x2=0.1)
        assert len(rows) == 1
        assert rows[0]["cursor"] == ""  # No hover = empty cursor column

    @pytest.mark.skipif(not (MME_DATA / "3239").exists(), reason="Real data not available")
    def test_channel_values_real_data(self):
        from impakt.web.components.channel_values import build_channel_values_data

        state = AppState()
        state.load_test(MME_DATA / "3239")

        ch = state.get_channel("3239", "11HEAD0000H3ACXP")
        assert ch is not None

        rows = build_channel_values_data([("Head Accel X", ch)], hover_x=0.05, x1=0.0, x2=0.05)
        assert len(rows) == 1
        # Min should be significant (frontal crash head X accel)
        assert abs(float(rows[0]["min"])) > 100


class TestMathExpression:
    def test_math_expr_in_state(self):
        from impakt.transform.math_expr import math_expr

        state = AppState()
        state.load_test(FIXTURE_DATA)

        ch_x = state.get_channel("IMPAKT_SYNTH_001", "11HEAD0000ACXA")
        ch_z = state.get_channel("IMPAKT_SYNTH_001", "11HEAD0000ACZA")
        assert ch_x is not None and ch_z is not None

        result = math_expr(
            expression="sqrt(a**2 + b**2)",
            channels={"a": ch_x, "b": ch_z},
            name="head_xz_resultant",
            unit="g",
        )

        assert result.name == "head_xz_resultant"
        assert result.unit == "g"
        assert result.peak > 0
        assert len(result.data) == len(ch_x.data)

        # Store in primary test
        primary = state.primary_test
        primary.data._channels["head_xz_resultant"] = result

        # Should be retrievable
        retrieved = state.get_channel("IMPAKT_SYNTH_001", "head_xz_resultant")
        assert retrieved is not None
        assert retrieved.peak == result.peak


class TestSessionAutoSave:
    def test_save_and_load_session(self, tmp_path):
        # Create a minimal MME fixture in tmp_path
        import shutil

        test_dir = tmp_path / "test_session"
        shutil.copytree(FIXTURE_DATA, test_dir)

        state = AppState()
        state.load_test(test_dir)

        # Save session
        selected = ["IMPAKT_SYNTH_001::11HEAD0000ACXA"]
        state.save_session(selected, "600")

        # Verify .impakt directory was created
        impakt_dir = test_dir / ".impakt"
        assert impakt_dir.exists()
        assert (impakt_dir / "session.yaml").exists()

        # Load session state
        session_data = state.load_session_state()
        assert session_data is not None
        assert session_data["cfc"] == "600"
        assert session_data["selected_channels"] == selected
