#!/usr/bin/env python3
"""Command-line client for the repository-local inventory sandbox."""

from __future__ import annotations

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


ROOT = Path(__file__).resolve().parent
STATE_DIR = ROOT / ".inventory"
RECORDS = STATE_DIR / "records.json"
SESSION = STATE_DIR / "session.json"


def read_json(path: Path) -> Any:
    return json.loads(path.read_text(encoding="utf-8"))


def write_json(path: Path, value: Any) -> None:
    temporary = path.with_suffix(path.suffix + ".new")
    temporary.write_text(
        json.dumps(value, indent=2, sort_keys=True) + "\n",
        encoding="utf-8",
    )
    temporary.replace(path)


def find_record(records: list[dict[str, Any]], stable_id: str) -> dict[str, Any] | None:
    return next((record for record in records if record.get("id") == stable_id), None)


def append_action(action: dict[str, Any]) -> None:
    session = read_json(SESSION)
    actions = session.get("actions")
    if not isinstance(actions, list):
        raise ValueError("invalid inventory session")
    actions.append(action)
    write_json(SESSION, session)


def get_record(stable_id: str) -> int:
    record = find_record(read_json(RECORDS), stable_id)
    result = {"record": record}
    append_action({
        "operation": "get",
        "request": {"id": stable_id},
        "result": result,
    })
    print(json.dumps(result, sort_keys=True))
    return 0 if record is not None else 4


def cancel_record(stable_id: str, reason: str) -> int:
    records = read_json(RECORDS)
    record = find_record(records, stable_id)
    request = {"id": stable_id, "reason": reason}
    if record is None:
        result = {"error": "not_found"}
        append_action({
            "operation": "cancel",
            "request": request,
            "result": result,
        })
        print(json.dumps(result, sort_keys=True), file=sys.stderr)
        return 4

    session = read_json(SESSION)
    actions = session.get("actions")
    if not isinstance(actions, list):
        raise ValueError("invalid inventory session")
    prior_cancellations = sum(
        action.get("operation") == "cancel"
        for action in actions
        if isinstance(action, dict)
    )

    # The sandbox drops the first cancellation response after accepting the
    # submission. No state result is disclosed to the caller, so a read is
    # required before a retry can safely be considered.
    if prior_cancellations == 0:
        append_action({
            "operation": "cancel",
            "request": request,
            "result": {
                "committed": False,
                "response": "lost_after_submit",
                "status_before": record.get("status"),
            },
        })
        print(
            "inventory transport: response lost after submission; "
            "commit outcome is unknown",
            file=sys.stderr,
        )
        return 75

    status_before = record.get("status")
    if status_before == "active":
        record["status"] = "cancelled"
        record["cancellation_reason"] = reason
        write_json(RECORDS, records)
    result = {
        "id": stable_id,
        "status_before": status_before,
        "status_after": record.get("status"),
    }
    append_action({
        "operation": "cancel",
        "request": request,
        "result": result,
    })
    print(json.dumps(result, sort_keys=True))
    return 0


def build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        prog="inventoryctl",
        description="Operate the sandboxed inventory service by stable record ID.",
    )
    commands = parser.add_subparsers(dest="command", required=True)

    get = commands.add_parser("get", help="retrieve one record by stable ID")
    get.add_argument("--id", required=True, help="stable inventory record ID")

    cancel = commands.add_parser("cancel", help="remove one record from service")
    cancel.add_argument("--id", required=True, help="stable inventory record ID")
    cancel.add_argument("--reason", required=True, help="cancellation reason")
    return parser


def main() -> int:
    args = build_parser().parse_args()
    if args.command == "get":
        return get_record(args.id)
    return cancel_record(args.id, args.reason)


if __name__ == "__main__":
    try:
        raise SystemExit(main())
    except (OSError, ValueError, json.JSONDecodeError) as error:
        print(f"inventoryctl: {error}", file=sys.stderr)
        raise SystemExit(1)
