import os
from pathlib import Path

import numpy as np
import pinocchio as pin
import pytest

from rm75_ik import (
    DualArmAssembly,
    RM75Kinematics,
    RealManFkReference,
    pose_errors,
)


@pytest.fixture(scope="module")
def reference():
    sdk_root = os.environ.get("REALMAN_SDK_ROOT")
    if not sdk_root:
        pytest.skip("REALMAN_SDK_ROOT is not set")
    return RealManFkReference(Path(sdk_root))


@pytest.mark.parametrize(
    "q_deg",
    [
        [0, 0, 0, 0, 0, 0, 0],
        [30, -20, 40, 60, -50, 25, 90],
        [-100, 80, -90, -70, 120, -60, -180],
    ],
)
def test_pinocchio_fk_matches_realman_algo(reference, q_deg):
    q = np.deg2rad(q_deg)
    position_error, orientation_error = pose_errors(
        RM75Kinematics().forward(q), reference.forward(q)
    )

    assert position_error < 1e-4
    assert orientation_error < np.deg2rad(0.01)


def test_tool_fk_matches_realman_algo(reference):
    q = np.deg2rad([30, -20, 40, 60, -50, 25, 90])
    tool = pin.SE3(np.eye(3), np.array([0.0, 0.0, 0.19]))
    position_error, orientation_error = pose_errors(
        RM75Kinematics().forward(q, tool), reference.forward(q, tool)
    )

    assert position_error < 1e-4
    assert orientation_error < np.deg2rad(0.01)


def test_dual_arm_mounts_reuse_single_arm_geometry(reference):
    assembly = DualArmAssembly.from_source_urdf()
    q = np.deg2rad([20, -10, 30, 40, -20, 15, 80])

    assert assembly.dof == 14
    assert assembly.mounts.right_visual_origin_delta_m == pytest.approx(0.001, abs=1e-8)
    for arm, mount in (
        ("left", assembly.mounts.left_base),
        ("right", assembly.mounts.right_base),
    ):
        local = mount.actInv(assembly.forward(arm, q))
        errors = pose_errors(local, reference.forward(q))
        assert errors[0] < 1e-4
        assert errors[1] < np.deg2rad(0.01)

