from __future__ import annotations

import hashlib
import json
import math
import os
import re
import stat
import unicodedata
from collections.abc import Mapping
from dataclasses import dataclass, replace
from datetime import UTC, datetime
from functools import lru_cache
from pathlib import Path
from typing import IO, Any

from k1link.device_plugins.xgrids_k1.mqtt.capture import (
    CAPTURE_CLOCK_FILENAME,
    CAPTURE_CLOCK_ORIGIN_FILENAME,
    FRAME_HEADER,
    GROUP_COMMIT_MAX_BYTES,
    GROUP_COMMIT_MAX_MESSAGES,
    MAX_CONFIGURABLE_MESSAGE_BYTES,
    MAX_TOPIC_BYTES,
    RAW_MAGIC,
    CaptureClockEnvelope,
    CaptureClockOrigin,
    CaptureFormatError,
    is_capture_clock_filename,
    read_capture_clock_envelope,
    read_capture_clock_origin,
)
from k1link.sessions.models import (
    LegacyMediaSourceCandidate,
    SessionModality,
    SessionStatus,
)

MAX_LEGACY_JSON_BYTES = 2 * 1024 * 1024
LEGACY_SESSION_PATTERN = re.compile(r"^[A-Za-z0-9._-]+_viewer_live(?:_[0-9]+)?$")
SHA256_PATTERN = re.compile(r"^[a-f0-9]{64}$")
MEDIA_SOURCE_PATTERN = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$")
MEDIA_EPOCH_PATTERN = re.compile(r"^epoch-(?!0+$)[0-9]+$")
MEDIA_SEGMENT_PATTERN = re.compile(r"^[0-9]+\.m4s$")
MAX_RECOVERY_METADATA_LINE_BYTES = 64 * 1024


@dataclass(frozen=True, slots=True)
class LegacySessionCandidate:
    """K1 archive discovery result retained inside the compatibility adapter."""

    session_id: str
    display_name: str
    status: SessionStatus
    started_at_utc: str | None
    completed_at_utc: str | None
    duration_seconds: float | None
    modalities: tuple[SessionModality, ...]
    replayable: bool
    total_bytes: int
    allowed_root: Path
    session_root: Path
    raw_path: Path
    raw_byte_length: int
    replay_raw_byte_length: int
    replay_metadata_byte_length: int
    replay_capture_clock_origin_byte_length: int
    replay_capture_clock_byte_length: int
    raw_sha256: str | None
    capture_clock_origin_sha256: str | None
    capture_clock_sha256: str | None
    capture_clock_path: Path | None
    raw_integrity_status: str
    timeline_origin_epoch_ns: int
    timeline_origin_monotonic_ns: int
    timeline_completion_epoch_ns: int
    timeline_completion_monotonic_ns: int
    media_sources: tuple[LegacyMediaSourceCandidate, ...]


@dataclass(frozen=True, slots=True)
class _RecoveredCapture:
    message_count: int
    topic_counts: dict[str, int]
    started_at_utc: str
    completed_at_utc: str
    duration_seconds: float
    raw_committed_bytes: int
    metadata_committed_bytes: int
    first_epoch_ns: int
    last_epoch_ns: int
    first_monotonic_ns: int
    last_monotonic_ns: int
    capture_clock_origin: CaptureClockOrigin | None = None
    capture_clock: CaptureClockEnvelope | None = None
    capture_clock_path: Path | None = None
    capture_clock_scope: str | None = None


def discover_legacy_viewer_sessions(root: Path) -> tuple[LegacySessionCandidate, ...]:
    """Describe legacy live sessions without copying or decoding their payloads."""

    allowed_root = root.expanduser().resolve()
    if not allowed_root.is_dir():
        return ()
    active_session_root = _active_session_root(allowed_root)
    candidates: list[LegacySessionCandidate] = []
    for entry in sorted(allowed_root.iterdir()):
        if not entry.is_dir() or not LEGACY_SESSION_PATTERN.fullmatch(entry.name):
            continue
        resolved = entry.resolve()
        if not resolved.is_relative_to(allowed_root):
            continue
        # The saved-session catalog deliberately omits the writer-owned run.
        # Besides avoiding a misleading interrupted/error row, this prevents
        # the two-second reconciler from repeatedly scanning and hashing a
        # capture that is still growing. Marker release makes it discoverable
        # on the next reconciliation pass.
        if active_session_root == resolved:
            continue
        candidates.append(
            _describe_session(
                allowed_root,
                resolved,
                active=False,
            )
        )
    return tuple(candidates)


