#!/usr/bin/env python3
"""Recover camera4.json for downloaded standard-path issue cases."""

from __future__ import annotations

import json
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Iterable, Optional


TARGET_CALIB_REL = Path("test_data") / "calibs" / "camera4.json"
CALIB_ATTACHMENT_NAMES = (
    "sigmastar.1/calibs/camera4.json",
    "test_data/calibs/camera4.json",
    "calibs/camera4.json",
)
CAMERA_CONFIG_FILE_NAME = "camera_config_folder.bin"
PREFERRED_CAMERA_IDS = (4, 0)
REQUIRED_FLAT_CALIB_KEYS = ("focal_u", "focal_v", "cu", "cv")


@dataclass(frozen=True)
class ExtractedCalibSource:
    payload: dict[str, Any]
    source_kind: str
    source_path: Path
    detail: str


@dataclass(frozen=True)
class CalibRecoveryResult:
    status: str
    detail: str
    target_path: Path
    source_path: Path | None = None
    source_kind: str | None = None


def _safe_int(value: Any) -> int | None:
    try:
        if value is None:
            return None
        return int(value)
    except (TypeError, ValueError):
        return None


def _sorted_unique_paths(paths: Iterable[Path]) -> list[Path]:
    unique = {path.resolve() if path.exists() else path for path in paths}
    return sorted(unique, key=lambda path: (len(path.parts), str(path)))


def _camera_attachment_matches(name: str) -> bool:
    normalized = str(name).strip().replace("\\", "/")
    if normalized in CALIB_ATTACHMENT_NAMES:
        return True
    return normalized.endswith("/camera4.json") or normalized == "camera4.json"


def iter_concatenated_json_objects(text: str) -> list[dict[str, Any]]:
    decoder = json.JSONDecoder()
    pos = 0
    payloads: list[dict[str, Any]] = []
    text_len = len(text)

    while pos < text_len:
        while pos < text_len and (text[pos].isspace() or text[pos] == "\x00"):
            pos += 1
        if pos >= text_len:
            break

        obj, end = decoder.raw_decode(text, pos)
        if isinstance(obj, dict):
            payloads.append(obj)
        pos = end

    return payloads


def _coerce_float_list(value: Any, *, min_len: int = 0) -> list[float]:
    if isinstance(value, dict):
        ordered = [value.get("x"), value.get("y"), value.get("z")]
        result = [float(item) for item in ordered if item is not None]
    elif isinstance(value, (list, tuple)):
        result = [float(item) for item in value]
    else:
        result = []
    while len(result) < min_len:
        result.append(0.0)
    return result


def _is_valid_flat_calib_payload(payload: dict[str, Any]) -> bool:
    return all(payload.get(key) is not None for key in REQUIRED_FLAT_CALIB_KEYS)


def _build_flat_camera4_payload(record: dict[str, Any], source_path: Path) -> dict[str, Any]:
    payload = dict(record)
    payload["focal_u"] = float(record["focal_u"])
    payload["focal_v"] = float(record["focal_v"])
    payload["cu"] = float(record["cu"])
    payload["cv"] = float(record["cv"])
    payload["roll"] = float(record.get("roll", 0.0))
    payload["pitch"] = float(record.get("pitch", 0.0))
    payload["yaw"] = float(record.get("yaw", 0.0))
    payload["distort_coeffs"] = _coerce_float_list(record.get("distort_coeffs"))
    payload["pos"] = _coerce_float_list(record.get("pos"), min_len=3)[:3]
    if payload.get("image_width") is not None:
        payload["image_width"] = int(payload["image_width"])
    if payload.get("image_height") is not None:
        payload["image_height"] = int(payload["image_height"])
    payload.setdefault("camera_id", 4)
    payload["recovered_from"] = CAMERA_CONFIG_FILE_NAME
    payload["recovered_from_path"] = str(source_path)
    return payload


def extract_calib_from_camera_config_folder(
    config_path: Path,
    preferred_camera_ids: tuple[int, ...] = PREFERRED_CAMERA_IDS,
) -> ExtractedCalibSource | None:
    text = config_path.read_text(encoding="utf-8")
    records = iter_concatenated_json_objects(text)
    valid_records = [record for record in records if _is_valid_flat_calib_payload(record)]
    if not valid_records:
        return None

    selected_records = valid_records
    available_camera_ids = {
        _safe_int(record.get("camera_id"))
        for record in valid_records
        if _safe_int(record.get("camera_id")) is not None
    }
    chosen_camera_id: int | None = None

    for camera_id in preferred_camera_ids:
        preferred_records = [
            record for record in valid_records if _safe_int(record.get("camera_id")) == camera_id
        ]
        if preferred_records:
            selected_records = preferred_records
            chosen_camera_id = camera_id
            break

    if chosen_camera_id is None:
        if len(available_camera_ids) == 1:
            chosen_camera_id = next(iter(available_camera_ids))
        else:
            counts: dict[int, int] = {}
            for record in valid_records:
                camera_id = _safe_int(record.get("camera_id"))
                if camera_id is None:
                    continue
                counts[camera_id] = counts.get(camera_id, 0) + 1
            if counts:
                chosen_camera_id = max(counts.items(), key=lambda item: item[1])[0]
                selected_records = [
                    record for record in valid_records if _safe_int(record.get("camera_id")) == chosen_camera_id
                ]

    best_record = max(
        enumerate(selected_records),
        key=lambda item: (_safe_int(item[1].get("utc_tick")) or -1, item[0]),
    )[1]
    payload = _build_flat_camera4_payload(best_record, config_path)
    detail = (
        f"camera_id={chosen_camera_id if chosen_camera_id is not None else 'unknown'} "
        f"records={len(valid_records)} latest_utc_tick={best_record.get('utc_tick')}"
    )
    return ExtractedCalibSource(
        payload=payload,
        source_kind="camera_config_folder",
        source_path=config_path,
        detail=detail,
    )


