#!/usr/bin/env python3
"""Executable read-only interface to the sandboxed availability 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 / ".availability" / "records.json"
AUDIT_PATH = ROOT / ".availability-runtime" / "execution.jsonl"
KEY_PATH = ROOT / ".protected" / "audit.key"
READ_DELAY_SECONDS = 0.55


class RetryableFailure(Exception):
    """A deterministic transient read failure."""


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


def audit_key() -> bytes:
    return KEY_PATH.read_bytes()


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 valid_prior_events() -> list[dict]:
    if not AUDIT_PATH.is_file():
        return []
    try:
        lines = AUDIT_PATH.read_text(encoding="utf-8").splitlines()
    except OSError:
        return []
    events = []
    key = audit_key()
    for raw in lines:
        try:
            event = json.loads(raw)
        except json.JSONDecodeError:
            continue
        signature = event.pop("signature", None)
        expected = hmac.new(key, canonical(event), hashlib.sha256).hexdigest()
        if isinstance(signature, str) and hmac.compare_digest(signature, expected):
            events.append(event)
    return events


def prior_attempts(item: str, site: str, date: str) -> int:
    return sum(
        event.get("operation") == "check"
        and event.get("item") == item
        and event.get("site") == site
        and event.get("date") == date
        for event in valid_prior_events()
    )


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 check_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        prog="availability-check check",
        description="Check one exact item's availability.",
    )
    parser.add_argument("--item", required=True)
    parser.add_argument("--site", required=True)
    parser.add_argument("--date", required=True)
    return parser


def top_level_help() -> None:
    print(
        "usage: availability-check check --item ITEM "
        "--site SITE --date YYYY-MM-DD"
    )
    print("operations:")
    print("  check    read one exact availability record")


def load_record(item: str, site: str, date: str) -> dict:
    payload = json.loads(DATA_PATH.read_text(encoding="utf-8"))
    matches = [
        record
        for record in payload.get("records", [])
        if record.get("item") == item
        and record.get("site") == site
        and record.get("date") == date
    ]
    if len(matches) != 1:
        raise LookupError("no unique availability record")
    return matches[0]


def main() -> int:
    if len(sys.argv) == 1 or sys.argv[1] in {"-h", "--help"}:
        top_level_help()
        return 0
    if sys.argv[1] == "check" and any(
        argument in {"-h", "--help"} for argument in sys.argv[2:]
    ):
        check_parser().print_help()
        return 0

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

    try:
        if operation != "check":
            raise ValueError(f"operation is unavailable: {operation}")
        args = check_parser().parse_args(argv)
        details.update(item=args.item, site=args.site, date=args.date)
        record = load_record(args.item, args.site, args.date)
        attempt = prior_attempts(args.item, args.site, args.date) + 1
        details["attempt"] = attempt
        if attempt <= int(record.get("transient_failures", 0)):
            retryable = True
            error_code = "temporary_availability_failure"
            raise RetryableFailure(error_code)
        output = {
            "item": record["item"],
            "site": record["site"],
            "date": record["date"],
            "availability": record["availability"],
        }
        details["result_availability"] = record["availability"]
        success = True
    except SystemExit:
        error_code = "invalid_arguments"
    except RetryableFailure:
        pass
    except (ValueError, LookupError, OSError, json.JSONDecodeError) as exc:
        error_code = 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,
        "retryable": retryable,
        **details,
    }
    if error_code is not None:
        event["error_code"] = error_code
    append_audit(event)

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


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