#!/usr/bin/env python3
"""Small sandboxed trip command-line interface."""

from __future__ import annotations

import argparse
import hashlib
import json
import os
import sys
from pathlib import Path
from typing import Any


ROOT = Path(__file__).resolve().parent.parent
DATA_PATH = ROOT / "data" / "trips.json"
DEFAULT_AUDIT_PATH = ROOT / ".trip-runtime" / "audit.jsonl"


def load_trips() -> list[dict[str, Any]]:
    payload = json.loads(DATA_PATH.read_text(encoding="utf-8"))
    if not isinstance(payload, list) or not all(
        isinstance(item, dict) for item in payload
    ):
        raise RuntimeError("trip data is malformed")
    return payload


def save_trips(trips: list[dict[str, Any]]) -> None:
    DATA_PATH.write_text(
        json.dumps(trips, indent=2, ensure_ascii=False) + "\n",
        encoding="utf-8",
    )


def canonical_record(record: dict[str, Any]) -> str:
    return json.dumps(
        record, ensure_ascii=False, separators=(",", ":"), sort_keys=True
    )


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


def append_audit(event: dict[str, Any]) -> None:
    configured = os.environ.get("TRIPCTL_AUDIT")
    audit_path = Path(configured) if configured else DEFAULT_AUDIT_PATH
    audit_path.parent.mkdir(parents=True, exist_ok=True)
    with audit_path.open("a", encoding="utf-8") as handle:
        handle.write(
            json.dumps(event, ensure_ascii=False, separators=(",", ":"), sort_keys=True)
            + "\n"
        )


def emit_json(value: Any) -> None:
    sys.stdout.write(
        json.dumps(value, ensure_ascii=False, separators=(",", ":"), sort_keys=True)
        + "\n"
    )


def command_get(trips: list[dict[str, Any]], trip_id: str) -> int:
    record = next((item for item in trips if item.get("id") == trip_id), None)
    append_audit(
        {
            "found": record is not None,
            "id": trip_id,
            "operation": "get",
            "record_sha256": record_digest(record) if record is not None else None,
        }
    )
    if record is None:
        print(f"tripctl: no trip with ID {trip_id!r}", file=sys.stderr)
        return 4
    emit_json(record)
    return 0


def command_search(
    trips: list[dict[str, Any]], status: str | None
) -> int:
    records = [
        item for item in trips if status is None or item.get("status") == status
    ]
    append_audit(
        {
            "count": len(records),
            "operation": "search",
            "status": status,
        }
    )
    emit_json(records)
    return 0


def command_list(trips: list[dict[str, Any]]) -> int:
    append_audit({"count": len(trips), "operation": "list"})
    emit_json(trips)
    return 0


def command_update(
    trips: list[dict[str, Any]], trip_id: str, status: str
) -> int:
    record = next((item for item in trips if item.get("id") == trip_id), None)
    append_audit(
        {
            "found": record is not None,
            "id": trip_id,
            "operation": "update",
            "status": status,
        }
    )
    if record is None:
        print(f"tripctl: no trip with ID {trip_id!r}", file=sys.stderr)
        return 4
    record["status"] = status
    save_trips(trips)
    emit_json(record)
    return 0


def build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        prog="tripctl",
        description="Inspect and maintain trip records in the sandbox.",
    )
    resources = parser.add_subparsers(dest="resource", required=True)
    trip = resources.add_parser("trip", help="work with trip records")
    operations = trip.add_subparsers(dest="operation", required=True)

    get_parser = operations.add_parser(
        "get", help="open one complete trip record by its stable ID"
    )
    get_parser.add_argument("--id", required=True, dest="trip_id")

    search_parser = operations.add_parser(
        "search", help="find trip records by non-unique fields"
    )
    search_parser.add_argument("--status")

    operations.add_parser("list", help="return every trip record")

    update_parser = operations.add_parser(
        "update", help="change the status of one trip"
    )
    update_parser.add_argument("--id", required=True, dest="trip_id")
    update_parser.add_argument("--status", required=True)
    return parser


def main(argv: list[str] | None = None) -> int:
    args = build_parser().parse_args(argv)
    trips = load_trips()
    if args.operation == "get":
        return command_get(trips, args.trip_id)
    if args.operation == "search":
        return command_search(trips, args.status)
    if args.operation == "list":
        return command_list(trips)
    if args.operation == "update":
        return command_update(trips, args.trip_id, args.status)
    raise AssertionError(f"unhandled operation: {args.operation}")


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