"""Inspect original camera inputs with NumPy and ffprobe without importing LingBot."""

import argparse
import json
import struct
import subprocess
from pathlib import Path

import numpy as np

p = argparse.ArgumentParser(description=__doc__)
p.add_argument("directory", type=Path)
a = p.parse_args()
r = a.directory
poses = np.load(r / "poses.npy", allow_pickle=False)
K = np.load(r / "intrinsics.npy", allow_pickle=False)
m = json.loads((r / "lingbot-camera-measurements.json").read_text())
assert poses.shape == (81, 4, 4) and K.shape == (1, 4)
assert np.isfinite(poses).all() and np.isfinite(K).all()
assert np.allclose(K, [[832, 832, 416, 240]], atol=1e-8)
assert np.allclose(poses[:, 3], [0, 0, 0, 1], atol=1e-8)
anchor = np.asarray(m["anchorWorld"])
errors = []
for i, pose in enumerate(poses):
    R = pose[:3, :3]
    assert np.allclose(R.T @ R, np.eye(3), atol=1e-6)
    assert abs(np.linalg.det(R) - 1) < 1e-6
    assert np.allclose(R, poses[0, :3, :3], atol=1e-8)
    assert abs(pose[0, 3] - (-0.8 + 1.6 * i / 80)) < 1e-6
    assert np.allclose(pose[:3, 3], m["frames"][i]["cameraWorld"], atol=1e-6)
    pc = np.linalg.inv(pose) @ anchor
    assert pc[2] > 0
    pixel = [K[0, 0] * pc[0] / pc[2] + K[0, 2], K[0, 1] * pc[1] / pc[2] + K[0, 3]]
    error = float(np.max(np.abs(np.asarray(pixel) - m["frames"][i]["nativeAnchorPixels"])))
    assert error < 0.001
    errors.append(error)
header = (r / "image.png").read_bytes()[:24]
assert header[:8] == b"\x89PNG\r\n\x1a\n" and struct.unpack(">II", header[16:24]) == (832, 480)
probe = json.loads(
    subprocess.check_output(
        [
            "ffprobe",
            "-v",
            "error",
            "-select_streams",
            "v:0",
            "-show_frames",
            "-show_entries",
            "frame=best_effort_timestamp_time",
            "-of",
            "json",
            str(r / "native-preview.mp4"),
        ],
        text=True,
    )
)
assert len(probe["frames"]) == 81
for i, frame in enumerate(probe["frames"]):
    assert abs(float(frame["best_effort_timestamp_time"]) - i / 16) < 1e-6
selected = 4 * ((min(81, len(poses)) - 1) // 4) + 1
latent = (selected - 1) // 4 + 1
kept = latent - latent % 4
controls = []
for t in np.linspace(0, selected - 1, kept):
    controls.append(
        {
            "sourcePoseIndex": float(t),
            "cameraWorld": [float(np.interp(t, np.arange(81), poses[:, j, 3])) for j in range(3)],
        }
    )
print(
    json.dumps(
        {
            "posesShape": list(poses.shape),
            "intrinsicsShape": list(K.shape),
            "maxProjectionErrorPixels": max(errors),
            "sourceFrames": 81,
            "selectedFrames": selected,
            "defaultCLILatentChunk": 4,
            "retainedLatentFrames": kept,
            "explicitChunkThreeEffectiveFrames": ((latent - latent % 3) - 1) * 4 + 1,
            "effectiveFrames": (kept - 1) * 4 + 1,
            "linearTranslationControlSamples": controls,
            "scope": "Independent structural and projection inspection only, not model or GPU execution",
        },
        indent=2,
    )
)
