from __future__ import annotations

from pathlib import Path

import pytest

from k1link.device_plugins.xgrids_k1.calibration_schema import (
    FactoryCalibrationSchemaError,
    parse_k1_factory_calibration,
)

FIXTURE_ROOT = Path(__file__).parent / "fixtures" / "k1" / "calibration"


def _documents() -> tuple[bytes, bytes]:
    return (
        (FIXTURE_ROOT / "camera.yaml").read_bytes(),
        (FIXTURE_ROOT / "extrinsic_camera_lidar.yaml").read_bytes(),
    )


def test_factory_calibration_normalizes_main_camera_mapping_and_transforms() -> None:
    calibration = parse_k1_factory_calibration(*_documents())
    profile = calibration.normalized_profile()
    streams = profile["stream_bindings"]
    assert isinstance(streams, dict)

    left = streams["sensor.camera.left"]
    right = streams["sensor.camera.right"]
    assert isinstance(left, dict)
    assert isinstance(right, dict)
    assert left["calibration_slot"] == "camera_0"
    assert right["calibration_slot"] == "camera_1"
    assert left["rtsp_path"] == "/live/chn_left_main"
    assert right["rtsp_path"] == "/live/chn_right_main"
    assert left["native_resolution"] == [4000, 3000]
    assert left["admitted_resolution"] == [800, 600]
    assert left["admitted_intrinsic_fx_fy_cx_cy"] == pytest.approx(
        [193.6, 193.6, 400.0, 300.0]
    )
    assert right["admitted_intrinsic_fx_fy_cx_cy"] == pytest.approx(
        [194.0, 194.2, 396.0, 302.0]
    )
    assert profile["transform_notation"] == "T_destination_from_source"

    right_transform = calibration.t_camera_from_lidar("camera_1")
    assert right_transform[0] == pytest.approx((-1.0, 0.0, 0.0, 0.007))
    assert right_transform[1] == pytest.approx((0.0, 0.0, -1.0, -0.0948))
    assert right_transform[2] == pytest.approx((0.0, -1.0, 0.0, -0.0328))


@pytest.mark.parametrize(
    ("mutation", "message"),
    [
        (
            lambda data: data.replace(
                b"calibrated: true\n",
                b"calibrated: true\ncalibrated: true\n",
                1,
            ),
            "safe valid YAML",
        ),
        (
            lambda data: data.replace(
                b"camera_pose: [1, 0, 0, 0,",
                b"camera_pose: &pose [1, 0, 0, 0,",
                1,
            ),
            "anchors",
        ),
        (
            lambda data: data.replace(b"intrinsic: [968,", b"intrinsic: [.nan,", 1),
            "finite",
        ),
        (
            lambda data: data.replace(b"image_width: 4000", b"image_width: 3999", 1),
            "resolution",
        ),
        (
            lambda data: data.replace(
                b"camera_model: kb4",
                b"camera_model: kb4\n  unexpected: true",
                1,
            ),
            "unexpected key set",
        ),
    ],
)
def test_factory_calibration_rejects_unsafe_or_nonprofile_camera_yaml(
    mutation: object,
    message: str,
) -> None:
    camera, extrinsic = _documents()
    mutate = mutation
    assert callable(mutate)
    with pytest.raises(FactoryCalibrationSchemaError, match=message):
        parse_k1_factory_calibration(mutate(camera), extrinsic)


def test_factory_calibration_rejects_version_or_rigid_transform_mismatch() -> None:
    camera, extrinsic = _documents()
    mismatched = extrinsic.replace(b"V2.2.0_alpha", b"V2.2.1_alpha")
    with pytest.raises(FactoryCalibrationSchemaError, match="versions do not match"):
        parse_k1_factory_calibration(camera, mismatched)

    invalid_rotation = extrinsic.replace(
        b"transform: [1, 0, 0,",
        b"transform: [2, 0, 0,",
    )
    with pytest.raises(FactoryCalibrationSchemaError, match="not orthonormal"):
        parse_k1_factory_calibration(camera, invalid_rotation)
