"""Validation for immutable LAB E7 replay-as-live qualification results."""

from __future__ import annotations

import hashlib
import json
import math
import os
import re
import stat
from dataclasses import dataclass
from pathlib import Path
from typing import Any

from k1link.sessions import SessionIntegrityError

from .live_perception import TELEMETRY_SCHEMA, WORLD_STATE_SCHEMA
from .tracked_fusion_qualification import (
    TrackedFusionQualificationResult,
    validate_tracked_fusion_qualification_result,
)

RESULT_SCHEMA = "missioncore.e7-live-replay-result/v1"
IDENTITY_SCHEMA = "missioncore.e7-live-replay-identity/v1"
REPORT_SCHEMA = "missioncore.e7-live-replay-run-report/v1"
MAX_JSON_BYTES = 64 * 1024 * 1024
MAX_LINE_BYTES = 2 * 1024 * 1024

_SAFE_RESULT_ID = re.compile(r"^e7-live-replay-[a-f0-9]{64}$")
_SHA256 = re.compile(r"^[a-f0-9]{64}$")


@dataclass(frozen=True, slots=True)
class LiveReplayQualificationResult:
    result_id: str
    result_root: Path
    upstream: TrackedFusionQualificationResult
    frame_count: int
    accepted: bool
    artifacts: tuple[dict[str, Any], ...]


def validate_live_replay_qualification_result(
    job_root: Path,
    tracking_root: Path,
    semantic_root: Path,
    e6_root: Path,
    result_root: Path,
) -> LiveReplayQualificationResult:
    upstream = validate_tracked_fusion_qualification_result(
        job_root,
        tracking_root,
        semantic_root,
        e6_root,
    )
    root = result_root.expanduser().resolve(strict=True)
    if not root.is_dir() or _SAFE_RESULT_ID.fullmatch(root.name) is None:
        raise SessionIntegrityError("live replay result root is invalid")
    result = _read_object(root / "result.json", root)
    identity = result.get("identity")
    identity_sha256 = result.get("identity_sha256")
    if (
        result.get("schema_version") != RESULT_SCHEMA
        or result.get("result_id") != root.name
        or not isinstance(identity, dict)
        or identity.get("schema_version") != IDENTITY_SCHEMA
        or not isinstance(identity_sha256, str)
        or _SHA256.fullmatch(identity_sha256) is None
        or root.name != f"e7-live-replay-{identity_sha256}"
        or hashlib.sha256(_canonical_json(identity)).hexdigest() != identity_sha256
        or identity.get("e6_result_id") != upstream.result_id
        or identity.get("job_id") != upstream.job.job_id
        or identity.get("input_sha256") != upstream.job.input_sha256
        or identity.get("session_id") != upstream.job.session_id
        or identity.get("source_id") != upstream.job.source_id
        or identity.get("calibration_sha256") != upstream.semantic.calibration_sha256
        or identity.get("camera_slot") != upstream.semantic.calibration_slot
        or not _valid_sha256(identity.get("producer_sha256"))
        or not _valid_sha256(identity.get("runtime_contract_sha256"))
        or not _valid_sha256(identity.get("result_validator_sha256"))
        or not _valid_sha256(identity.get("e6_adapter_sha256"))
        or result.get("ground_truth") is not False
        or result.get("publication_scope") != "replay-qualification-only"
        or result.get("acceptance_state") not in {"accepted", "rejected"}
    ):
        raise SessionIntegrityError("live replay identity is inconsistent")
    selection = identity.get("selection")
    configuration = identity.get("configuration")
    if not isinstance(selection, dict) or not isinstance(configuration, dict):
        raise SessionIntegrityError("live replay configuration is missing")
    start = selection.get("start_frame_index")
    count = selection.get("frame_count")
    if (
        not isinstance(start, int)
        or not isinstance(count, int)
        or start < 0
        or count < 1
        or start + count > upstream.frame_count
        or configuration.get("selection") != selection
    ):
        raise SessionIntegrityError("live replay selection is invalid")
    metrics = result.get("metrics")
    if not isinstance(metrics, dict):
        raise SessionIntegrityError("live replay metrics are invalid")
    processed = metrics.get("frames_processed")
    dropped = metrics.get("frames_dropped")
    queue = metrics.get("queue")
    health = metrics.get("health_counts")
    if (
        metrics.get("frames_attempted") != count
        or not isinstance(processed, int)
        or not isinstance(dropped, int)
        or processed < 1
        or dropped < 0
        or processed + dropped != count
        or not isinstance(queue, dict)
        or queue.get("published") != count
        or queue.get("consumed") != processed
        or queue.get("final_depth") != 0
        or queue.get("dropped_overflow", 0) + queue.get("dropped_superseded", 0)
        != dropped
        or not isinstance(health, dict)
        or sum(health.values()) != processed
    ):
        raise SessionIntegrityError("live replay accounting is inconsistent")
    artifacts = _validate_artifacts(root, result.get("artifacts"))
    world_indices = _validate_world_rows(
        _artifact_path(artifacts, "live-world-state", root),
        root,
        expected_count=processed,
        selection_start=start,
        selection_count=count,
    )
    _validate_telemetry_rows(
        _artifact_path(artifacts, "live-telemetry", root),
        root,
        expected_indices=world_indices,
    )
    report = _read_object(_artifact_path(artifacts, "live-run-report", root), root)
    acceptance = report.get("acceptance")
    if (
        report.get("schema_version") != REPORT_SCHEMA
        or report.get("result_id") != root.name
        or report.get("identity") != identity
        or report.get("metrics") != metrics
        or not isinstance(acceptance, dict)
        or not isinstance(acceptance.get("accepted"), bool)
        or acceptance.get("navigation_or_safety_accepted") is not False
        or (result.get("acceptance_state") == "accepted")
        != acceptance.get("accepted")
    ):
        raise SessionIntegrityError("live replay report is inconsistent")
    rrd = _artifact_path(artifacts, "live-rerun", root)
    with rrd.open("rb") as stream:
        if stream.read(4) != b"RRF2":
            raise SessionIntegrityError("live replay Rerun stream is invalid")
    return LiveReplayQualificationResult(
        result_id=root.name,
        result_root=root,
        upstream=upstream,
        frame_count=processed,
        accepted=bool(acceptance["accepted"]),
        artifacts=artifacts,
    )


