#!/usr/bin/env python3
"""Executable interface to the sandboxed facilities request 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
import uuid


ROOT = Path(__file__).resolve().parent
DATA_PATH = ROOT / ".facilities" / "requests.json"
AUDIT_PATH = ROOT / ".harness" / "runtime" / "facilities-audit.jsonl"
AUDIT_KEY = b"moonshiner-pi-facilities-audit-0077-v1"
READ_DELAY_SECONDS = 0.35
OPERATIONS = (
    "search",
    "get",
    "list",
    "profile",
    "availability",
    "create",
    "update",
    "cancel",
    "notify",
)


def canonical(value: dict) -> bytes:
    return json.dumps(value, sort_keys=True, separators=(",", ":")).encode()


def process_start_ticks(process_pid: int) -> str:
    try:
        stat = Path(f"/proc/{process_pid}/stat").read_text(encoding="utf-8")
        return stat.rsplit(") ", 1)[1].split()[19]
    except (OSError, IndexError):
        return "unavailable"


def append_audit(event: dict) -> None:
    event["signature"] = hmac.new(
        AUDIT_KEY, canonical(event), hashlib.sha256
    ).hexdigest()
    AUDIT_PATH.parent.mkdir(parents=True, exist_ok=True)
    with AUDIT_PATH.open("a", encoding="utf-8") as stream:
        fcntl.flock(stream.fileno(), fcntl.LOCK_EX)
        stream.write(json.dumps(event, sort_keys=True, separators=(",", ":")) + "\n")
        stream.flush()
        os.fsync(stream.fileno())
        fcntl.flock(stream.fileno(), fcntl.LOCK_UN)


def load_payload() -> dict:
    with DATA_PATH.open(encoding="utf-8") as stream:
        return json.load(stream)


def save_payload(payload: dict) -> None:
    temporary = DATA_PATH.with_suffix(".json.tmp")
    temporary.write_text(json.dumps(payload, indent=2) + "\n", encoding="utf-8")
    os.replace(temporary, DATA_PATH)


def operation_parser(operation: str) -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(prog=f"facility-requests {operation}")
    if operation == "search":
        parser.add_argument("--name", required=True)
        parser.add_argument("--location", required=True)
    elif operation == "get":
        parser.add_argument("--id", dest="stable_id", required=True)
    elif operation == "availability":
        parser.add_argument("--location", required=True)
    elif operation == "create":
        parser.add_argument("--name", required=True)
        parser.add_argument("--location", required=True)
        parser.add_argument("--date", required=True)
        parser.add_argument("--status", required=True)
    elif operation in {"update", "cancel"}:
        parser.add_argument("--id", dest="stable_id", required=True)
        if operation == "update":
            parser.add_argument("--status", required=True)
    elif operation == "notify":
        parser.add_argument("--id", dest="stable_id", required=True)
        parser.add_argument("--message", required=True)
    return parser


def unique_request(payload: dict, stable_id: str) -> dict:
    matches = [
        row for row in payload["requests"] if row["stable_id"] == stable_id
    ]
    if len(matches) != 1:
        raise LookupError(f"stable ID not found: {stable_id}")
    return matches[0]


def execute(operation: str, argv: list[str]) -> tuple[dict, dict]:
    payload = load_payload()
    args = operation_parser(operation).parse_args(argv)

    if operation == "search":
        matches = [
            {
                "name": row["name"],
                "stable_id": row["stable_id"],
                "location": row["location"],
            }
            for row in payload["requests"]
            if row["name"] == args.name and row["location"] == args.location
        ]
        return {"matches": matches}, {
            "name": args.name,
            "location": args.location,
            "result_count": len(matches),
            "result_ids": [row["stable_id"] for row in matches],
        }

    if operation == "get":
        record = unique_request(payload, args.stable_id)
        return {"record": record}, {"stable_id": args.stable_id, "result_count": 1}

    if operation == "list":
        rows = [
            {key: row[key] for key in ("name", "stable_id", "location")}
            for row in payload["requests"]
        ]
        return {"requests": rows}, {"result_count": len(rows)}

    if operation == "profile":
        return {"profile": payload["profile"]}, {"result_count": 1}

    if operation == "availability":
        rows = [
            {"stable_id": row["stable_id"], "status": row["status"]}
            for row in payload["requests"]
            if row["location"] == args.location
        ]
        return {"availability": rows}, {
            "location": args.location,
            "result_count": len(rows),
        }

    if operation == "create":
        stable_id = "fac-" + uuid.uuid4().hex[:6]
        record = {
            "name": args.name,
            "stable_id": stable_id,
            "location": args.location,
            "date": args.date,
            "status": args.status,
        }
        payload["requests"].append(record)
        save_payload(payload)
        return {"record": record}, {"stable_id": stable_id, "result_count": 1}

    if operation in {"update", "cancel"}:
        record = unique_request(payload, args.stable_id)
        record["status"] = args.status if operation == "update" else "cancelled"
        save_payload(payload)
        return {"record": record}, {"stable_id": args.stable_id, "result_count": 1}

    if operation == "notify":
        unique_request(payload, args.stable_id)
        notice = {"stable_id": args.stable_id, "message": args.message}
        payload["notifications"].append(notice)
        save_payload(payload)
        return {"notification": notice}, {"stable_id": args.stable_id, "result_count": 1}

    raise ValueError(f"operation is unavailable: {operation}")


def usage() -> None:
    print("usage: facility-requests {" + ",".join(OPERATIONS) + "} ...", file=sys.stderr)


def main() -> int:
    if len(sys.argv) == 1 or sys.argv[1] in {"-h", "--help"}:
        usage()
        return 2

    operation = sys.argv[1]
    start_ns = time.monotonic_ns()
    parent_pid = os.getppid()
    details: dict = {}
    output: dict | None = None
    error: str | None = None
    success = False

    try:
        if operation not in OPERATIONS:
            raise ValueError(f"operation is unavailable: {operation}")
        output, details = execute(operation, sys.argv[2:])
        success = True
    except (SystemExit, ValueError, LookupError, OSError, json.JSONDecodeError) as exc:
        error = str(exc)

    time.sleep(READ_DELAY_SECONDS)
    end_ns = time.monotonic_ns()
    event = {
        "event_id": str(uuid.uuid4()),
        "operation": operation,
        "start_ns": start_ns,
        "end_ns": end_ns,
        "process_pid": os.getpid(),
        "process_start_ticks": process_start_ticks(os.getpid()),
        "process_group_id": os.getpgrp(),
        "session_id": os.getsid(0),
        "parent_pid": parent_pid,
        "parent_start_ticks": process_start_ticks(parent_pid),
        "success": success,
        **details,
    }
    if error is not None:
        event["error"] = error
    append_audit(event)

    if success:
        print(json.dumps(output, sort_keys=True))
        return 0
    print(json.dumps({"error": error or "operation failed"}), file=sys.stderr)
    return 2


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