def _describe_session(
    allowed_root: Path,
    session_root: Path,
    *,
    active: bool,
) -> LegacySessionCandidate:
    capture_root = session_root / "captures" / "mqtt_live"
    raw_path = capture_root / "mqtt.raw.k1mqtt"
    summary = _read_json_object(capture_root / "mqtt.summary.json")
    manifest = _read_json_object(session_root / "manifest.redacted.json")
    raw_bytes = raw_path.stat().st_size if _confined_file(raw_path, session_root) else 0
    raw_magic_ok = False
    if raw_bytes >= len(RAW_MAGIC):
        with raw_path.open("rb") as stream:
            raw_magic_ok = stream.read(len(RAW_MAGIC)) == RAW_MAGIC

    has_completed_summary = _is_completed_summary(summary)
    completed = (
        _validate_completed_capture(capture_root, session_root, raw_path, summary)
        if has_completed_summary and raw_magic_ok
        else None
    )
    recovered = (
        _recover_interrupted_capture(capture_root, session_root, raw_path)
        if raw_magic_ok
        and completed is None
        and (not has_completed_summary or summary.get("error") is not None)
        else None
    )
    message_count = (
        _non_negative_int(summary.get("message_count"))
        if completed is not None
        else recovered.message_count
        if recovered is not None
        else 0
    )
    summary_topic_counts = summary.get("topic_counts")
    topic_counts = (
        _normalized_topic_counts(summary_topic_counts)
        if completed is not None
        else recovered.topic_counts
        if recovered is not None
        else {}
    )
    media_sources = _discover_media_sources(session_root)
    modalities = list(_modalities(topic_counts))
    if media_sources:
        modalities.append("video")
    replayable = bool(
        not active
        and raw_magic_ok
        and raw_bytes > len(RAW_MAGIC)
        and message_count > 0
        and any(modality in {"point-cloud", "trajectory"} for modality in modalities)
        # A v2 transport-scoped completion is provisional. When camera media
        # exists, only the owner-sealed session envelope can advertise a
        # combined replay. Legacy captures have no scope and retain their
        # reviewed first/last-message fallback.
        and not (
            media_sources
            and (
                completed.capture_clock_scope
                if completed is not None
                else recovered.capture_clock_scope
                if recovered is not None
                else None
            )
            in {"transport", "origin"}
        )
    )
    if completed is not None:
        status = (
            "interrupted"
            if active
            else _status(replayable, summary.get("error"), summary.get("stop_reason"))
        )
        started_at = (
            _safe_timestamp(summary.get("created_at_utc"))
            or _safe_timestamp(manifest.get("started_at_utc"))
            or _timestamp_from_session_name(session_root.name)
        )
        completed_at = _safe_timestamp(summary.get("completed_at_utc")) or _safe_timestamp(
            manifest.get("completed_at_utc")
        )
        duration = _duration(summary.get("session_elapsed_seconds"))
        if duration is None:
            duration = _duration(summary.get("capture_elapsed_seconds"))
        declared_hash = _declared_raw_hash(summary)
        integrity_status = "verified" if declared_hash is not None else "validated-structure"
    else:
        status = "interrupted" if recovered is not None or active else "failed"
        started_at = (
            recovered.started_at_utc
            if recovered is not None
            else _safe_timestamp(manifest.get("started_at_utc"))
            or _timestamp_from_session_name(session_root.name)
        )
        completed_at = recovered.completed_at_utc if recovered is not None else None
        duration = recovered.duration_seconds if recovered is not None else None
        declared_hash = None
        integrity_status = "validated-prefix" if recovered is not None else "unverified"

    replay_raw_bytes = (
        completed.raw_committed_bytes
        if completed is not None
        else recovered.raw_committed_bytes
        if recovered is not None
        else 0
    )
    replay_metadata_bytes = (
        completed.metadata_committed_bytes
        if completed is not None
        else recovered.metadata_committed_bytes
        if recovered is not None
        else 0
    )
    timing = completed if completed is not None else recovered
    capture_clock = None if timing is None else timing.capture_clock
    capture_clock_origin = None if timing is None else timing.capture_clock_origin
    replay_capture_clock_origin_bytes = (
        capture_clock_origin.artifact_byte_length if capture_clock_origin is not None else 0
    )
    replay_capture_clock_bytes = (
        capture_clock.artifact_byte_length if capture_clock is not None else 0
    )
    timeline_origin_epoch_ns = (
        capture_clock.started_at_epoch_ns
        if capture_clock is not None
        else capture_clock_origin.started_at_epoch_ns
        if capture_clock_origin is not None
        else timing.first_epoch_ns
        if timing is not None
        else 0
    )
    timeline_origin_monotonic_ns = (
        capture_clock.started_monotonic_ns
        if capture_clock is not None
        else capture_clock_origin.started_monotonic_ns
        if capture_clock_origin is not None
        else timing.first_monotonic_ns
        if timing is not None
        else 0
    )
    timeline_completion_epoch_ns = (
        capture_clock.completed_at_epoch_ns
        if capture_clock is not None
        else timing.last_epoch_ns
        if timing is not None
        else 0
    )
    timeline_completion_monotonic_ns = (
        capture_clock.completed_monotonic_ns
        if capture_clock is not None
        else timing.last_monotonic_ns
        if timing is not None
        else 0
    )

    return LegacySessionCandidate(
        session_id=session_root.name,
        display_name=_project_display_name(manifest, fallback=session_root.name),
        status=status,
        started_at_utc=started_at,
        completed_at_utc=completed_at,
        duration_seconds=duration,
        modalities=tuple(modalities),
        replayable=replayable,
        total_bytes=raw_bytes + sum(source.byte_length for source in media_sources),
        allowed_root=allowed_root,
        session_root=session_root,
        raw_path=raw_path.resolve(strict=False),
        raw_byte_length=raw_bytes,
        replay_raw_byte_length=replay_raw_bytes,
        replay_metadata_byte_length=replay_metadata_bytes,
        replay_capture_clock_origin_byte_length=replay_capture_clock_origin_bytes,
        replay_capture_clock_byte_length=replay_capture_clock_bytes,
        raw_sha256=declared_hash,
        capture_clock_origin_sha256=(
            capture_clock_origin.artifact_sha256 if capture_clock_origin is not None else None
        ),
        capture_clock_sha256=(capture_clock.artifact_sha256 if capture_clock is not None else None),
        capture_clock_path=None if timing is None else timing.capture_clock_path,
        raw_integrity_status=integrity_status,
        timeline_origin_epoch_ns=timeline_origin_epoch_ns,
        timeline_origin_monotonic_ns=timeline_origin_monotonic_ns,
        timeline_completion_epoch_ns=timeline_completion_epoch_ns,
        timeline_completion_monotonic_ns=timeline_completion_monotonic_ns,
        media_sources=media_sources,
    )


