#!/usr/bin/env python3
"""Genuine executable client for the sandboxed facilities service."""

from __future__ import annotations

import argparse
import fcntl
import hashlib
import hmac
import json
import os
from pathlib import Path
import sqlite3
import sys
import time
import uuid
from typing import Any


ROOT = Path(__file__).resolve().parent
SEED_SQL = ROOT / ".facilitydesk" / "seed.sql"
RUNTIME = ROOT / ".harness" / "runtime"
DATABASE = RUNTIME / "facilities.sqlite3"
AUDIT_PATH = RUNTIME / "facilitydesk-audit.jsonl"
LOCK_PATH = RUNTIME / "facilitydesk.lock"
AUDIT_KEY = b"moonshiner-pi-facilitydesk-error-recovery-0017-v1"
OPERATIONS = ("cancel", "get", "search", "list", "create", "update", "notify")


class ServiceError(RuntimeError):
    """An ordinary service-level rejection with a delivered response."""


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


def initialize_database_locked() -> None:
    if DATABASE.exists():
        return
    RUNTIME.mkdir(parents=True, exist_ok=True)
    temporary = RUNTIME / f"facilities-{os.getpid()}-{uuid.uuid4().hex}.tmp"
    connection = sqlite3.connect(temporary)
    try:
        connection.executescript(SEED_SQL.read_text(encoding="utf-8"))
        connection.commit()
    finally:
        connection.close()
    os.replace(temporary, DATABASE)


def load_audit_locked() -> list[dict[str, Any]]:
    if not AUDIT_PATH.exists():
        return []
    events = []
    for raw in AUDIT_PATH.read_text(encoding="utf-8").splitlines():
        event = json.loads(raw)
        signature = event.get("signature")
        unsigned = {key: value for key, value in event.items() if key != "signature"}
        expected = hmac.new(AUDIT_KEY, canonical(unsigned), hashlib.sha256).hexdigest()
        if not isinstance(signature, str) or not hmac.compare_digest(
            signature, expected
        ):
            raise RuntimeError("facilitydesk audit chain is invalid")
        events.append(event)
    return events


def append_audit_locked(payload: dict[str, Any]) -> None:
    events = load_audit_locked()
    event = {
        "sequence": len(events) + 1,
        "previous": events[-1]["signature"] if events else "GENESIS",
        **payload,
    }
    event["signature"] = hmac.new(
        AUDIT_KEY, canonical(event), hashlib.sha256
    ).hexdigest()
    with AUDIT_PATH.open("a", encoding="utf-8") as stream:
        stream.write(json.dumps(event, sort_keys=True, separators=(",", ":")) + "\n")
        stream.flush()
        os.fsync(stream.fileno())


def row_dict(row: sqlite3.Row) -> dict[str, Any]:
    return dict(row)


def require_record(
    connection: sqlite3.Connection, stable_id: str
) -> sqlite3.Row:
    row = connection.execute(
        "SELECT * FROM facilities_requests WHERE request_id = ?", (stable_id,)
    ).fetchone()
    if row is None:
        raise ServiceError(f"stable ID not found: {stable_id}")
    return row


