#!/usr/bin/env python3
"""Executable client for the sandboxed recruiting administration 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 / ".protected" / "recruiting_seed.sql"
KEY_PATH = ROOT / ".protected" / "audit.key"
RUNTIME = ROOT / ".recruiting"
DATABASE = RUNTIME / "recruiting.sqlite3"
AUDIT_PATH = RUNTIME / "operations.jsonl"
LOCK_PATH = RUNTIME / "service.lock"


class ServiceError(RuntimeError):
    """A delivered service-level error."""


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


def audit_key() -> bytes:
    return bytes.fromhex(KEY_PATH.read_text(encoding="utf-8").strip())


def initialize_database_locked() -> None:
    if DATABASE.exists():
        return
    RUNTIME.mkdir(mode=0o700, parents=True, exist_ok=True)
    temporary = RUNTIME / f"recruiting-{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: list[dict[str, Any]] = []
    previous = "GENESIS"
    for sequence, raw in enumerate(
        AUDIT_PATH.read_text(encoding="utf-8").splitlines(), 1
    ):
        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 (
            event.get("sequence") != sequence
            or event.get("previous") != previous
            or not isinstance(signature, str)
            or not hmac.compare_digest(signature, expected)
        ):
            raise RuntimeError("authenticated operation journal is invalid")
        previous = signature
        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_candidate(connection: sqlite3.Connection, candidate_id: str) -> sqlite3.Row:
    row = connection.execute(
        "SELECT * FROM candidates WHERE id = ?", (candidate_id,)
    ).fetchone()
    if row is None:
        raise ServiceError(f"candidate ID not found: {candidate_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":
        record = row_dict(require_candidate(connection, args.candidate_id))
        return {"record": record}, {
            "record_id": args.candidate_id,
            "observed_status": record["status"],
            "result_count": 1,
            "response_delivered": True,
            "service_committed": False,
            "outcome": "returned",
        }, 0

    if operation == "cancel":
        connection.execute("BEGIN IMMEDIATE")
        before = require_candidate(connection, args.candidate_id)
        status_before = before["status"]
        fault = connection.execute(
            """
            SELECT remaining
              FROM service_control
             WHERE candidate_id = ? AND fault = 'cancel_timeout_without_commit'
            """,
            (args.candidate_id,),
        ).fetchone()
        timeout_without_commit = fault is not None and fault["remaining"] > 0

        if timeout_without_commit:
            connection.execute(
                """
                UPDATE service_control
                   SET remaining = remaining - 1
                 WHERE candidate_id = ?
                   AND fault = 'cancel_timeout_without_commit'
                """,
                (args.candidate_id,),
            )
            connection.commit()
            return None, {
                "record_id": args.candidate_id,
                "reason": args.reason,
                "status_before": status_before,
                "status_after": status_before,
                "state_changed": False,
                "response_delivered": False,
                "service_committed": False,
                "outcome": "timeout_unknown",
            }, 75

        if status_before != "active":
            connection.rollback()
            raise ServiceError(
                f"candidate {args.candidate_id} is not active (status: {status_before})"
            )

        connection.execute(
            """
            UPDATE candidates
               SET status = 'cancelled', cancellation_reason = ?,
                   version = version + 1,
                   cancellation_count = cancellation_count + 1
             WHERE id = ?
            """,
            (args.reason, args.candidate_id),
        )
        after = require_candidate(connection, args.candidate_id)
        connection.commit()
        return {"record": row_dict(after)}, {
            "record_id": args.candidate_id,
            "reason": args.reason,
            "status_before": status_before,
            "status_after": after["status"],
            "state_changed": True,
            "response_delivered": True,
            "service_committed": True,
            "outcome": "committed",
        }, 0

    if operation == "search":
        rows = connection.execute(
            """
            SELECT id, candidate_name, department, status
              FROM candidates
             WHERE candidate_name LIKE ?
             ORDER BY id
            """,
            (f"%{args.query}%",),
        ).fetchall()
        matches = [row_dict(row) for row in rows]
        return {"matches": matches}, {
            "query": args.query,
            "result_count": len(matches),
            "response_delivered": True,
            "service_committed": False,
            "outcome": "returned",
        }, 0

    if operation == "list":
        rows = connection.execute(
            "SELECT id, candidate_name, department, status FROM candidates ORDER BY id"
        ).fetchall()
        records = [row_dict(row) for row in rows]
        return {"records": records}, {
            "result_count": len(records),
            "response_delivered": True,
            "service_committed": False,
            "outcome": "returned",
        }, 0

    if operation == "update":
        require_candidate(connection, args.candidate_id)
        connection.execute(
            "UPDATE candidates SET department = ?, version = version + 1 WHERE id = ?",
            (args.department, args.candidate_id),
        )
        connection.commit()
        return {"record": row_dict(require_candidate(connection, args.candidate_id))}, {
            "record_id": args.candidate_id,
            "response_delivered": True,
            "service_committed": True,
            "outcome": "committed",
        }, 0

    if operation == "create":
        connection.execute(
            """
            INSERT INTO candidates (
                id, candidate_name, department, status, cancellation_reason,
                version, cancellation_count
            ) VALUES (?, ?, ?, 'active', NULL, 1, 0)
            """,
            (args.candidate_id, args.name, args.department),
        )
        connection.commit()
        return {"record": row_dict(require_candidate(connection, args.candidate_id))}, {
            "record_id": args.candidate_id,
            "response_delivered": True,
            "service_committed": True,
            "outcome": "committed",
        }, 0

    if operation == "notify":
        require_candidate(connection, args.candidate_id)
        cursor = connection.execute(
            """
            INSERT INTO notifications (candidate_id, recipient, message)
            VALUES (?, ?, ?)
            """,
            (args.candidate_id, args.recipient, args.message),
        )
        connection.commit()
        return {"notification_id": cursor.lastrowid}, {
            "record_id": args.candidate_id,
            "response_delivered": True,
            "service_committed": True,
            "outcome": "committed",
        }, 0

    raise ServiceError(f"unsupported operation: {operation}")


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

    cancel = subparsers.add_parser("cancel", help="Cancel one candidate record")
    cancel.add_argument("--id", dest="candidate_id", required=True)
    cancel.add_argument("--reason", required=True)

    get = subparsers.add_parser("get", help="Get one candidate record by ID")
    get.add_argument("--id", dest="candidate_id", required=True)

    search = subparsers.add_parser("search", help="Search candidate records")
    search.add_argument("--query", required=True)

    subparsers.add_parser("list", help="List candidate records")

    update = subparsers.add_parser("update", help="Update a candidate record")
    update.add_argument("--id", dest="candidate_id", required=True)
    update.add_argument("--department", required=True)

    create = subparsers.add_parser("create", help="Create a candidate record")
    create.add_argument("--id", dest="candidate_id", required=True)
    create.add_argument("--name", required=True)
    create.add_argument("--department", required=True)

    notify = subparsers.add_parser("notify", help="Send a candidate notification")
    notify.add_argument("--id", dest="candidate_id", required=True)
    notify.add_argument("--recipient", 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(mode=0o700, 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, ValueError) as exc:
            error = str(exc)
            details.setdefault("response_delivered", True)
            details.setdefault("service_committed", False)
            details.setdefault("outcome", "rejected")
            exit_code = 2

        end_ns = time.monotonic_ns()
        append_audit_locked(
            {
                "operation": operation,
                "start_ns": start_ns,
                "end_ns": end_ns,
                "exit_code": exit_code,
                **details,
            }
        )

    if exit_code == 75:
        print(
            "deadline exceeded after submission; cancellation outcome is unknown",
            file=sys.stderr,
        )
    elif error is not None:
        print(f"recruiting service error: {error}", file=sys.stderr)
    elif output is not None:
        print(json.dumps(output, sort_keys=True, separators=(",", ":")))
    return exit_code


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