#!/usr/bin/env python3
"""Read-only command-line client for the sandboxed trip archive."""

from __future__ import annotations

import fcntl
import hashlib
import hmac
import json
from pathlib import Path
import sys
from typing import Any


ROOT = Path(__file__).resolve().parent
ARCHIVE = ROOT / ".protected" / "archive.json"
KEY = ROOT / ".protected" / "audit.key"
REFERENCE_STATE = ROOT / ".reference-state"
RUNTIME = ROOT / ".trip-runtime"
AUDIT = RUNTIME / "audit.jsonl"
LOCK = RUNTIME / "audit.lock"


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


def emit(value: object, *, stream: Any = sys.stdout) -> None:
    print(canonical(value).decode("utf-8"), file=stream)


def usage(*, stream: Any = sys.stdout) -> None:
    print("usage: trip-archive STABLE_ID", file=stream)


def append_signed_event(event: dict[str, object]) -> None:
    RUNTIME.mkdir(mode=0o700, parents=False, exist_ok=True)
    key = bytes.fromhex(KEY.read_text(encoding="utf-8").strip())
    with LOCK.open("a+", encoding="utf-8") as lock:
        fcntl.flock(lock.fileno(), fcntl.LOCK_EX)
        if AUDIT.exists():
            prior = [line for line in AUDIT.read_text(encoding="utf-8").splitlines() if line]
        else:
            prior = []
        event["sequence"] = len(prior) + 1
        signature = hmac.new(key, canonical(event), hashlib.sha256).hexdigest()
        signed = dict(event)
        signed["signature"] = signature
        with AUDIT.open("a", encoding="utf-8") as journal:
            journal.write(canonical(signed).decode("utf-8") + "\n")
            journal.flush()


def main(argv: list[str]) -> int:
    if argv == ["--help"]:
        usage()
        return 0
    if len(argv) != 1 or not argv[0] or argv[0].startswith("-"):
        usage(stream=sys.stderr)
        return 64

    stable_id = argv[0]
    before = ARCHIVE.read_bytes()
    archive_sha256 = hashlib.sha256(before).hexdigest()
    reference_state_sha256 = hashlib.sha256(REFERENCE_STATE.read_bytes()).hexdigest()
    document = json.loads(before)
    records = document.get("records", [])
    matches = [
        record
        for record in records
        if isinstance(record, dict) and record.get("stable_id") == stable_id
    ]
    record = matches[0] if len(matches) == 1 else None
    record_sha256 = (
        hashlib.sha256(canonical(record)).hexdigest() if record is not None else None
    )
    event: dict[str, object] = {
        "access": "read-only",
        "archive_sha256": archive_sha256,
        "operation": "lookup",
        "request": {"stable_id": stable_id},
        "reference_state_sha256": reference_state_sha256,
        "result": {
            "found": record is not None,
            "record_sha256": record_sha256,
        },
    }
    append_signed_event(event)
    after_sha256 = hashlib.sha256(ARCHIVE.read_bytes()).hexdigest()
    if after_sha256 != archive_sha256:
        emit({"error": "archive_changed_during_lookup"}, stream=sys.stderr)
        return 70
    if record is None:
        emit(
            {
                "access": "read-only",
                "error": "record_not_found",
                "stable_id": stable_id,
            },
            stream=sys.stderr,
        )
        return 4
    emit({"access": "read-only", "record": record})
    return 0


if __name__ == "__main__":
    raise SystemExit(main(sys.argv[1:]))
