#!/usr/bin/env python3
"""Configuration propagation and bounded-size contracts for bilateral v2."""

from pathlib import Path
import sys
import unittest


CODE_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(CODE_ROOT))

from experiments.executors import _bilateral_scenario_and_config  # noqa: E402
from experiments.plan import build_trial_plan, load_document  # noqa: E402
from experiments.rng import named_seed_record  # noqa: E402


CONFIG_ROOT = CODE_ROOT / "config" / "experiments"


def bilateral_trial():
    return {
        "method": {"method_id": "proposed_energy"},
        "trajectory": {
            "trajectory_id": "unit_contact",
            "family": "contact_roundtrip",
            "duration_s": 0.8,
            "contact_probe_fraction": 0.01,
        },
        "factors": {
            "map_policy": "source_stamped",
            "network_profile": {
                "forward_delay_s": 0.04,
                "return_delay_s": 0.08,
                "forward_jitter_s": 0.003,
                "return_jitter_s": 0.004,
                "forward_packet_loss": 0.01,
                "return_packet_loss": 0.02,
            },
            "haptic_profile": {
                "feedback_strength": 0.25,
                "energy_min": 0.0,
                "energy_max": 0.3,
                "energy_initial": 0.1,
            },
            # A direct factor must override the coupled profile.
            "forward_delay_s": 0.02,
            "energy_initial": 0.12,
        },
        "seeds": named_seed_record(7, {"test": "bilateral-v2"}),
    }


class BilateralCalibrationV2Test(unittest.TestCase):
    def test_haptic_and_network_factors_reach_simulation_config(self):
        scenario, config = _bilateral_scenario_and_config(bilateral_trial())
        self.assertEqual(scenario.map_policy, "source_stamped")
        self.assertEqual(config.feedback_strength, 0.25)
        self.assertEqual(config.energy_min, 0.0)
        self.assertEqual(config.energy_max, 0.3)
        self.assertEqual(config.energy_initial, 0.12)
        self.assertEqual(config.forward_delay_s, 0.02)
        self.assertEqual(config.feedback_delay_s, 0.08)
        self.assertEqual(config.forward_jitter_s, 0.003)
        self.assertEqual(config.return_jitter_s, 0.004)
        self.assertEqual(config.forward_packet_loss, 0.01)
        self.assertEqual(config.return_packet_loss, 0.02)

    def test_v2_grids_are_directional_and_bounded(self):
        energy = build_trial_plan(
            load_document(CONFIG_ROOT / "bilateral_calibration_v2_energy.json")
        )
        network = build_trial_plan(
            load_document(CONFIG_ROOT / "bilateral_calibration_v2_network.json")
        )
        self.assertEqual(energy["pair_count"], 36)
        self.assertEqual(energy["trial_count"], 36)
        self.assertEqual(network["pair_count"], 12)
        self.assertEqual(network["trial_count"], 36)

        profiles = {
            trial["factors"]["network_profile"]["profile_id"]
            for trial in network["trials"]
        }
        self.assertEqual(
            profiles,
            {
                "nominal",
                "return_delay_80ms",
                "forward_delay_40ms",
                "asymmetric_delay",
                "asymmetric_delay_plus_jitter",
                "asymmetric_delay_plus_loss",
            },
        )


if __name__ == "__main__":
    unittest.main()
