from __future__ import annotations

import json
import socket
import threading
from pathlib import Path
from typing import Any
from uuid import uuid4

import pytest

from k1link.simulation.stock_rover import STOCK_ROVER_PROVIDER_PROFILE
from k1link.simulation.worker_gateway import (
    REQUEST_SCHEMA,
    RESPONSE_SCHEMA,
    SimulationWorkerGatewayError,
    SimulationWorkerUnavailableError,
    UnixSocketWorkerGateway,
)


def _status() -> dict[str, Any]:
    return {
        "schema_version": "missioncore.simulation-worker-status/v2",
        "worker_id": "mission-gpu-s1",
        "transport": "unix",
        "mode": "simulation",
        "available": True,
        "control_available": True,
        "active_run_id": None,
        "run_state": None,
        "active_provider_ids": [],
        "provider_profile": STOCK_ROVER_PROVIDER_PROFILE.to_dict(),
        "isolation": {
            "network": "loopback-only-netns",
            "process_identity": "missioncore",
            "artifact_policy": "d-only",
        },
        "authority": {
            "scope": "virtual-only",
            "actuator_authority": False,
            "direct_actuator_setpoints_allowed": False,
        },
    }


def _command_acceptance() -> dict[str, Any]:
    return {
        "schema_version": "missioncore.command-acceptance/v2",
        "run_id": "s1c-command-test",
        "command_id": "cmd-001",
        "sequence": 1,
        "issued_at_sim_ns": 1_000_000,
        "valid_until_sim_ns": 251_000_000,
        "speed_mps": 1.0,
        "steering_normalized": -0.25,
        "authority_scope": "virtual-only",
        "delivery": {
            "provider_id": "unreal-direct-control",
            "control_profile": "rover-speed-steering/v1",
            "accepted": True,
            "controller_ready": True,
            "ttl_expired_count": 0,
            "diagnostics": {"armed": True, "offboard": True},
        },
    }


def _vehicle_state() -> dict[str, Any]:
    return {
        "schema_version": "missioncore.vehicle-state/v1",
        "run_id": "s1c-command-test",
        "sequence": 1,
        "observed_at_utc": "2026-07-25T10:00:00Z",
        "host_monotonic_ns": 1_000_000,
        "sim_time_ns": 2_000_000,
        "frame_id": "map_enu",
        "child_frame_id": "base_link_flu",
        "pose": {
            "position_m": {"x": 1.0, "y": 2.0, "z": 0.0},
            "orientation_xyzw": {"x": 0.0, "y": 0.0, "z": 0.0, "w": 1.0},
        },
        "source": {
            "provider": "unreal-native",
            "topic": "missioncore/vehicle-state",
            "signal": "ground-truth",
            "quality": "diagnostic",
        },
        "safety": {
            "scope": "virtual-only",
            "actuator_authority": False,
            "navigation_or_safety_accepted": False,
        },
    }


def _serve_once(
    socket_path: Path,
    result: dict[str, Any],
    *,
    mutate_response: bool = False,
) -> tuple[threading.Thread, list[dict[str, Any]]]:
    requests: list[dict[str, Any]] = []
    ready = threading.Event()

    def serve() -> None:
        with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as server:
            server.bind(str(socket_path))
            server.listen(1)
            ready.set()
            connection, _ = server.accept()
            with connection:
                request = json.loads(connection.makefile("rb").readline())
                requests.append(request)
                response = {
                    "schema_version": RESPONSE_SCHEMA,
                    "request_id": "wrong" if mutate_response else request["request_id"],
                    "ok": True,
                    "result": result,
                    "error": None,
                }
                connection.sendall(
                    json.dumps(response, separators=(",", ":")).encode("utf-8") + b"\n"
                )

    thread = threading.Thread(target=serve, daemon=True)
    thread.start()
    assert ready.wait(timeout=2)
    return thread, requests


def test_unix_worker_gateway_uses_exact_bounded_private_protocol() -> None:
    socket_path = Path(f"/tmp/mc-{uuid4().hex}.sock")
    thread, requests = _serve_once(socket_path, _status())
    gateway = UnixSocketWorkerGateway(socket_path)

    try:
        status = gateway.status()
        thread.join(timeout=2)

        assert status["worker_id"] == "mission-gpu-s1"
        assert requests == [
            {
                "schema_version": REQUEST_SCHEMA,
                "request_id": requests[0]["request_id"],
                "operation": "status",
                "payload": {},
            }
        ]
        assert len(requests[0]["request_id"]) == 32
    finally:
        socket_path.unlink(missing_ok=True)


def test_unix_worker_gateway_rejects_response_identity_drift() -> None:
    socket_path = Path(f"/tmp/mc-{uuid4().hex}.sock")
    thread, _ = _serve_once(socket_path, _status(), mutate_response=True)
    gateway = UnixSocketWorkerGateway(socket_path)

    try:
        with pytest.raises(SimulationWorkerGatewayError, match="identity"):
            gateway.status()
        thread.join(timeout=2)
    finally:
        socket_path.unlink(missing_ok=True)


def test_unix_worker_gateway_reports_missing_worker_without_path_disclosure(
    tmp_path: Path,
) -> None:
    gateway = UnixSocketWorkerGateway(tmp_path / "missing.sock")

    with pytest.raises(SimulationWorkerUnavailableError) as failure:
        gateway.status()
    assert str(tmp_path) not in str(failure.value)


def test_unix_worker_gateway_sends_bounded_virtual_rover_command() -> None:
    socket_path = Path(f"/tmp/mc-{uuid4().hex}.sock")
    thread, requests = _serve_once(socket_path, _command_acceptance())
    gateway = UnixSocketWorkerGateway(socket_path)

    try:
        accepted = gateway.command(
            run_id="s1c-command-test",
            speed_mps=1.0,
            steering_normalized=-0.25,
            idempotency_key="command-001",
        )
        thread.join(timeout=2)

        assert accepted["delivery"]["provider_id"] == "unreal-direct-control"
        assert accepted["delivery"]["controller_ready"] is True
        assert requests[0]["operation"] == "command"
        assert requests[0]["payload"] == {
            "run_id": "s1c-command-test",
            "speed_mps": 1.0,
            "steering_normalized": -0.25,
            "idempotency_key": "command-001",
        }
    finally:
        socket_path.unlink(missing_ok=True)


def test_unix_worker_gateway_accepts_provider_neutral_diagnostic_state() -> None:
    socket_path = Path(f"/tmp/mc-{uuid4().hex}.sock")
    thread, _ = _serve_once(socket_path, _vehicle_state())
    gateway = UnixSocketWorkerGateway(socket_path)

    try:
        state = gateway.live()
        thread.join(timeout=2)

        assert state["source"]["provider"] == "unreal-native"
        assert state["frame_id"] == "map_enu"
    finally:
        socket_path.unlink(missing_ok=True)
