#!/usr/bin/env python3
"""
aria-cli (v0.1) — client-side daemon-runner for the Aria CLI device queue.

Lives at ~/.aria-cli/aria-cli on user machines after curl-bash install. Reads
~/.aria-cli/config for server URL + device_id + auth_token.

Commands:
  aria-cli pull            # poll the server once; execute any pending job; exit
  aria-cli devices         # list this user's enrolled devices on the server
  aria-cli config          # print current config (without revealing auth_token)
  aria-cli health          # one-shot self-test against the server

The "pull" command is the primary mode — schedule it every 60s via the
installer-printed launchd / systemd / cron template.

Job kinds handled in v0.1:
  - ping              → echoes back a heartbeat
  - noop              → reports done with {} payload (sanity check)
  - create_api_key:anthropic → STUB: prints instructions for the user to
                        create an Anthropic key + paste it back. Real
                        Anthropic OAuth integration arrives in R3.T1-B.

All other job_kind values are reported back as {status:'failed',error:'unsupported_kind'}.
"""
import json
import os
import sys
import urllib.error
import urllib.request
from pathlib import Path

CONFIG_PATH = Path.home() / ".aria-cli" / "config"


def _read_config() -> dict:
    if not CONFIG_PATH.exists():
        sys.stderr.write(
            f"✗ Config not found at {CONFIG_PATH}.\n"
            f"  Run the installer first: curl -fsSL https://staycool.ai/install/aria-cli | bash -s -- <TOKEN>\n"
        )
        sys.exit(2)
    cfg = {}
    for line in CONFIG_PATH.read_text().splitlines():
        line = line.strip()
        if not line or line.startswith("#") or "=" not in line:
            continue
        k, _, v = line.partition("=")
        cfg[k.strip()] = v.strip()
    required = ("ARIA_SERVER", "ARIA_DEVICE_ID", "ARIA_AUTH_TOKEN")
    missing = [k for k in required if k not in cfg]
    if missing:
        sys.stderr.write(f"✗ Config missing keys: {missing}. Re-run installer.\n")
        sys.exit(3)
    return cfg


def _request(
    method: str, url: str, token: str | None = None, body: dict | None = None
) -> dict:
    data = json.dumps(body).encode() if body is not None else None
    req = urllib.request.Request(url, data=data, method=method)
    req.add_header("Content-Type", "application/json")
    if token:
        req.add_header("Authorization", f"Bearer {token}")
    try:
        with urllib.request.urlopen(req, timeout=15) as resp:
            return json.loads(resp.read().decode())
    except urllib.error.HTTPError as e:
        body_txt = ""
        try:
            body_txt = e.read().decode()
        except Exception:
            pass
        return {"_error": True, "status": e.code, "body": body_txt[:400]}
    except Exception as e:
        return {"_error": True, "status": 0, "body": f"{type(e).__name__}: {e}"}


# ── Job executors ────────────────────────────────────────────────────────


def _execute_ping(payload: dict) -> dict:
    return {
        "echo": payload,
        "ts": __import__("datetime")
        .datetime.now(__import__("datetime").timezone.utc)
        .isoformat(),
    }


def _execute_noop(payload: dict) -> dict:
    return {}


def _execute_create_api_key_anthropic(payload: dict) -> tuple[str, dict, str]:
    """v0.1 STUB. Real OAuth flow arrives in R3.T1-B. For now: print user
    instructions + return a 'instructions_shown' status so the server can
    track that the user was prompted.

    Returns: (status, result_dict, error_str)
    """
    print("─────────────────────────────────────────────")
    print("  Aria asked aria-cli to help you create an")
    print(
        "  Anthropic API key (request_id={req}).".format(
            req=payload.get("request_id", "")
        )
    )
    print("")
    print("  1. Open https://console.anthropic.com/settings/keys")
    print("  2. Create a new key (Workspace 'default' is fine)")
    print("  3. Copy the key starting with sk-ant-…")
    print("  4. Paste it back into the Aria onboarding tab")
    print("")
    print("  (R3.T1-B will replace this with an OAuth flow.)")
    print("─────────────────────────────────────────────")
    return (
        "done",
        {"instructions_shown": True, "request_id": payload.get("request_id", "")},
        "",
    )


