#!/usr/bin/env python3
"""Build a concise backend comparison and select the recommended result."""
import argparse
import json
from pathlib import Path

import numpy as np
from scipy.spatial.transform import Rotation


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--open3d", required=True)
    parser.add_argument("--small", required=True)
    parser.add_argument("--open3d-quality", required=True)
    parser.add_argument("--small-quality", required=True)
    parser.add_argument("--open3d-check", required=True)
    parser.add_argument("--small-check", required=True)
    parser.add_argument("--output", required=True)
    parser.add_argument("--recommended-output", required=True)
    args = parser.parse_args()

    open_result = json.loads(Path(args.open3d).read_text(encoding="utf-8-sig"))
    small_result = json.loads(Path(args.small).read_text(encoding="utf-8-sig"))
    open_quality = json.loads(Path(args.open3d_quality).read_text(encoding="utf-8-sig"))
    small_quality = json.loads(Path(args.small_quality).read_text(encoding="utf-8-sig"))
    open_check = json.loads(Path(args.open3d_check).read_text(encoding="utf-8-sig"))
    small_check = json.loads(Path(args.small_check).read_text(encoding="utf-8-sig"))
    x_open = np.asarray(open_result["matrix_4x4"], float)
    x_small = np.asarray(small_result["matrix_4x4"], float)
    delta = np.linalg.inv(x_open) @ x_small

    def compact(result, quality, check):
        estimate = result["estimation"]["residuals"]
        auxiliary = check["metrics"]
        return {
            "translation_m": result["translation_m"],
            "rotation_rpy_deg_xyz": result["rotation_rpy_deg_xyz"],
            "estimation_pairs": estimate["pairs"],
            "estimation_translation_rms_m": estimate["translation_m"]["rms"],
            "estimation_rotation_rms_deg": estimate["rotation_deg"]["rms"],
            "bootstrap_std": result["bootstrap"]["std"],
            "initial_B_loop_closure": quality["accepted_loop_closure"],
            "batch1_auxiliary_pairs": auxiliary["pairs"],
            "batch1_auxiliary_translation_rms_m": auxiliary["translation_m"]["rms"],
            "batch1_auxiliary_rotation_rms_deg": auxiliary["rotation_deg"]["rms"],
        }

    summary = {
        "recommended_backend": "open3d_gicp",
        "selection_reason": (
            "The two X estimates agree closely; Open3D has lower second-batch AX residual, "
            "better B loop closure, and lower first-batch auxiliary residual."
        ),
        "coordinate_convention": "T_body_lidar maps raw LiDAR points into rear-axle body frame",
        "measured_extrinsic_used_as_initial": False,
        "second_batch_role": "estimation (dense RTK)",
        "first_batch_role": "auxiliary check only (sparse RTK)",
        "backend_difference": {
            "translation_m": float(np.linalg.norm(delta[:3, 3])),
            "rotation_deg": float(np.rad2deg(Rotation.from_matrix(delta[:3, :3]).magnitude())),
        },
        "open3d_gicp": compact(open_result, open_quality, open_check),
        "small_gicp": compact(small_result, small_quality, small_check),
        "important_limit": (
            "Backend agreement is strong, but AX rotation RMS remains about one degree. "
            "This is not a centimetre-grade absolute certification."
        ),
    }
    output = Path(args.output)
    output.parent.mkdir(parents=True, exist_ok=True)
    output.write_text(json.dumps(summary, ensure_ascii=False, indent=2), encoding="utf-8")
    recommended = dict(open_result)
    recommended["selection"] = {
        "recommended_backend": "open3d_gicp",
        "comparison_summary": str(output.name),
        "backend_difference": summary["backend_difference"],
        "warning": summary["important_limit"],
    }
    Path(args.recommended_output).write_text(
        json.dumps(recommended, ensure_ascii=False, indent=2), encoding="utf-8"
    )
    print(json.dumps(summary, ensure_ascii=False, indent=2))


if __name__ == "__main__":
    main()