def _project_display_name(manifest: Mapping[str, Any], *, fallback: str) -> str:
    value = manifest.get("project_name")
    if not isinstance(value, str):
        return fallback
    normalized = unicodedata.normalize("NFKC", value).strip()
    try:
        normalized.encode("utf-8", errors="strict")
    except UnicodeEncodeError:
        return fallback
    if (
        not normalized
        or len(normalized) > 96
        or any(unicodedata.category(character) in {"Cc", "Cs"} for character in normalized)
    ):
        return fallback
    return normalized


def _is_completed_summary(summary: dict[str, Any]) -> bool:
    return (
        "message_count" in summary
        and isinstance(summary.get("message_count"), int)
        and not isinstance(summary.get("message_count"), bool)
        and isinstance(summary.get("topic_counts"), dict)
        and isinstance(summary.get("stop_reason"), str)
    )


def _validate_completed_capture(
    capture_root: Path,
    session_root: Path,
    raw_path: Path,
    summary: dict[str, Any],
) -> _RecoveredCapture | None:
    metadata_path = capture_root / "mqtt.metadata.jsonl"
    try:
        raw_stat = raw_path.lstat()
        metadata_stat = metadata_path.lstat()
    except OSError:
        return None
    if not stat.S_ISREG(raw_stat.st_mode) or not stat.S_ISREG(metadata_stat.st_mode):
        return None
    fingerprint = json.dumps(
        {
            "message_count": summary.get("message_count"),
            "topic_counts": summary.get("topic_counts"),
            "raw_bytes": summary.get("raw_bytes"),
            "artifact_hashes": summary.get("artifact_hashes"),
        },
        sort_keys=True,
        separators=(",", ":"),
    )
    validated = _validate_completed_capture_cached(
        str(capture_root),
        str(session_root),
        str(raw_path),
        _stat_identity(raw_stat),
        _stat_identity(metadata_stat),
        fingerprint,
    )
    if validated is None:
        return None

    schema_version = summary.get("schema_version", 1)
    requires_capture_clock = (
        isinstance(schema_version, int)
        and not isinstance(schema_version, bool)
        and schema_version >= 2
    )
    capture_clock_scope = summary.get("capture_clock_scope")
    if requires_capture_clock and capture_clock_scope not in {"transport", "session"}:
        return None
    artifacts = summary.get("artifacts")
    declared_name = artifacts.get("capture_clock") if isinstance(artifacts, dict) else None
    declared_origin_name = (
        artifacts.get("capture_clock_origin") if isinstance(artifacts, dict) else None
    )
    declared_hash = _declared_capture_clock_hash(summary)
    declared_origin_hash = _declared_capture_clock_origin_hash(summary)
    if declared_name is None and declared_hash is None and not requires_capture_clock:
        return validated
    if (
        not is_capture_clock_filename(declared_name)
        or declared_hash is None
        or declared_origin_name != CAPTURE_CLOCK_ORIGIN_FILENAME
        or declared_origin_hash is None
        or (capture_clock_scope == "transport" and declared_name != CAPTURE_CLOCK_FILENAME)
        or (
            capture_clock_scope == "session"
            and isinstance(declared_name, str)
            and declared_name != f"mqtt.timeline.session-{declared_hash}.json"
        )
    ):
        return None
    assert isinstance(declared_name, str)
    capture_clock_path = capture_root / declared_name
    capture_clock_origin_path = capture_root / CAPTURE_CLOCK_ORIGIN_FILENAME
    if not _confined_file(capture_clock_path, session_root) or not _confined_file(
        capture_clock_origin_path,
        session_root,
    ):
        return None
    try:
        capture_clock_origin = read_capture_clock_origin(
            capture_clock_origin_path,
            expected_sha256=declared_origin_hash,
        )
        capture_clock = read_capture_clock_envelope(
            capture_clock_path,
            expected_sha256=declared_hash,
        )
    except CaptureFormatError:
        return None
    if not (
        capture_clock.started_at_epoch_ns == capture_clock_origin.started_at_epoch_ns
        and capture_clock.started_monotonic_ns == capture_clock_origin.started_monotonic_ns
        and capture_clock.started_monotonic_ns
        <= validated.first_monotonic_ns
        <= validated.last_monotonic_ns
        <= capture_clock.completed_monotonic_ns
    ):
        return None
    declared_duration = _duration(summary.get("session_elapsed_seconds"))
    if requires_capture_clock and (
        declared_duration is None
        or abs(declared_duration - capture_clock.duration_ns / 1_000_000_000) > 1e-9
    ):
        return None
    return replace(
        validated,
        capture_clock_origin=capture_clock_origin,
        capture_clock=capture_clock,
        capture_clock_path=capture_clock_path,
        capture_clock_scope=(capture_clock_scope if isinstance(capture_clock_scope, str) else None),
    )


