#!/usr/bin/env python3
"""scr: command line for scrd. Uses the root socket when it can, otherwise the user socket."""
import argparse
import json
import os
import socket
import sys

RUN_DIR = os.environ.get("SCR_RUN", "/run/scr")


def call(cmd: str, **args) -> dict:
    root = os.path.join(RUN_DIR, "root.sock")
    path = root if os.access(root, os.W_OK) else os.path.join(RUN_DIR, "user.sock")
    try:
        with socket.socket(socket.AF_UNIX) as s:
            s.settimeout(30)
            s.connect(path)
            s.sendall((json.dumps({"cmd": cmd, "args": args}) + "\n").encode())
            return json.loads(s.makefile().readline())
    except OSError as e:
        sys.exit("scr: cannot reach scrd (%s): %s" % (path, e))


def table(rows: list) -> None:
    print("%-5s %-16s %-9s %-4s %-7s %-8s %s" % ("ID", "NAME", "STATE", "ON", "PID", "UPTIME", "RESTARTS"))
    for r in rows:
        print("%-5s %-16s %-9s %-4s %-7s %-8s %s" % (r["id"], r["name"], r["state"], "yes" if r["enabled"] else "no",
                                                    r["pid"] or "-", "%ds" % r["uptime"] if r["pid"] else "-",
                                                    r["restarts"]))


def main() -> None:
    p = argparse.ArgumentParser(prog="scr", description="service runner")
    sub = p.add_subparsers(dest="cmd", required=True)
    r = sub.add_parser("register", help="add a service (root)")
    r.add_argument("name")
    r.add_argument("target", help="path or shell command (derive: the parent service name or ID)")
    r.add_argument("--runner", choices=["exec", "uv", "npm", "python", "node", "java", "cargo", "systemd", "external", "derive"], default="exec")
    r.add_argument("--user", default="root")
    r.add_argument("--task", help="npm script to run (npm runner)")
    r.add_argument("--after", action="append", default=[], metavar="SERVICE", help="start after this service (repeat)")
    r.add_argument("--check", metavar="SPEC", help="port:8090 or http://localhost:8089/health")
    r.add_argument("--args", help="extra arguments passed to the program")
    r.add_argument("--venv", help="python runner: virtualenv directory")
    r.add_argument("--mem", help="java runner: heap size, e.g. 2G")
    r.add_argument("--label", help="display name for the status page")
    r.add_argument("--stop-timeout", type=int, dest="stop_timeout", help="seconds to wait after SIGTERM before SIGKILL (default 15)")
    r.add_argument("--cwd")
    r.add_argument("--id")
    for name in ("enable", "disable", "start", "stop", "restart", "remove"):
        sub.add_parser(name).add_argument("service", help="name or ID")
    c = sub.add_parser("check")
    c.add_argument("service")
    c.add_argument("-j", "--json", action="store_true")
    sub.add_parser("attach", help="open the service's tmux session (detach: Ctrl-b d)").add_argument("service")
    sd = sub.add_parser("send", help="type text into the service (presses Enter)")
    sd.add_argument("service")
    sd.add_argument("text", nargs="+")
    sd.add_argument("--no-enter", action="store_true")
    l = sub.add_parser("logs")
    l.add_argument("service")
    l.add_argument("-n", "--lines", type=int, default=50)
    st = sub.add_parser("status")
    st.add_argument("-j", "--json", action="store_true")
    a = p.parse_args()

    args = {k: v for k, v in vars(a).items() if k not in ("cmd", "json") and v is not None}
    if a.cmd == "send":
        args["text"] = " ".join(a.text)
    if a.cmd == "attach":
        reply = call("where", service=a.service)
        if not reply.get("ok"):
            sys.exit("scr: %s" % reply.get("error"))
        os.execvp("tmux", ["tmux", "-S", reply["sock"], "attach", "-t", reply["session"]])
    reply = call(a.cmd, **args)
    if not reply.get("ok"):
        sys.exit("scr: %s" % reply.get("error"))
    if getattr(a, "json", False):
        print(json.dumps(reply.get("services", reply), indent=2))
    elif a.cmd == "status":
        table(reply["services"])
    elif a.cmd == "check":
        print("%s %s: %s%s" % (reply["id"], reply["name"], reply["state"],
                               ", healthy" if reply["healthy"] else ", NOT healthy"))
        sys.exit(0 if reply["state"] == "running" and reply["healthy"] else 1)
    elif a.cmd == "logs":
        print(reply["text"], end="")
    else:
        print("ok %s" % reply.get("id", ""))


if __name__ == "__main__":
    main()
