#!/usr/bin/env python3
"""Command-line client for the speech-to-text daemon (sttd).

  stt toggle [--lang CODE] [--enter] [--agent]   start recording, or stop + transcribe + paste (--agent: hand the text to the default agent)
  stt start [--lang CODE]
  stt stop [--enter]
  stt cancel
  stt status [--json] [--follow]
  stt history [-n N] [-q QUERY] [--json]
  stt copy ID | paste ID [--enter] | play ID | delete ID
  stt set KEY JSON_VALUE               change one config key (e.g. stt set liveText false)
  stt rebind                           re-apply the Hyprland key bindings
  stt quit
"""

import json
import os
import shutil
import socket
import subprocess
import sys
import time

SOCK = os.path.join(os.environ.get("STT_RUNTIME_DIR") or os.path.join(os.environ.get("XDG_RUNTIME_DIR", "/tmp"), "speech-to-text"), "ctl.sock")


def die(msg, notify=True):
    print(msg, file=sys.stderr)
    if notify:
        cmd = "omarchy-notification-send" if shutil.which("omarchy-notification-send") else "notify-send"
        try:
            subprocess.Popen([cmd, "Speech to text", msg], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
        except OSError:
            pass
    sys.exit(1)


def connect():
    s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    try:
        s.connect(SOCK)
    except OSError:
        die("The speech-to-text daemon is not running (is the plugin enabled in the bar?)")
    return s


def send(s, obj):
    s.sendall((json.dumps(obj) + "\n").encode())


def lines(s):
    buf = b""
    while True:
        data = s.recv(65536)
        if not data:
            return
        buf += data
        while b"\n" in buf:
            line, buf = buf.split(b"\n", 1)
            if line.strip():
                yield json.loads(line)


def first(s, type_):
    for msg in lines(s):
        if msg.get("type") == type_:
            return msg


def flag(args, name):
    if name in args:
        args.remove(name)
        return True
    return False


def opt(args, name, default=None):
    if name in args:
        i = args.index(name)
        if i + 1 < len(args):
            v = args[i + 1]
            del args[i:i + 2]
            return v
    return default


def fmt_state(st):
    s = st.get("state", "?")
    if s == "recording":
        return f"recording ({st.get('langLabel')}) {st.get('elapsed', 0)}s: {st.get('partial', '')}"
    if s == "transcribing":
        return "transcribing"
    last = st.get("last") or {}
    return "idle" + (f" · last: {last.get('text', '')}" if last else "")


def main(argv):
    args = list(argv)
    if not args or args[0] in ("-h", "--help", "help"):
        print(__doc__.strip())
        return
    cmd = args.pop(0)
    s = connect()
    s.settimeout(None if cmd == "status" and "--follow" in args else 15)  # never leave a key binding's process hanging
    try:
        initial = first(s, "state")  # the daemon greets with its state
    except socket.timeout:
        die("The speech-to-text daemon is not answering")

    if cmd in ("toggle", "start"):
        send(s, {"cmd": cmd, "lang": opt(args, "--lang"), "enter": flag(args, "--enter"), "agent": flag(args, "--agent")})
        try:
            st = first(s, "state")
        except socket.timeout:
            st = None
        print(fmt_state(st) if st else "ok")
    elif cmd == "stop":
        send(s, {"cmd": "stop", "enter": flag(args, "--enter"), "agent": flag(args, "--agent")})
        print("stopping")
    elif cmd in ("cancel", "rebind", "quit", "stopPlay", "clearError"):
        send(s, {"cmd": cmd})
        print("ok")
    elif cmd == "status":
        as_json = flag(args, "--json")
        follow = flag(args, "--follow")
        st = initial
        while True:
            print(json.dumps(st) if as_json else fmt_state(st), flush=True)
            if not follow:
                break
            st = first(s, "state")
            if st is None:
                break
    elif cmd == "history":
        n = int(opt(args, "-n", "20"))
        q = opt(args, "-q", "")
        as_json = flag(args, "--json")
        send(s, {"cmd": "history", "limit": n, "query": q})
        h = first(s, "history")
        if as_json:
            print(json.dumps(h, indent=2))
        else:
            for t in h.get("items", []):
                when = time.strftime("%Y-%m-%d %H:%M", time.localtime(t["createdAt"]))
                print(f"{t['id']:>5}  {when}  {t['lang']:<3} {t['duration']:>5.1f}s  {t['text']}")
            total = h.get("total", 0)
            print(f"({total} recording{'' if total == 1 else 's'})")
    elif cmd in ("copy", "paste", "play", "delete"):
        if not args:
            die(f"usage: stt {cmd} ID", notify=False)
        send(s, {"cmd": cmd, "id": int(args[0]), "enter": flag(args, "--enter")})
        time.sleep(0.2)
        print("ok")
    elif cmd == "set":
        if len(args) < 2:
            die("usage: stt set KEY JSON_VALUE", notify=False)
        try:
            value = json.loads(args[1])
        except ValueError:
            value = args[1]
        send(s, {"cmd": "set", "config": {args[0]: value}})
        st = first(s, "state")
        print(json.dumps(st.get("config", {}).get(args[0])))
    else:
        die(f"unknown command: {cmd}", notify=False)
    s.close()


if __name__ == "__main__":
    main(sys.argv[1:])