@lru_cache(maxsize=128)
def _validate_completed_capture_cached(
    capture_root_text: str,
    session_root_text: str,
    raw_path_text: str,
    _raw_identity: tuple[int, int, int, int, int],
    _metadata_identity: tuple[int, int, int, int, int],
    summary_fingerprint: str,
) -> _RecoveredCapture | None:
    capture_root = Path(capture_root_text)
    session_root = Path(session_root_text)
    raw_path = Path(raw_path_text)
    summary = json.loads(summary_fingerprint)
    validated = _scan_capture_prefix(
        capture_root,
        session_root,
        raw_path,
        tolerate_incomplete_metadata_tail=False,
        tolerate_raw_crash_tail=False,
    )
    if validated is None:
        return None
    if validated.message_count != _non_negative_int(summary.get("message_count")):
        return None
    if validated.topic_counts != _normalized_topic_counts(summary.get("topic_counts")):
        return None
    declared_raw_bytes = summary.get("raw_bytes")
    if declared_raw_bytes is not None and (
        not isinstance(declared_raw_bytes, int)
        or isinstance(declared_raw_bytes, bool)
        or declared_raw_bytes != validated.raw_committed_bytes
    ):
        return None
    raw_hash = _declared_raw_hash(summary)
    if raw_hash is not None and _sha256_stable(raw_path) != raw_hash:
        return None
    metadata_hash = _declared_metadata_hash(summary)
    metadata_path = capture_root / "mqtt.metadata.jsonl"
    if metadata_hash is not None and _sha256_stable(metadata_path) != metadata_hash:
        return None
    return validated


def _recover_interrupted_capture(
    capture_root: Path,
    session_root: Path,
    raw_path: Path,
) -> _RecoveredCapture | None:
    recovered = _scan_capture_prefix(
        capture_root,
        session_root,
        raw_path,
        tolerate_incomplete_metadata_tail=True,
        tolerate_raw_crash_tail=True,
    )
    if recovered is None:
        return None
    origin_path = capture_root / CAPTURE_CLOCK_ORIGIN_FILENAME
    if not _confined_file(origin_path, session_root):
        return recovered
    try:
        origin = read_capture_clock_origin(origin_path)
    except CaptureFormatError:
        return recovered
    if origin.started_monotonic_ns > recovered.first_monotonic_ns:
        return recovered
    return replace(
        recovered,
        capture_clock_origin=origin,
        capture_clock_scope="origin",
    )


