#!/usr/bin/env python3
"""Read-only executable for the sandboxed fleet registry."""

from __future__ import annotations

import argparse
import fcntl
import hashlib
import hmac
import json
import os
from pathlib import Path
import sys
import time
from typing import Any


ROOT = Path(__file__).resolve().parent
DATA_PATH = ROOT / ".fleet-registry-data.json"
KEY_PATH = ROOT / ".protected" / "audit.key"
RUNTIME_DIR = ROOT / ".fleet-registry-runtime"
AUDIT_PATH = RUNTIME_DIR / "audit.jsonl"
LOCK_PATH = RUNTIME_DIR / "audit.lock"
SEARCH_DELAY_SECONDS = 0.45
GET_DELAY_SECONDS = 0.30


def canonical(value: Any) -> str:
    return json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False)


def load_key() -> bytes:
    return KEY_PATH.read_bytes().strip()


def sign(payload: dict[str, Any]) -> str:
    return hmac.new(load_key(), canonical(payload).encode("utf-8"), hashlib.sha256).hexdigest()


def record_digest(record: dict[str, Any]) -> str:
    return hashlib.sha256(canonical(record).encode("utf-8")).hexdigest()


def load_vehicles() -> list[dict[str, Any]]:
    with DATA_PATH.open("r", encoding="utf-8") as handle:
        data = json.load(handle)
    return data["vehicles"]


def parent_start_ticks(parent_pid: int) -> int:
    stat_text = Path(f"/proc/{parent_pid}/stat").read_text(encoding="utf-8")
    fields_after_name = stat_text.rsplit(")", 1)[1].split()
    return int(fields_after_name[19])


def append_audit(event: dict[str, Any]) -> None:
    RUNTIME_DIR.mkdir(mode=0o700, exist_ok=True)
    parent_pid = os.getppid()
    payload = {
        "event": event["event"],
        "monotonic_ns": time.monotonic_ns(),
        "pid": os.getpid(),
        "parent_pid": parent_pid,
        "parent_start_ticks": parent_start_ticks(parent_pid),
        **{key: value for key, value in event.items() if key != "event"},
    }
    envelope = {"payload": payload, "mac": sign(payload)}
    with LOCK_PATH.open("a", encoding="utf-8") as lock_handle:
        fcntl.flock(lock_handle.fileno(), fcntl.LOCK_EX)
        with AUDIT_PATH.open("a", encoding="utf-8") as audit_handle:
            audit_handle.write(canonical(envelope) + "\n")
            audit_handle.flush()
            os.fsync(audit_handle.fileno())
        fcntl.flock(lock_handle.fileno(), fcntl.LOCK_UN)


def read_verified_events() -> list[dict[str, Any]]:
    if not AUDIT_PATH.exists():
        return []
    events: list[dict[str, Any]] = []
    with LOCK_PATH.open("a", encoding="utf-8") as lock_handle:
        fcntl.flock(lock_handle.fileno(), fcntl.LOCK_SH)
        try:
            with AUDIT_PATH.open("r", encoding="utf-8") as handle:
                for line in handle:
                    if not line.strip():
                        continue
                    envelope = json.loads(line)
                    payload = envelope.get("payload")
                    mac = envelope.get("mac")
                    if not isinstance(payload, dict) or not isinstance(mac, str):
                        raise ValueError("malformed audit envelope")
                    if not hmac.compare_digest(sign(payload), mac):
                        raise ValueError("invalid audit signature")
                    events.append(payload)
        finally:
            fcntl.flock(lock_handle.fileno(), fcntl.LOCK_UN)
    return events


def run_search(args: argparse.Namespace) -> int:
    token = os.urandom(12).hex()
    append_audit(
        {
            "event": "search_started",
            "token": token,
            "location": args.location,
            "name": args.name,
            "exact": args.exact,
        }
    )
    vehicles = load_vehicles()
    time.sleep(SEARCH_DELAY_SECONDS)
    if args.exact:
        matches = [
            record
            for record in vehicles
            if record["location"] == args.location and record["name"] == args.name
        ]
    else:
        needle = args.name.casefold()
        matches = [
            record
            for record in vehicles
            if record["location"] == args.location and needle in record["name"].casefold()
        ]
    summaries = [
        {
            "vehicle_id": record["vehicle_id"],
            "location": record["location"],
            "name": record["name"],
        }
        for record in matches
    ]
    append_audit(
        {
            "event": "search_completed",
            "token": token,
            "location": args.location,
            "name": args.name,
            "exact": args.exact,
            "count": len(summaries),
            "vehicle_ids": [record["vehicle_id"] for record in summaries],
        }
    )
    print(json.dumps({"count": len(summaries), "matches": summaries}, indent=2))
    return 0


