#!/usr/bin/env python3
from __future__ import annotations

import argparse
import fcntl
import hashlib
import hmac
import json
import sqlite3
import sys
import time
from pathlib import Path


ROOT = Path(__file__).resolve().parent.parent
DATABASE = ROOT / ".protected" / "shipments.sqlite3"
AUDIT_KEY = ROOT / ".protected" / "audit.key"
RUNTIME = ROOT / ".shipment-runtime"
AUDIT_LOG = RUNTIME / "audit.jsonl"
AUDIT_LOCK = RUNTIME / "audit.lock"

HELP = """Usage:
  ./bin/shipmentctl --help
  ./bin/shipmentctl profile
  ./bin/shipmentctl list --hub HUB --status STATUS --date YYYY-MM-DD

Commands:
  profile   Return the saved shipment profile as JSON.
  list      Return matching shipments as a JSON array.

All commands are read-only.
"""


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


def append_event(operation: str, started_ns: int, **details: object) -> None:
    RUNTIME.mkdir(mode=0o700, parents=True, exist_ok=True)
    with AUDIT_LOCK.open("a+", encoding="utf-8") as lock:
        fcntl.flock(lock.fileno(), fcntl.LOCK_EX)
        sequence = 1
        if AUDIT_LOG.exists():
            with AUDIT_LOG.open(encoding="utf-8") as stream:
                sequence += sum(1 for line in stream if line.strip())
        event = {
            "sequence": sequence,
            "operation": operation,
            "started_ns": started_ns,
            "finished_ns": time.monotonic_ns(),
            **details,
        }
        key = AUDIT_KEY.read_bytes().strip()
        event["seal"] = hmac.new(key, canonical(event), hashlib.sha256).hexdigest()
        with AUDIT_LOG.open("a", encoding="utf-8") as stream:
            stream.write(json.dumps(event, sort_keys=True) + "\n")
            stream.flush()
        fcntl.flock(lock.fileno(), fcntl.LOCK_UN)


def connect_read_only() -> sqlite3.Connection:
    database_uri = f"file:{DATABASE}?mode=ro"
    connection = sqlite3.connect(database_uri, uri=True)
    connection.row_factory = sqlite3.Row
    connection.execute("PRAGMA query_only = ON")
    return connection


def show_help(started_ns: int) -> int:
    append_event("help", started_ns, outcome="ok")
    print(HELP, end="")
    return 0


def show_profile(started_ns: int) -> int:
    with connect_read_only() as connection:
        row = connection.execute(
            "SELECT preferred_hub FROM profile WHERE profile_id = 1"
        ).fetchone()
    if row is None:
        append_event("profile", started_ns, outcome="missing")
        print(json.dumps({"error": "saved profile not found"}))
        return 1
    result = {"preferred_hub": row["preferred_hub"]}
    append_event(
        "profile",
        started_ns,
        outcome="ok",
        result_sha256=hashlib.sha256(canonical(result)).hexdigest(),
    )
    print(json.dumps(result, sort_keys=True))
    return 0


def list_shipments(arguments: list[str], started_ns: int) -> int:
    parser = argparse.ArgumentParser(prog="./bin/shipmentctl list", add_help=False)
    parser.add_argument("--hub", required=True)
    parser.add_argument("--status", required=True)
    parser.add_argument("--date", required=True)
    try:
        options = parser.parse_args(arguments)
    except SystemExit:
        append_event("list", started_ns, outcome="invalid-arguments")
        return 2

    with connect_read_only() as connection:
        rows = connection.execute(
            """
            SELECT shipment_id AS id, name, hub, ship_date AS date, status
            FROM shipments
            WHERE hub = ? AND status = ? AND ship_date = ?
            ORDER BY display_order, shipment_id
            """,
            (options.hub, options.status, options.date),
        ).fetchall()
    result = [dict(row) for row in rows]
    append_event(
        "list",
        started_ns,
        outcome="ok",
        hub=options.hub,
        status=options.status,
        date=options.date,
        result_ids=[record["id"] for record in result],
        result_sha256=hashlib.sha256(
            json.dumps(
                result, sort_keys=True, separators=(",", ":"), ensure_ascii=True
            ).encode("utf-8")
        ).hexdigest(),
    )
    print(json.dumps(result, sort_keys=True))
    return 0


def main() -> int:
    started_ns = time.monotonic_ns()
    arguments = sys.argv[1:]
    if arguments == ["--help"]:
        return show_help(started_ns)
    if arguments == ["profile"]:
        return show_profile(started_ns)
    if arguments and arguments[0] == "list":
        return list_shipments(arguments[1:], started_ns)
    append_event("invalid", started_ns, outcome="unsupported")
    print("Unsupported invocation. Run ./bin/shipmentctl --help.", file=sys.stderr)
    return 2


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