def _scan_capture_prefix(
    capture_root: Path,
    session_root: Path,
    raw_path: Path,
    *,
    tolerate_incomplete_metadata_tail: bool,
    tolerate_raw_crash_tail: bool,
) -> _RecoveredCapture | None:
    metadata_path = capture_root / "mqtt.metadata.jsonl"
    if not _confined_file(raw_path, session_root) or not _confined_file(
        metadata_path, session_root
    ):
        return None
    try:
        raw_size = raw_path.stat().st_size
        with raw_path.open("rb") as raw_stream, metadata_path.open("rb") as metadata_stream:
            if raw_stream.read(len(RAW_MAGIC)) != RAW_MAGIC:
                return None
            consumed_metadata_bytes = 0
            message_count = 0
            topic_counts: dict[str, int] = {}
            first_epoch_ns: int | None = None
            last_epoch_ns: int | None = None
            first_monotonic_ns: int | None = None
            last_monotonic_ns: int | None = None
            first_timestamp: str | None = None
            last_timestamp: str | None = None
            metadata_committed_bytes = 0
            while True:
                line = metadata_stream.readline(MAX_RECOVERY_METADATA_LINE_BYTES + 1)
                if not line:
                    break
                consumed_metadata_bytes += len(line)
                if len(line) > MAX_RECOVERY_METADATA_LINE_BYTES:
                    return None
                if not line.endswith(b"\n"):
                    if tolerate_incomplete_metadata_tail:
                        break
                    return None
                record = _metadata_record(line)
                if record is None:
                    return None
                metadata_committed_bytes = consumed_metadata_bytes
                if record.get("record_type") != "message":
                    continue
                expected_sequence = message_count + 1
                validated = _validate_recovery_frame(
                    raw_stream,
                    raw_size=raw_size,
                    record=record,
                    expected_sequence=expected_sequence,
                )
                if validated is None:
                    return None
                topic, epoch_ns, monotonic_ns, timestamp = validated
                if last_monotonic_ns is not None and monotonic_ns < last_monotonic_ns:
                    return None
                message_count = expected_sequence
                topic_counts[topic] = topic_counts.get(topic, 0) + 1
                if first_epoch_ns is None:
                    first_epoch_ns = epoch_ns
                    first_monotonic_ns = monotonic_ns
                    first_timestamp = timestamp
                last_epoch_ns = epoch_ns
                last_monotonic_ns = monotonic_ns
                last_timestamp = timestamp
            if metadata_stream.read(1):
                return None
            raw_committed_bytes = raw_stream.tell()
            if raw_committed_bytes != raw_size and not (
                tolerate_raw_crash_tail and _tolerable_raw_crash_tail(raw_stream, raw_size=raw_size)
            ):
                return None
    except OSError:
        return None
    if (
        message_count == 0
        or first_epoch_ns is None
        or last_epoch_ns is None
        or first_timestamp is None
        or last_timestamp is None
        or first_monotonic_ns is None
        or last_monotonic_ns is None
    ):
        return None
    return _RecoveredCapture(
        message_count=message_count,
        topic_counts=topic_counts,
        started_at_utc=first_timestamp,
        completed_at_utc=last_timestamp,
        duration_seconds=(last_monotonic_ns - first_monotonic_ns) / 1_000_000_000,
        raw_committed_bytes=raw_committed_bytes,
        metadata_committed_bytes=metadata_committed_bytes,
        first_epoch_ns=first_epoch_ns,
        last_epoch_ns=last_epoch_ns,
        first_monotonic_ns=first_monotonic_ns,
        last_monotonic_ns=last_monotonic_ns,
    )


def _metadata_record(line: bytes) -> dict[str, Any] | None:
    try:
        value = json.loads(line.decode("utf-8"))
    except (UnicodeDecodeError, json.JSONDecodeError):
        return None
    return value if isinstance(value, dict) else None


def _tolerable_raw_crash_tail(raw_stream: IO[bytes], *, raw_size: int) -> bool:
    """Accept at most one raw frame which was not committed by metadata.

    The capture writer flushes raw before metadata and does not begin the next
    message until the current metadata record is written. Consequently a
    process crash can leave only a prefix (or all) of one additional frame.
    Anything beyond that boundary is corruption, not a recoverable tail.
    """

    tail_offset = raw_stream.tell()
    tail_bytes = raw_size - tail_offset
    if tail_bytes <= 0:
        return True
    if tail_bytes > GROUP_COMMIT_MAX_BYTES + MAX_CONFIGURABLE_MESSAGE_BYTES:
        return False
    frames = 0
    while raw_stream.tell() < raw_size:
        remaining = raw_size - raw_stream.tell()
        header = raw_stream.read(min(remaining, FRAME_HEADER.size))
        if len(header) < FRAME_HEADER.size:
            return frames < GROUP_COMMIT_MAX_MESSAGES
        topic_length, payload_length = FRAME_HEADER.unpack(header)
        if not 1 <= topic_length <= MAX_TOPIC_BYTES:
            return False
        if payload_length > MAX_CONFIGURABLE_MESSAGE_BYTES:
            return False
        frame_bytes = FRAME_HEADER.size + topic_length + payload_length
        frame_remaining = raw_size - (raw_stream.tell() - FRAME_HEADER.size)
        available_topic_bytes = min(
            topic_length,
            max(0, frame_remaining - FRAME_HEADER.size),
        )
        topic_prefix = raw_stream.read(available_topic_bytes)
        if available_topic_bytes < topic_length:
            return frames < GROUP_COMMIT_MAX_MESSAGES
        try:
            topic = topic_prefix.decode("utf-8")
        except UnicodeDecodeError:
            return False
        if not topic:
            return False
        payload_available = frame_remaining - FRAME_HEADER.size - topic_length
        if payload_available < payload_length:
            return frames < GROUP_COMMIT_MAX_MESSAGES
        raw_stream.seek(payload_length, 1)
        frames += 1
        if frames > GROUP_COMMIT_MAX_MESSAGES or frame_remaining < frame_bytes:
            return False
    return frames <= GROUP_COMMIT_MAX_MESSAGES