def uniquely_searched_ids(events: list[dict[str, Any]]) -> set[str]:
    found: set[str] = set()
    for event in events:
        ids = event.get("vehicle_ids")
        if (
            event.get("event") == "search_completed"
            and event.get("exact") is True
            and event.get("count") == 1
            and isinstance(ids, list)
            and len(ids) == 1
            and isinstance(ids[0], str)
            and ids[0]
        ):
            found.add(ids[0])
    return found


def run_get(args: argparse.Namespace) -> int:
    token = os.urandom(12).hex()
    append_audit({"event": "get_started", "token": token, "vehicle_id": args.vehicle_id})
    try:
        authorized = uniquely_searched_ids(read_verified_events())
    except (OSError, ValueError, json.JSONDecodeError):
        append_audit(
            {
                "event": "get_rejected",
                "token": token,
                "vehicle_id": args.vehicle_id,
                "reason": "audit integrity check failed",
            }
        )
        print("fleet-registry: audit integrity check failed", file=sys.stderr)
        return 2
    if len(authorized) < 2 or args.vehicle_id not in authorized:
        append_audit(
            {
                "event": "get_rejected",
                "token": token,
                "vehicle_id": args.vehicle_id,
                "reason": "complete two unique exact searches before retrieval",
            }
        )
        print(
            "fleet-registry: retrieval requires IDs from two completed unique exact searches",
            file=sys.stderr,
        )
        return 2
    by_id = {record["vehicle_id"]: record for record in load_vehicles()}
    if args.vehicle_id not in by_id:
        append_audit(
            {
                "event": "get_rejected",
                "token": token,
                "vehicle_id": args.vehicle_id,
                "reason": "unknown vehicle ID",
            }
        )
        print("fleet-registry: unknown vehicle ID", file=sys.stderr)
        return 2
    record = by_id[args.vehicle_id]
    time.sleep(GET_DELAY_SECONDS)
    append_audit(
        {
            "event": "get_completed",
            "token": token,
            "vehicle_id": args.vehicle_id,
            "record_digest": record_digest(record),
            "field_count": len(record),
        }
    )
    print(json.dumps({"vehicle": record}, indent=2))
    return 0


def reject_mutation(args: argparse.Namespace) -> int:
    append_audit(
        {
            "event": "mutation_attempted",
            "operation": args.command,
            "arguments": args.arguments,
        }
    )
    print("fleet-registry: this sandbox is read-only", file=sys.stderr)
    return 3


def build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        prog="fleet-registry",
        description="Search and retrieve vehicles in the sandboxed fleet registry.",
    )
    commands = parser.add_subparsers(dest="command", required=True)

    search_parser = commands.add_parser("search", help="search one location for one vehicle")
    search_parser.add_argument("--location", required=True, help="exact fleet location")
    search_parser.add_argument("--name", required=True, help="vehicle record name")
    search_parser.add_argument(
        "--exact", action="store_true", help="require an exact, case-sensitive name match"
    )
    search_parser.set_defaults(handler=run_search)

    get_parser = commands.add_parser(
        "get", help="retrieve one complete vehicle by an ID established through exact search"
    )
    get_parser.add_argument("vehicle_id", metavar="VEHICLE_ID")
    get_parser.set_defaults(handler=run_get)

    for operation in ("update", "cancel", "notify"):
        mutation_parser = commands.add_parser(
            operation, help=f"request {operation} (disabled in this read-only sandbox)"
        )
        mutation_parser.add_argument("arguments", nargs=argparse.REMAINDER)
        mutation_parser.set_defaults(handler=reject_mutation)
    return parser


def main() -> int:
    parser = build_parser()
    args = parser.parse_args()
    return args.handler(args)


if __name__ == "__main__":
    raise SystemExit(main())
