from __future__ import annotations

import importlib.util
import json
import sys
from pathlib import Path

import numpy as np
import pytest


def _module() -> object:
    path = (
        Path(__file__).resolve().parents[1]
        / "experiments"
        / "perception"
        / "prepare_e15_projection_pack.py"
    )
    spec = importlib.util.spec_from_file_location("e15_projection_pack_test", path)
    assert spec is not None and spec.loader is not None
    module = importlib.util.module_from_spec(spec)
    sys.modules[spec.name] = module
    spec.loader.exec_module(module)
    return module


def test_projection_pack_contains_calibration_only(tmp_path: Path) -> None:
    module = _module()
    calibration_sha256 = "a" * 64
    source = tmp_path / "source-pack"
    source.mkdir()
    arrays = source / "lidar-pack.npz"
    np.savez(
        arrays,
        frame_indices=np.asarray([0]),
        cloud_points_map=np.asarray([[99.0, 99.0, 99.0]]),
        intrinsic_fx_fy_cx_cy=np.asarray([1.0, 2.0, 3.0, 4.0]),
        distortion_kb4=np.asarray([0.1, 0.2, 0.3, 0.4]),
        t_camera_from_lidar=np.eye(4),
    )
    manifest = {
        "pack_id": "source-pack",
        "identity_sha256": "b" * 64,
        "identity": {
            "calibration_sha256": calibration_sha256,
            "source_id": "sensor.camera.right",
            "camera_slot": "camera_1",
        },
        "artifact": {"sha256": module._sha256(arrays)},
    }
    (source / "manifest.json").write_text(json.dumps(manifest))

    output = module.build_projection_pack(
        source,
        tmp_path / "output",
        expected_calibration_sha256=calibration_sha256,
    )
    document = module.validate_projection_pack(output, calibration_sha256)

    assert document["classification"] == "calibration-only-no-recorded-sensor-frames"
    with np.load(output / "projection.npz", allow_pickle=False) as packed:
        assert set(packed.files) == module.PACK_ARRAYS
        assert "cloud_points_map" not in packed.files
        assert "frame_indices" not in packed.files


def test_projection_pack_rejects_changed_calibration(tmp_path: Path) -> None:
    module = _module()
    source = tmp_path / "source-pack"
    source.mkdir()
    arrays = source / "lidar-pack.npz"
    np.savez(
        arrays,
        intrinsic_fx_fy_cx_cy=np.ones(4),
        distortion_kb4=np.ones(4),
        t_camera_from_lidar=np.eye(4),
    )
    (source / "manifest.json").write_text(
        json.dumps(
            {
                "identity": {
                    "calibration_sha256": "a" * 64,
                    "source_id": "sensor.camera.right",
                    "camera_slot": "camera_1",
                },
                "artifact": {"sha256": module._sha256(arrays)},
            }
        )
    )

    with pytest.raises(RuntimeError, match="calibration binding changed"):
        module.build_projection_pack(
            source,
            tmp_path / "output",
            expected_calibration_sha256="b" * 64,
        )