# ── Commands ─────────────────────────────────────────────────────────────


def cmd_pull(cfg: dict) -> int:
    server = cfg["ARIA_SERVER"]
    token = cfg["ARIA_AUTH_TOKEN"]
    r = _request("POST", f"{server}/aria-cli/pull", token=token, body={})
    if r.get("_error"):
        sys.stderr.write(f"✗ pull failed: HTTP {r.get('status')}: {r.get('body')}\n")
        return 1
    job = r.get("job")
    if not job:
        print("· no pending job")
        return 0

    kind = job.get("kind", "")
    job_id = job.get("job_id")
    payload = job.get("payload", {}) or {}
    print(f"→ executing {kind} (job_id={job_id})")

    status, result, error = "failed", {}, "unsupported_kind"
    try:
        if kind == "ping":
            result = _execute_ping(payload)
            status, error = "done", ""
        elif kind == "noop":
            result = _execute_noop(payload)
            status, error = "done", ""
        elif kind == "create_api_key:anthropic":
            status, result, error = _execute_create_api_key_anthropic(payload)
        else:
            error = f"unsupported_kind:{kind}"
    except Exception as e:
        status, error = "failed", f"{type(e).__name__}: {e}"

    r2 = _request(
        "POST",
        f"{server}/aria-cli/result/{job_id}",
        token=token,
        body={"status": status, "result": result, "error": error},
    )
    if r2.get("_error"):
        sys.stderr.write(
            f"✗ result post failed: HTTP {r2.get('status')}: {r2.get('body')}\n"
        )
        return 1
    print(f"✓ {kind} {status}")
    return 0


def cmd_devices(cfg: dict) -> int:
    # This endpoint is ac_session-cookie auth, not device-token auth, so we
    # can't call it from aria-cli directly without the user's browser session.
    # Print a friendly redirect.
    server = cfg["ARIA_SERVER"]
    print(f"Open: {server}/portal/aria-cli/devices")
    print("(device list endpoint is session-cookie authed, not aria-cli token)")
    return 0


def cmd_config(cfg: dict) -> int:
    redacted = dict(cfg)
    if "ARIA_AUTH_TOKEN" in redacted:
        t = redacted["ARIA_AUTH_TOKEN"]
        redacted["ARIA_AUTH_TOKEN"] = (t[:8] + "…" + t[-4:]) if len(t) > 12 else "***"
    for k, v in redacted.items():
        print(f"{k}={v}")
    return 0


def cmd_health(cfg: dict) -> int:
    server = cfg["ARIA_SERVER"]
    token = cfg["ARIA_AUTH_TOKEN"]
    # Best smoke: enqueue+pull a ping job in one go would require server-side
    # privileges. Instead just call /pull — server-side device touch updates
    # last_seen_at + we get a clear 200/401.
    r = _request("POST", f"{server}/aria-cli/pull", token=token, body={})
    if r.get("_error"):
        print(
            f"✗ server={server} status=HTTP-{r.get('status')} body={r.get('body','')[:200]}"
        )
        return 1
    print(
        f"✓ server={server} reachable; device authed; job-queue={'pending' if r.get('job') else 'empty'}"
    )
    return 0


def main() -> int:
    cmd = sys.argv[1] if len(sys.argv) > 1 else "pull"
    cfg = _read_config()
    handlers = {
        "pull": cmd_pull,
        "devices": cmd_devices,
        "config": cmd_config,
        "health": cmd_health,
    }
    fn = handlers.get(cmd)
    if not fn:
        sys.stderr.write(f"unknown command: {cmd}\n")
        sys.stderr.write(f"available: {', '.join(handlers.keys())}\n")
        return 2
    return fn(cfg)


if __name__ == "__main__":
    sys.exit(main())
