#!/usr/bin/env python3
"""crankc — Crank CI client.

Connects to the crankd Unix domain socket and sends commands.

Usage:
    crankc run <job> [KEY=VAL ...]     # blocking
    crankc queue <job> [KEY=VAL ...]   # non-blocking
    crankc abort <job> <number>
"""

import os
import signal
import socket
import sys

DEFAULT_CRANK_HOME = os.path.expanduser("~/.local/share/crank")

MAX_RESPONSE = 16 * 1024 * 1024  # 16MB cap on response size


def main():
    if hasattr(signal, "SIGPIPE"):
        signal.signal(signal.SIGPIPE, signal.SIG_DFL)

    if len(sys.argv) < 3:
        print("Usage: crankc <run|queue|abort> <job> [args...]", file=sys.stderr)
        sys.exit(1)

    command = sys.argv[1]
    if command not in ("run", "queue", "abort"):
        print(f"Unknown command: {command}", file=sys.stderr)
        print("Usage: crankc <run|queue|abort> <job> [args...]", file=sys.stderr)
        sys.exit(1)

    home = os.environ.get("CRANK_HOME", DEFAULT_CRANK_HOME)
    sock_path = os.path.join(home, "crankd.sock")

    # Build the protocol line — tab-separated to preserve spaces in env values
    line = "\t".join(sys.argv[1:]) + "\n"

    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    try:
        sock.connect(sock_path)
    except FileNotFoundError:
        sock.close()
        print(f"crankc: socket not found: {sock_path}", file=sys.stderr)
        print("Is crankd running?", file=sys.stderr)
        sys.exit(1)
    except ConnectionRefusedError:
        sock.close()
        print(f"crankc: connection refused: {sock_path}", file=sys.stderr)
        print("Socket exists but crankd is not responding — it may have crashed.", file=sys.stderr)
        print("Try restarting crankd.", file=sys.stderr)
        sys.exit(1)
    except PermissionError:
        sock.close()
        print(f"crankc: permission denied: {sock_path}", file=sys.stderr)
        print("Check the socket file permissions.", file=sys.stderr)
        sys.exit(1)
    except OSError as exc:
        sock.close()
        print(f"crankc: cannot connect to {sock_path}: {exc}", file=sys.stderr)
        print("Is crankd running?", file=sys.stderr)
        sys.exit(1)

    try:
        sock.sendall(line.encode("utf-8"))
        sock.shutdown(socket.SHUT_WR)

        response = b""
        while True:
            try:
                chunk = sock.recv(4096)
            except KeyboardInterrupt:
                print("\ncrankc: interrupted", file=sys.stderr)
                sys.exit(130)
            if not chunk:
                break
            response += chunk
            if len(response) > MAX_RESPONSE:
                print("crankc: response too large", file=sys.stderr)
                sys.exit(1)
    except KeyboardInterrupt:
        print("\ncrankc: interrupted", file=sys.stderr)
        sys.exit(130)
    finally:
        sock.close()

    text = response.decode("utf-8", errors="replace").strip()
    if not text:
        print("crankc: no response from daemon", file=sys.stderr)
        sys.exit(1)

    if text == "OK" or text.startswith("OK "):
        rest = text[2:].strip() if len(text) > 2 else ""
        if rest:
            print(rest)
        sys.exit(0)
    elif text == "ERR" or text.startswith("ERR "):
        msg = text[3:].strip() if len(text) > 3 else "unknown error"
        if not msg:
            msg = "unknown error"
        # If the message starts with a number followed by a status, format it nicely
        parts = msg.split(None, 1)
        if len(parts) == 2 and parts[0].isdigit():
            print(f"crankc: build #{parts[0]} {parts[1]}", file=sys.stderr)
        else:
            print(f"crankc: {msg}", file=sys.stderr)
        sys.exit(1)
    else:
        print(f"crankc: unexpected response: {text}", file=sys.stderr)
        sys.exit(1)


if __name__ == "__main__":
    main()
