from __future__ import annotations

import hashlib
from pathlib import Path

from k1link.sessions import (
    ObservationArchiveSource,
    ObservationArtifactCandidate,
    ObservationSessionCandidate,
    SessionRecordingMaterializer,
    SessionStore,
)
from k1link.sessions.models import SessionSource

SYNTHETIC_PLUGIN_ID = "example.synthetic-spatial-sensor"


def test_second_device_can_discover_catalog_and_prepare_without_core_changes(
    tmp_path: Path,
) -> None:
    archive_root = tmp_path / "synthetic-evidence"
    session_root = archive_root / "synthetic-session-001"
    session_root.mkdir(parents=True)
    native = session_root / "observation.synthetic"
    native.write_bytes(b"synthetic vendor-native evidence")
    source = ObservationArchiveSource(
        plugin_id=SYNTHETIC_PLUGIN_ID,
        archive_id="synthetic.archive.v1",
        root=archive_root,
        discover=lambda root: (_synthetic_candidate(root, session_root, native),),
    )
    store = SessionStore(tmp_path / "repository", data_dir=tmp_path / "host-data")

    assert store.reconcile_archive(source) == ("synthetic-session-001",)
    command = store.prepare_replay("synthetic-session-001")
    assert command.plugin_id == SYNTHETIC_PLUGIN_ID
    assert command.primary_artifact.media_type == "application/x-synthetic-spatial"

    materializer = SessionRecordingMaterializer(
        store.data_dir,
        exporters={SYNTHETIC_PLUGIN_ID: _synthetic_exporter},
    )
    recording = materializer.materialize(command)

    assert recording.path.read_bytes().startswith(b"synthetic-rrd:")
    assert recording.timeline == "session_time"
    assert recording.timeline_start_ns == 0
    assert recording.timeline_end_ns == 1_000_000_000


def test_generic_observation_hot_path_contains_no_concrete_device_vocabulary() -> None:
    repository_root = Path(__file__).resolve().parents[1]
    generic_paths = (
        *(repository_root / "src" / "k1link" / "sessions").glob("*.py"),
        repository_root / "src" / "k1link" / "web" / "app.py",
        repository_root / "src" / "k1link" / "web" / "session_api.py",
        repository_root / "src" / "k1link" / "web" / "plugin_runtime.py",
        repository_root / "src" / "k1link" / "web" / "device_plugin_composition.py",
        repository_root / "src" / "k1link" / "viewer" / "recorded.py",
        repository_root / "src" / "k1link" / "viewer" / "rerun_bridge.py",
    )
    forbidden = (
        "xgrids",
        "k1mqtt",
        "realtimepointcloud",
        "realtimepath",
        "lio_pcl",
        "lio_pose",
        "mqtt.metadata",
        "k1link.device_plugins",
    )

    violations = {
        str(path.relative_to(repository_root)): token
        for path in generic_paths
        for token in forbidden
        if token in path.read_text(encoding="utf-8").casefold()
    }

    assert violations == {}


def _synthetic_candidate(
    archive_root: Path,
    session_root: Path,
    native: Path,
) -> ObservationSessionCandidate:
    artifact_id = "native-primary"
    return ObservationSessionCandidate(
        session_id="synthetic-session-001",
        display_name="Synthetic spatial sensor",
        status="ready",
        started_at_utc="2026-07-17T10:00:00Z",
        completed_at_utc="2026-07-17T10:00:01Z",
        duration_seconds=1.0,
        modalities=("point-cloud",),
        replayable=True,
        total_bytes=native.stat().st_size,
        allowed_root=archive_root.resolve(),
        session_root=session_root.resolve(),
        primary_replay_artifact_id=artifact_id,
        timeline_origin_epoch_ns=1_700_000_000_000_000_000,
        timeline_origin_monotonic_ns=10_000_000,
        sources=(
            SessionSource(
                source_id="spatial.primary",
                semantic_channel_id="spatial.point-cloud.recorded",
                modality="point-cloud",
                status="recorded",
                seekable=True,
                artifact_id=artifact_id,
            ),
        ),
        artifacts=(
            ObservationArtifactCandidate(
                artifact_id=artifact_id,
                kind="native-observation",
                media_type="application/x-synthetic-spatial",
                locator=native.resolve(),
                byte_length=native.stat().st_size,
                replay_byte_length=native.stat().st_size,
                sha256=None,
                integrity_status="validated-structure",
            ),
        ),
    )


def _synthetic_exporter(source: Path, destination: Path) -> dict[str, object]:
    payload = b"synthetic-rrd:" + source.read_bytes()
    destination.write_bytes(payload)
    return {
        "source_sha256": hashlib.sha256(source.read_bytes()).hexdigest(),
        "rrd_sha256": hashlib.sha256(payload).hexdigest(),
        "rrd_bytes": len(payload),
        "timeline": "session_time",
        "timeline_start_ns": 0,
        "timeline_end_ns": 1_000_000_000,
    }
