"""Tests des formats MQTT."""

import json
import pytest
from typing import Dict, Any


def validate_json_schema(
    data: Dict[str, Any], required_keys: list, optional_keys: list = None
) -> bool:
    """Valide schéma JSON simple."""
    optional_keys = optional_keys or []

    # Vérifier clés requises
    for key in required_keys:
        if key not in data:
            return False

    # Vérifier pas de clés inconnues
    allowed_keys = set(required_keys + optional_keys)
    for key in data.keys():
        if key not in allowed_keys:
            return False

    return True


def test_vision_targets_format():
    """Test format vision/targets."""
    targets_data = {
        "ts_ms": 1234567890,
        "targets": [
            {
                "id": 1,
                "conf": 0.92,
                "bbox": {"cx": 0.52, "cy": 0.41, "w": 0.22, "h": 0.28},
                "yaw": -0.18,
                "pitch": 0.05,
                "roll": 0.02,
            }
        ],
    }

    # Validation structure
    assert validate_json_schema(targets_data, ["ts_ms", "targets"])
    assert isinstance(targets_data["targets"], list)

    target = targets_data["targets"][0]
    required_target_keys = ["id", "conf", "bbox"]
    optional_target_keys = ["yaw", "pitch", "roll"]
    assert validate_json_schema(target, required_target_keys, optional_target_keys)

    # Validation ranges
    bbox = target["bbox"]
    assert validate_json_schema(bbox, ["cx", "cy", "w", "h"])

    assert 0.0 <= bbox["cx"] <= 1.0
    assert 0.0 <= bbox["cy"] <= 1.0
    assert 0.0 <= bbox["w"] <= 1.0
    assert 0.0 <= bbox["h"] <= 1.0

    assert 0.0 <= target["conf"] <= 1.0
    assert isinstance(target["id"], int)

    # JSON sérialisable
    json_str = json.dumps(targets_data)
    assert len(json_str) > 0


def test_vision_pose_format():
    """Test format vision/pose."""
    pose_data = {
        "ts_ms": 1234567890,
        "yaw": -0.18,
        "pitch": 0.05,
        "conf": 0.92,
        "target_id": 1,
    }

    # Validation structure
    required_keys = ["ts_ms", "yaw", "pitch", "conf", "target_id"]
    assert validate_json_schema(pose_data, required_keys)

    # Types
    assert isinstance(pose_data["ts_ms"], int)
    assert isinstance(pose_data["target_id"], int)
    assert 0.0 <= pose_data["conf"] <= 1.0

    # JSON sérialisable
    json_str = json.dumps(pose_data)
    assert len(json_str) > 0


def test_vision_motion_format():
    """Test format vision/motion."""
    motion_data = {
        "ts_ms": 1234567890,
        "active": True,
        "score": 0.67,
        "reason": "face_motion",
    }

    # Validation structure
    required_keys = ["ts_ms", "active", "score", "reason"]
    assert validate_json_schema(motion_data, required_keys)

    # Types et valeurs
    assert isinstance(motion_data["active"], bool)
    assert 0.0 <= motion_data["score"] <= 1.0
    assert motion_data["reason"] in ["face_motion", "no_face", "static"]

    # JSON sérialisable
    json_str = json.dumps(motion_data)
    assert len(json_str) > 0


def test_vision_capabilities_format():
    """Test format vision/capabilities."""
    capabilities_data = {
        "mediapipe": True,
        "fps": 15,
        "res": "320x240",
        "multi_face": True,
    }

    # Validation structure
    required_keys = ["mediapipe", "fps", "res", "multi_face"]
    assert validate_json_schema(capabilities_data, required_keys)

    # Types
    assert isinstance(capabilities_data["mediapipe"], bool)
    assert isinstance(capabilities_data["fps"], int)
    assert isinstance(capabilities_data["res"], str)
    assert isinstance(capabilities_data["multi_face"], bool)

    # JSON sérialisable
    json_str = json.dumps(capabilities_data)
    assert len(json_str) > 0


def test_eyes_cmd_format():
    """Test format eyes/cmd."""
    eyes_cmd = {"h": 15.5, "spd": 0.8}

    # Validation structure
    required_keys = ["h", "spd"]
    assert validate_json_schema(eyes_cmd, required_keys)

    # Types et ranges
    assert isinstance(eyes_cmd["h"], (int, float))
    assert 0.0 <= eyes_cmd["spd"] <= 1.0

    # JSON sérialisable
    json_str = json.dumps(eyes_cmd)
    assert len(json_str) > 0


def test_neck_cmd_format():
    """Test format neck/cmd."""
    neck_cmd = {"pan": -25.3, "spd": 0.6, "lag_ms": 120}

    # Validation structure
    required_keys = ["pan", "spd", "lag_ms"]
    assert validate_json_schema(neck_cmd, required_keys)

    # Types et ranges
    assert isinstance(neck_cmd["pan"], (int, float))
    assert 0.0 <= neck_cmd["spd"] <= 1.0
    assert isinstance(neck_cmd["lag_ms"], int)
    assert neck_cmd["lag_ms"] >= 0

    # JSON sérialisable
    json_str = json.dumps(neck_cmd)
    assert len(json_str) > 0


def test_bbox_coordinates_validation():
    """Test validation coordonnées bbox."""
    # Cas valides
    valid_bboxes = [
        {"cx": 0.5, "cy": 0.5, "w": 0.2, "h": 0.3},
        {"cx": 0.0, "cy": 0.0, "w": 0.1, "h": 0.1},
        {"cx": 1.0, "cy": 1.0, "w": 0.0, "h": 0.0},
    ]

    for bbox in valid_bboxes:
        assert 0.0 <= bbox["cx"] <= 1.0
        assert 0.0 <= bbox["cy"] <= 1.0
        assert 0.0 <= bbox["w"] <= 1.0
        assert 0.0 <= bbox["h"] <= 1.0

    # Cas limites OK
    edge_cases = [{"cx": 0.999, "cy": 0.001, "w": 0.5, "h": 0.5}]

    for bbox in edge_cases:
        assert 0.0 <= bbox["cx"] <= 1.0
        assert 0.0 <= bbox["cy"] <= 1.0


def test_angles_ranges():
    """Test ranges des angles yaw/pitch/roll."""
    # Angles typiques en radians
    angles_tests = [
        {"yaw": 0.0, "pitch": 0.0, "roll": 0.0},
        {"yaw": -0.5, "pitch": 0.2, "roll": -0.1},
        {"yaw": 0.8, "pitch": -0.6, "roll": 0.5},
    ]

    for angles in angles_tests:
        # Les angles doivent être dans des ranges raisonnables
        assert -1.0 <= angles["yaw"] <= 1.0  # ~±57°
        assert -1.0 <= angles["pitch"] <= 1.0
        assert -1.0 <= angles["roll"] <= 1.0


def test_timestamp_format():
    """Test format timestamp."""
    import time

    # Timestamp actuel en millisecondes
    ts_ms = int(time.time() * 1000)

    # Doit être un entier > 0
    assert isinstance(ts_ms, int)
    assert ts_ms > 0

    # Doit être dans range raisonnable (après 2020)
    assert ts_ms > 1577836800000  # 2020-01-01 00:00:00 UTC