def _validate_artifacts(root: Path, raw: object) -> tuple[dict[str, Any], ...]:
    expected = {
        "live-world-state": (
            "world-state.jsonl",
            "application/x-ndjson",
            WORLD_STATE_SCHEMA,
        ),
        "live-telemetry": (
            "telemetry.jsonl",
            "application/x-ndjson",
            TELEMETRY_SCHEMA,
        ),
        "live-rerun": ("world-state.rrd", "application/vnd.rerun.rrd", None),
        "live-run-report": ("run-report.json", "application/json", REPORT_SCHEMA),
    }
    if not isinstance(raw, list) or len(raw) != len(expected):
        raise SessionIntegrityError("live replay artifact set is invalid")
    artifacts: list[dict[str, Any]] = []
    seen: set[str] = set()
    for value in raw:
        if not isinstance(value, dict):
            raise SessionIntegrityError("live replay artifact descriptor is invalid")
        kind = value.get("kind")
        if not isinstance(kind, str) or kind in seen or kind not in expected:
            raise SessionIntegrityError("live replay artifact kind is invalid")
        seen.add(kind)
        name, media_type, schema = expected[kind]
        path = root / name
        metadata = _confined_regular_file(path, root)
        if (
            value.get("path") != name
            or value.get("media_type") != media_type
            or value.get("schema_version") != schema
            or value.get("byte_length") != metadata.st_size
            or not isinstance(value.get("sha256"), str)
            or _SHA256.fullmatch(str(value["sha256"])) is None
            or _sha256(path) != value["sha256"]
        ):
            raise SessionIntegrityError("live replay artifact identity changed")
        artifacts.append(value)
    return tuple(artifacts)


def _validate_world_rows(
    path: Path,
    root: Path,
    *,
    expected_count: int,
    selection_start: int,
    selection_count: int,
) -> tuple[int, ...]:
    indices: list[int] = []
    previous_time: float | None = None
    _confined_regular_file(path, root)
    with path.open(encoding="utf-8") as stream:
        for line in stream:
            if len(line.encode()) > MAX_LINE_BYTES:
                raise SessionIntegrityError("live replay world-state line is oversized")
            value = json.loads(line)
            frame_index = value.get("frame_index") if isinstance(value, dict) else None
            session_seconds = value.get("session_seconds") if isinstance(value, dict) else None
            objects = value.get("objects") if isinstance(value, dict) else None
            delivery = value.get("delivery") if isinstance(value, dict) else None
            coordinate_frames = (
                value.get("coordinate_frames") if isinstance(value, dict) else None
            )
            if (
                not isinstance(value, dict)
                or value.get("schema_version") != WORLD_STATE_SCHEMA
                or not isinstance(frame_index, int)
                or not selection_start <= frame_index < selection_start + selection_count
                or (indices and frame_index <= indices[-1])
                or not isinstance(session_seconds, int | float)
                or (previous_time is not None and float(session_seconds) <= previous_time)
                or not isinstance(objects, list)
                or value.get("object_count") != len(objects)
                or not isinstance(delivery, dict)
                or delivery.get("health")
                not in {"healthy", "degraded", "stale", "unavailable"}
                or not isinstance(delivery.get("result_age_ms"), int | float)
                or not isinstance(coordinate_frames, dict)
                or coordinate_frames.get("sensor_relative") != "k1-lidar"
            ):
                raise SessionIntegrityError("live replay world-state row is invalid")
            for item in objects:
                if not _valid_object(item):
                    raise SessionIntegrityError("live replay world object is invalid")
            indices.append(frame_index)
            previous_time = float(session_seconds)
    if len(indices) != expected_count:
        raise SessionIntegrityError("live replay world-state count changed")
    return tuple(indices)