def _extract_calib_from_mcap_with_clip_reader(mcap_path: Path) -> ExtractedCalibSource | None:
    try:
        from pdcl_pyclip.reader import ClipReader
    except ImportError:
        return None

    reader = ClipReader(str(mcap_path))
    for attachment in reader.iter_attachments():
        if _camera_attachment_matches(attachment.name):
            payload = json.loads(attachment.data.decode("utf-8"))
            return ExtractedCalibSource(
                payload=payload,
                source_kind="mcap_attachment",
                source_path=mcap_path,
                detail=f"attachment={attachment.name}",
            )
    return None


def _extract_calib_from_mcap_with_generic_reader(mcap_path: Path) -> ExtractedCalibSource | None:
    try:
        from mcap.reader import make_reader
    except ImportError:
        return None

    with mcap_path.open("rb") as file:
        reader = make_reader(file)
        for attachment in reader.iter_attachments():
            if _camera_attachment_matches(attachment.name):
                payload = json.loads(attachment.data.decode("utf-8"))
                return ExtractedCalibSource(
                    payload=payload,
                    source_kind="mcap_attachment",
                    source_path=mcap_path,
                    detail=f"attachment={attachment.name}",
                )
    return None


def extract_calib_from_mcap_attachment(mcap_path: Path) -> ExtractedCalibSource | None:
    errors: list[str] = []
    for extractor in (_extract_calib_from_mcap_with_clip_reader, _extract_calib_from_mcap_with_generic_reader):
        try:
            extracted = extractor(mcap_path)
        except Exception as exc:  # pragma: no cover - recovery fallback logging
            errors.append(f"{extractor.__name__}: {type(exc).__name__}: {exc}")
            continue
        if extracted is not None:
            return extracted

    if errors:
        raise RuntimeError("; ".join(errors))
    return None


def _find_candidate_camera_config_paths(source_root: Path) -> list[Path]:
    return _sorted_unique_paths(source_root.rglob(CAMERA_CONFIG_FILE_NAME))


def _find_candidate_mcap_paths(source_root: Path) -> list[Path]:
    return _sorted_unique_paths(source_root.rglob("*.mcap"))


def _write_camera4_json(target_path: Path, payload: dict[str, Any]) -> None:
    target_path.parent.mkdir(parents=True, exist_ok=True)
    target_path.write_text(
        json.dumps(payload, ensure_ascii=False, indent=2) + "\n",
        encoding="utf-8",
    )


def recover_camera4_json(
    source_root: Path,
    target_root: Path,
    dry_run: bool = False,
) -> CalibRecoveryResult:
    target_path = target_root / TARGET_CALIB_REL
    if target_path.is_file():
        return CalibRecoveryResult(
            status="skipped_existing_calib",
            detail=f"target already exists: {target_path}",
            target_path=target_path,
        )

    camera_config_paths = _find_candidate_camera_config_paths(source_root)
    mcap_paths = _find_candidate_mcap_paths(source_root)
    errors: list[str] = []

    for config_path in camera_config_paths:
        try:
            extracted = extract_calib_from_camera_config_folder(config_path)
        except Exception as exc:
            errors.append(f"{config_path}: {type(exc).__name__}: {exc}")
            continue
        if extracted is None:
            continue
        if dry_run:
            return CalibRecoveryResult(
                status="planned_calib_recovery_from_camera_config_folder",
                detail=f"would write {target_path} from {config_path} ({extracted.detail})",
                target_path=target_path,
                source_path=config_path,
                source_kind=extracted.source_kind,
            )
        _write_camera4_json(target_path, extracted.payload)
        return CalibRecoveryResult(
            status="recovered_calib_from_camera_config_folder",
            detail=f"wrote {target_path} from {config_path} ({extracted.detail})",
            target_path=target_path,
            source_path=config_path,
            source_kind=extracted.source_kind,
        )

    for mcap_path in mcap_paths:
        try:
            extracted = extract_calib_from_mcap_attachment(mcap_path)
        except Exception as exc:
            errors.append(f"{mcap_path}: {type(exc).__name__}: {exc}")
            continue
        if extracted is None:
            continue
        if dry_run:
            return CalibRecoveryResult(
                status="planned_calib_recovery_from_mcap_attachment",
                detail=f"would write {target_path} from {mcap_path} ({extracted.detail})",
                target_path=target_path,
                source_path=mcap_path,
                source_kind=extracted.source_kind,
            )
        _write_camera4_json(target_path, extracted.payload)
        return CalibRecoveryResult(
            status="recovered_calib_from_mcap_attachment",
            detail=f"wrote {target_path} from {mcap_path} ({extracted.detail})",
            target_path=target_path,
            source_path=mcap_path,
            source_kind=extracted.source_kind,
        )

    checked_sources = (
        f"camera_config_folder.bin={len(camera_config_paths)} mcap={len(mcap_paths)}"
    )
    if errors:
        checked_sources += f"; errors: {' | '.join(errors[:3])}"
    return CalibRecoveryResult(
        status="failed_calib_recovery",
        detail=f"no recoverable calibration found under {source_root} ({checked_sources})",
        target_path=target_path,
    )