def _validate_recovery_frame(
    raw_stream: IO[bytes],
    *,
    raw_size: int,
    record: dict[str, Any],
    expected_sequence: int,
) -> tuple[str, int, int, str] | None:
    sequence = record.get("sequence")
    topic = record.get("topic")
    payload_bytes = record.get("payload_bytes")
    frame_offset = record.get("raw_frame_offset")
    payload_offset = record.get("raw_payload_offset")
    frame_bytes = record.get("raw_frame_bytes")
    epoch_ns = record.get("received_at_epoch_ns")
    monotonic_ns = record.get("received_monotonic_ns")
    if (
        sequence != expected_sequence
        or not isinstance(topic, str)
        or not topic
        or not isinstance(payload_bytes, int)
        or isinstance(payload_bytes, bool)
        or not 0 <= payload_bytes <= MAX_CONFIGURABLE_MESSAGE_BYTES
        or not isinstance(frame_offset, int)
        or isinstance(frame_offset, bool)
        or frame_offset != raw_stream.tell()
        or not isinstance(payload_offset, int)
        or isinstance(payload_offset, bool)
        or not isinstance(frame_bytes, int)
        or isinstance(frame_bytes, bool)
        or not isinstance(epoch_ns, int)
        or isinstance(epoch_ns, bool)
        or epoch_ns < 0
        or not isinstance(monotonic_ns, int)
        or isinstance(monotonic_ns, bool)
        or monotonic_ns < 0
    ):
        return None
    header = raw_stream.read(FRAME_HEADER.size)
    if len(header) != FRAME_HEADER.size:
        return None
    topic_length, raw_payload_bytes = FRAME_HEADER.unpack(header)
    if (
        not 1 <= topic_length <= MAX_TOPIC_BYTES
        or raw_payload_bytes != payload_bytes
        or payload_offset != frame_offset + FRAME_HEADER.size + topic_length
        or frame_bytes != FRAME_HEADER.size + topic_length + payload_bytes
        or frame_offset + frame_bytes > raw_size
    ):
        return None
    raw_topic = raw_stream.read(topic_length)
    try:
        decoded_topic = raw_topic.decode("utf-8")
    except UnicodeDecodeError:
        return None
    if decoded_topic != topic:
        return None
    raw_stream.seek(payload_bytes, 1)
    timestamp = _safe_timestamp(record.get("received_at_utc"))
    if timestamp is None:
        try:
            timestamp = datetime.fromtimestamp(epoch_ns / 1_000_000_000, tz=UTC).isoformat()
        except (OSError, OverflowError, ValueError):
            return None
    return topic, epoch_ns, monotonic_ns, timestamp


def _read_json_object(path: Path) -> dict[str, Any]:
    try:
        if not path.is_file() or path.stat().st_size > MAX_LEGACY_JSON_BYTES:
            return {}
        value = json.loads(path.read_text(encoding="utf-8"))
    except (OSError, UnicodeDecodeError, json.JSONDecodeError):
        return {}
    return value if isinstance(value, dict) else {}


def _confined_file(path: Path, session_root: Path) -> bool:
    try:
        resolved = path.resolve(strict=True)
    except OSError:
        return False
    return resolved.is_file() and resolved.is_relative_to(session_root)


def _non_negative_int(value: object) -> int:
    return value if isinstance(value, int) and not isinstance(value, bool) and value >= 0 else 0


def _normalized_topic_counts(value: object) -> dict[str, int]:
    if not isinstance(value, dict):
        return {}
    result: dict[str, int] = {}
    for topic, raw_count in value.items():
        count = _non_negative_int(raw_count)
        if isinstance(topic, str) and count > 0:
            result[topic] = count
    return result