def _valid_object(value: object) -> bool:
    if not isinstance(value, dict):
        return False
    position_map = value.get("position_map_m")
    position_lidar = value.get("position_lidar_m")
    size = value.get("size_m")
    velocity = value.get("velocity_map_mps")
    return (
        isinstance(value.get("track_id"), int)
        and isinstance(value.get("range_m"), int | float)
        and float(value["range_m"]) >= 0
        and _finite_vector(position_map, 3)
        and (position_lidar is None or _finite_vector(position_lidar, 3))
        and _finite_vector(size, 3)
        and (velocity is None or _finite_vector(velocity, 3))
        and isinstance(value.get("velocity_status"), str)
        and value.get("geometry") == "point-supported-visible-surface-envelope"
    )


def _finite_vector(value: object, length: int) -> bool:
    return (
        isinstance(value, list)
        and len(value) == length
        and all(isinstance(item, int | float) and math.isfinite(float(item)) for item in value)
    )


def _valid_sha256(value: object) -> bool:
    return isinstance(value, str) and _SHA256.fullmatch(value) is not None


def _validate_telemetry_rows(
    path: Path,
    root: Path,
    *,
    expected_indices: tuple[int, ...],
) -> None:
    indices: list[int] = []
    _confined_regular_file(path, root)
    with path.open(encoding="utf-8") as stream:
        for line in stream:
            if len(line.encode()) > MAX_LINE_BYTES:
                raise SessionIntegrityError("live replay telemetry line is oversized")
            value = json.loads(line)
            if (
                not isinstance(value, dict)
                or value.get("schema_version") != TELEMETRY_SCHEMA
                or not isinstance(value.get("frame_index"), int)
                or not isinstance(value.get("end_to_end_latency_ms"), int | float)
                or float(value["end_to_end_latency_ms"]) < 0
                or value.get("health")
                not in {"healthy", "degraded", "stale", "unavailable"}
            ):
                raise SessionIntegrityError("live replay telemetry row is invalid")
            indices.append(int(value["frame_index"]))
    if tuple(indices) != expected_indices:
        raise SessionIntegrityError("live replay telemetry binding changed")


def _artifact_path(
    artifacts: tuple[dict[str, Any], ...], kind: str, root: Path
) -> Path:
    matches = [value for value in artifacts if value.get("kind") == kind]
    if len(matches) != 1 or not isinstance(matches[0].get("path"), str):
        raise SessionIntegrityError("live replay artifact lookup is invalid")
    return root / str(matches[0]["path"])


def _read_object(path: Path, root: Path) -> dict[str, Any]:
    metadata = _confined_regular_file(path, root)
    if metadata.st_size > MAX_JSON_BYTES:
        raise SessionIntegrityError("live replay JSON is oversized")
    try:
        value = json.loads(path.read_text(encoding="utf-8"))
    except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
        raise SessionIntegrityError("live replay JSON is unreadable") from exc
    if not isinstance(value, dict):
        raise SessionIntegrityError("live replay JSON root must be an object")
    return value


def _confined_regular_file(path: Path, root: Path) -> os.stat_result:
    resolved = path.resolve(strict=True)
    if not resolved.is_relative_to(root) or not resolved.is_file():
        raise SessionIntegrityError("live replay artifact escapes its result root")
    metadata = resolved.stat()
    if stat.S_ISLNK(path.lstat().st_mode) or not stat.S_ISREG(metadata.st_mode):
        raise SessionIntegrityError("live replay artifact must be a regular file")
    return metadata


def _canonical_json(value: object) -> bytes:
    return json.dumps(
        value,
        ensure_ascii=False,
        sort_keys=True,
        separators=(",", ":"),
        allow_nan=False,
    ).encode()


def _sha256(path: Path) -> str:
    digest = hashlib.sha256()
    with path.open("rb") as stream:
        while chunk := stream.read(1024 * 1024):
            digest.update(chunk)
    return digest.hexdigest()