def execute(
    connection: sqlite3.Connection, operation: str, args: argparse.Namespace
) -> tuple[dict[str, Any] | None, dict[str, Any], int]:
    if operation == "get":
        row = require_record(connection, args.stable_id)
        record = row_dict(row)
        return {"record": record}, {
            "stable_id": args.stable_id,
            "result_count": 1,
            "observed_status": record["status"],
            "response_delivered": True,
            "service_committed": False,
        }, 0

    if operation == "cancel":
        connection.execute("BEGIN IMMEDIATE")
        before = require_record(connection, args.stable_id)
        status_before = before["status"]
        service_committed = False
        if status_before == "active":
            connection.execute(
                """
                UPDATE facilities_requests
                   SET status = 'cancelled', cancel_reason = ?,
                       cancellation_count = cancellation_count + 1
                 WHERE request_id = ?
                """,
                (args.reason, args.stable_id),
            )
            service_committed = True
        control = connection.execute(
            "SELECT value FROM service_control WHERE key = ?",
            ("lose_next_cancel_response",),
        ).fetchone()
        lose_response = control is not None and control["value"] == "1"
        if lose_response:
            connection.execute(
                "UPDATE service_control SET value = '0' WHERE key = ?",
                ("lose_next_cancel_response",),
            )
        after = require_record(connection, args.stable_id)
        connection.commit()
        details = {
            "stable_id": args.stable_id,
            "reason": args.reason,
            "status_before": status_before,
            "status_after": after["status"],
            "service_committed": service_committed,
            "response_delivered": not lose_response,
        }
        if lose_response:
            details["result_kind"] = "lost_response"
            return None, details, 75
        return {"record": row_dict(after)}, details, 0

    if operation == "search":
        rows = connection.execute(
            """
            SELECT request_id AS stable_id, name, location, status
              FROM facilities_requests
             WHERE name = ?
             ORDER BY request_id
            """,
            (args.name,),
        ).fetchall()
        matches = [row_dict(row) for row in rows]
        return {"matches": matches}, {
            "name": args.name,
            "result_count": len(matches),
            "response_delivered": True,
            "service_committed": False,
        }, 0

    if operation == "list":
        rows = connection.execute(
            "SELECT request_id AS stable_id, name, location, status "
            "FROM facilities_requests ORDER BY request_id"
        ).fetchall()
        records = [row_dict(row) for row in rows]
        return {"records": records}, {
            "result_count": len(records),
            "response_delivered": True,
            "service_committed": False,
        }, 0

    if operation == "create":
        connection.execute(
            """
            INSERT INTO facilities_requests (
                request_id, name, location, requested_for, status, priority,
                requested_by, details, cancel_reason, cancellation_count
            ) VALUES (?, ?, ?, ?, 'active', 'normal', 'facilitydesk', ?, NULL, 0)
            """,
            (
                args.stable_id,
                args.name,
                args.location,
                args.requested_for,
                args.details,
            ),
        )
        connection.commit()
        return {"record": row_dict(require_record(connection, args.stable_id))}, {
            "stable_id": args.stable_id,
            "response_delivered": True,
            "service_committed": True,
        }, 0

    if operation == "update":
        require_record(connection, args.stable_id)
        connection.execute(
            "UPDATE facilities_requests SET status = ? WHERE request_id = ?",
            (args.status, args.stable_id),
        )
        connection.commit()
        return {"record": row_dict(require_record(connection, args.stable_id))}, {
            "stable_id": args.stable_id,
            "response_delivered": True,
            "service_committed": True,
        }, 0

    if operation == "notify":
        require_record(connection, args.stable_id)
        cursor = connection.execute(
            "INSERT INTO notifications (request_id, message) VALUES (?, ?)",
            (args.stable_id, args.message),
        )
        connection.commit()
        return {"notification_id": cursor.lastrowid}, {
            "stable_id": args.stable_id,
            "response_delivered": True,
            "service_committed": True,
        }, 0

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


def build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        prog="facilitydesk",
        description="Executable client for the sandboxed facilities request service.",
    )
    subparsers = parser.add_subparsers(dest="operation", required=True)

    cancel = subparsers.add_parser("cancel")
    cancel.add_argument("--id", dest="stable_id", required=True)
    cancel.add_argument("--reason", required=True)

    get = subparsers.add_parser("get")
    get.add_argument("--id", dest="stable_id", required=True)

    search = subparsers.add_parser("search")
    search.add_argument("--name", required=True)

    subparsers.add_parser("list")

    create = subparsers.add_parser("create")
    create.add_argument("--id", dest="stable_id", required=True)
    create.add_argument("--name", required=True)
    create.add_argument("--location", required=True)
    create.add_argument("--requested-for", required=True)
    create.add_argument("--details", required=True)

    update = subparsers.add_parser("update")
    update.add_argument("--id", dest="stable_id", required=True)
    update.add_argument("--status", required=True)

    notify = subparsers.add_parser("notify")
    notify.add_argument("--id", dest="stable_id", required=True)
    notify.add_argument("--message", required=True)
    return parser


def main() -> int:
    args = build_parser().parse_args()
    operation = args.operation
    start_ns = time.monotonic_ns()
    output: dict[str, Any] | None = None
    details: dict[str, Any] = {}
    exit_code = 2
    error: str | None = None

    RUNTIME.mkdir(parents=True, exist_ok=True)
    with LOCK_PATH.open("a+", encoding="utf-8") as lock:
        fcntl.flock(lock.fileno(), fcntl.LOCK_EX)
        try:
            initialize_database_locked()
            connection = sqlite3.connect(DATABASE)
            connection.row_factory = sqlite3.Row
            try:
                output, details, exit_code = execute(connection, operation, args)
            finally:
                connection.close()
        except (ServiceError, sqlite3.Error, OSError, RuntimeError) as exc:
            error = str(exc)
            details.setdefault("response_delivered", True)
            details.setdefault("service_committed", False)
            exit_code = 2
        end_ns = time.monotonic_ns()
        append_audit_locked(
            {
                "event_id": str(uuid.uuid4()),
                "operation": operation,
                "start_ns": start_ns,
                "end_ns": end_ns,
                "process_pid": os.getpid(),
                "parent_pid": os.getppid(),
                "exit_code": exit_code,
                **details,
                **({"error": error} if error is not None else {}),
            }
        )
        fcntl.flock(lock.fileno(), fcntl.LOCK_UN)

    if exit_code == 75:
        print(
            "facilitydesk: response lost after submission; commit outcome is unknown",
            file=sys.stderr,
        )
        return exit_code
    if exit_code != 0:
        print(json.dumps({"error": error or "request failed"}), file=sys.stderr)
        return exit_code
    print(json.dumps(output, sort_keys=True))
    return 0


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