def _duration(value: object) -> float | None:
    if not isinstance(value, (int, float)) or isinstance(value, bool):
        return None
    duration = float(value)
    return duration if math.isfinite(duration) and duration >= 0 else None


def _safe_timestamp(value: object) -> str | None:
    if not isinstance(value, str) or len(value) > 64:
        return None
    candidate = value.strip()
    try:
        parsed = datetime.fromisoformat(candidate.replace("Z", "+00:00"))
    except ValueError:
        return None
    if parsed.tzinfo is None:
        return None
    return candidate


def _timestamp_from_session_name(session_id: str) -> str | None:
    prefix = session_id.split("_", 1)[0]
    try:
        parsed = datetime.strptime(prefix, "%Y%m%dT%H%M%SZ").replace(tzinfo=UTC)
    except ValueError:
        return None
    return parsed.isoformat().replace("+00:00", "Z")


def _declared_raw_hash(summary: dict[str, Any]) -> str | None:
    hashes = summary.get("artifact_hashes")
    if not isinstance(hashes, dict):
        return None
    value = hashes.get("raw_sha256")
    return value if isinstance(value, str) and SHA256_PATTERN.fullmatch(value) else None


def _declared_metadata_hash(summary: dict[str, Any]) -> str | None:
    hashes = summary.get("artifact_hashes")
    if not isinstance(hashes, dict):
        return None
    value = hashes.get("metadata_jsonl_sha256")
    return value if isinstance(value, str) and SHA256_PATTERN.fullmatch(value) else None


def _declared_capture_clock_hash(summary: dict[str, Any]) -> str | None:
    hashes = summary.get("artifact_hashes")
    if not isinstance(hashes, dict):
        return None
    value = hashes.get("capture_clock_sha256")
    return value if isinstance(value, str) and SHA256_PATTERN.fullmatch(value) else None


def _declared_capture_clock_origin_hash(summary: dict[str, Any]) -> str | None:
    hashes = summary.get("artifact_hashes")
    if not isinstance(hashes, dict):
        return None
    value = hashes.get("capture_clock_origin_sha256")
    return value if isinstance(value, str) and SHA256_PATTERN.fullmatch(value) else None


def _sha256_stable(path: Path) -> str:
    try:
        current = path.lstat()
    except OSError:
        return ""
    if not stat.S_ISREG(current.st_mode):
        return ""
    return _sha256_cached(str(path), _stat_identity(current))


@lru_cache(maxsize=128)
def _sha256_cached(
    path_text: str,
    expected_identity: tuple[int, int, int, int, int],
) -> str:
    path = Path(path_text)
    flags = os.O_RDONLY | getattr(os, "O_CLOEXEC", 0) | getattr(os, "O_NOFOLLOW", 0)
    try:
        descriptor = os.open(path, flags)
    except OSError:
        return ""
    try:
        before = os.fstat(descriptor)
        if not stat.S_ISREG(before.st_mode) or _stat_identity(before) != expected_identity:
            return ""
        digest = hashlib.sha256()
        while chunk := os.read(descriptor, 1024 * 1024):
            digest.update(chunk)
        after = os.fstat(descriptor)
        try:
            current = os.lstat(path)
        except OSError:
            return ""
        if _stat_identity(before) != _stat_identity(after) or (
            current.st_dev,
            current.st_ino,
        ) != (before.st_dev, before.st_ino):
            return ""
        return digest.hexdigest()
    finally:
        os.close(descriptor)


def _stat_identity(value: os.stat_result) -> tuple[int, int, int, int, int]:
    return (value.st_dev, value.st_ino, value.st_size, value.st_mtime_ns, value.st_ctime_ns)


def _active_session_root(root: Path) -> Path | None:
    marker = root / ".current_session"
    try:
        marker_stat = marker.lstat()
        if not stat.S_ISREG(marker_stat.st_mode) or marker_stat.st_size > 4096:
            return None
        value = marker.read_text(encoding="utf-8").strip()
    except (OSError, UnicodeDecodeError):
        return None
    if not value:
        return None
    raw = Path(value).expanduser()
    if raw.is_absolute():
        candidate = raw
    elif raw.parts and raw.parts[0] == root.name:
        candidate = root.parent / raw
    else:
        candidate = root / raw
    try:
        resolved = candidate.resolve(strict=True)
    except OSError:
        return None
    return resolved if resolved.is_dir() and resolved.is_relative_to(root) else None


def _modalities(topic_counts: Mapping[str, int]) -> tuple[SessionModality, ...]:
    topics = {
        topic
        for topic, count in topic_counts.items()
        if isinstance(topic, str) and _non_negative_int(count) > 0
    }
    result: list[SessionModality] = []
    if any(topic == "RealtimePointcloud" or topic.endswith("/lio_pcl") for topic in topics):
        result.append("point-cloud")
    if any(topic == "RealtimePath" or topic.endswith("/lio_pose") for topic in topics):
        result.append("trajectory")
    return tuple(result)


def _status(replayable: bool, error: object, stop_reason: object) -> SessionStatus:
    if error is None and stop_reason in {
        "duration_elapsed",
        "external_stop",
        "keyboard_interrupt",
    }:
        return "ready" if replayable else "failed"
    return "interrupted" if replayable else "failed"


def _discover_media_sources(session_root: Path) -> tuple[LegacyMediaSourceCandidate, ...]:
    media_root = session_root / "media"
    try:
        resolved_media_root = media_root.resolve(strict=True)
    except OSError:
        return ()
    if not resolved_media_root.is_dir() or not resolved_media_root.is_relative_to(session_root):
        return ()
    candidates: list[LegacyMediaSourceCandidate] = []
    for source_root in sorted(resolved_media_root.iterdir()):
        if not source_root.is_dir() or not MEDIA_SOURCE_PATTERN.fullmatch(source_root.name):
            continue
        try:
            resolved_source_root = source_root.resolve(strict=True)
        except OSError:
            continue
        if not resolved_source_root.is_relative_to(resolved_media_root):
            continue
        epochs = [
            epoch
            for epoch in sorted(resolved_source_root.iterdir())
            if epoch.is_dir()
            and MEDIA_EPOCH_PATTERN.fullmatch(epoch.name)
            and _validated_media_epoch(epoch, resolved_source_root.name)
        ]
        if not epochs:
            continue
        byte_length = sum(_media_epoch_bytes(epoch) for epoch in epochs)
        artifact_suffix = hashlib.sha256(resolved_source_root.name.encode()).hexdigest()[:16]
        candidates.append(
            LegacyMediaSourceCandidate(
                source_id=resolved_source_root.name,
                artifact_id=f"recorded-video-{artifact_suffix}",
                locator=resolved_source_root,
                byte_length=byte_length,
                epoch_count=len(epochs),
            )
        )
    return tuple(candidates)


def _validated_media_epoch(epoch: Path, expected_source_id: str) -> bool:
    try:
        resolved_epoch = epoch.resolve(strict=True)
        init_path = (epoch / "init.mp4").resolve(strict=True)
        segments_root = (epoch / "segments").resolve(strict=True)
        index_path = (epoch / "index.jsonl").resolve(strict=True)
        summary_path = (epoch / "summary.json").resolve(strict=True)
    except OSError:
        return False
    if not all(
        path.is_relative_to(resolved_epoch)
        for path in (init_path, segments_root, index_path, summary_path)
    ):
        return False
    if (
        not init_path.is_file()
        or init_path.stat().st_size <= 0
        or not segments_root.is_dir()
        or not index_path.is_file()
        or index_path.stat().st_size <= 0
        or not summary_path.is_file()
    ):
        return False
    summary = _read_json_object(summary_path)
    if summary.get("schema_version") not in {1, "missioncore.camera-recording/v1"}:
        return False
    if summary.get("source_id") != expected_source_id:
        return False
    segment_count = _non_negative_int(summary.get("segment_count"))
    if segment_count < 1:
        return False
    segment_sequences: set[int] = set()
    for path in segments_root.iterdir():
        match = MEDIA_SEGMENT_PATTERN.fullmatch(path.name)
        if match is None or not path.is_file():
            continue
        resolved = path.resolve()
        sequence = int(path.stem)
        if (
            resolved.parent != segments_root
            or path.name != f"{sequence}.m4s"
            or resolved.stat().st_size <= 0
            or sequence in segment_sequences
        ):
            return False
        segment_sequences.add(sequence)
    if (
        len(segment_sequences) != segment_count
        or min(segment_sequences, default=0) != 1
        or max(segment_sequences, default=0) != segment_count
    ):
        return False
    try:
        with index_path.open("r", encoding="utf-8") as stream:
            index_count = 0
            for index_count, line in enumerate(stream, start=1):
                record = json.loads(line)
                if not isinstance(record, dict) or record.get("sequence") != index_count:
                    return False
    except (OSError, UnicodeDecodeError, json.JSONDecodeError):
        return False
    return index_count == segment_count


def _media_epoch_bytes(epoch: Path) -> int:
    try:
        resolved_epoch = epoch.resolve(strict=True)
    except OSError:
        return 0
    total = 0
    for path in resolved_epoch.rglob("*"):
        try:
            resolved = path.resolve(strict=True)
        except OSError:
            continue
        if resolved.is_file() and resolved.is_relative_to(resolved_epoch):
            total += resolved.stat().st_size
    return total
