import os, json, time, shlex, signal, subprocess, http.server, socketserver
from urllib.parse import urlparse, parse_qs

BASE = os.path.expanduser("~/Claude/Projects/에이전트구축")
JOBS = os.path.join(BASE, ".jobs")
PORT = 8899
TOKEN = "sx7k2m"
PY = "/opt/homebrew/bin/python3"
ALLOWED = {"collect_x": "collect_x.py", "collect_news": "collect_news.py", "flow_scan": "flow_scan.py"}
MAX_RUN = 900

os.makedirs(JOBS, exist_ok=True)
try:
    signal.signal(signal.SIGCHLD, signal.SIG_IGN)
except Exception:
    pass

def paths(name):
    b = os.path.join(JOBS, name)
    return b + ".json", b + ".log", b + ".rc"

def alive(pid):
    try:
        os.kill(int(pid), 0)
        return True
    except Exception:
        return False

def read_status(name, with_log=True):
    sf, lf, rf = paths(name)
    if not os.path.exists(sf):
        return {"job": name, "state": "never_run"}
    try:
        st = json.load(open(sf))
    except Exception:
        return {"job": name, "state": "unknown"}
    if st.get("state") == "running":
        if os.path.exists(rf):
            rc = open(rf).read().strip()
            st["state"] = "done" if rc == "0" else "failed"
            st["returncode"] = rc
            st["elapsed"] = round(os.path.getmtime(rf) - st["started"], 1)
            json.dump(st, open(sf, "w"), ensure_ascii=False)
        elif not alive(st.get("pid", -1)) or time.time() - st["started"] > MAX_RUN:
            st["state"] = "died"
            json.dump(st, open(sf, "w"), ensure_ascii=False)
        else:
            st["elapsed"] = round(time.time() - st["started"], 1)
    if with_log and os.path.exists(lf):
        st["log"] = open(lf, errors="replace").read()[-2000:]
    return st

def launch(name):
    cur = read_status(name, with_log=False)
    if cur.get("state") == "running":
        return 409, cur
    sf, lf, rf = paths(name)
    for f in (lf, rf):
        if os.path.exists(f):
            os.remove(f)
    cmd = "%s -u %s > %s 2>&1; echo $? > %s" % (
        shlex.quote(PY),
        shlex.quote(os.path.join(BASE, ALLOWED[name])),
        shlex.quote(lf), shlex.quote(rf))
    p = subprocess.Popen(["/bin/sh", "-c", cmd], cwd=BASE,
                         stdin=subprocess.DEVNULL,
                         stdout=subprocess.DEVNULL,
                         stderr=subprocess.DEVNULL,
                         start_new_session=True)
    st = {"job": name, "state": "running", "pid": p.pid,
          "started": time.time(),
          "started_str": time.strftime("%Y-%m-%d %H:%M:%S")}
    json.dump(st, open(sf, "w"), ensure_ascii=False)
    return 202, st

class H(http.server.SimpleHTTPRequestHandler):
    def __init__(self, *a, **kw):
        super().__init__(*a, directory=BASE, **kw)

    def log_message(self, *a):
        pass

    def reply(self, code, obj):
        body = json.dumps(obj, ensure_ascii=False).encode()
        self.send_response(code)
        self.send_header("Content-Type", "application/json; charset=utf-8")
        self.send_header("Content-Length", str(len(body)))
        self.end_headers()
        self.wfile.write(body)

    def do_GET(self):
        u = urlparse(self.path)
        path = u.path
        if not (path.startswith("/run/") or path.startswith("/status")):
            return super().do_GET()
        q = parse_qs(u.query)
        if (q.get("k") or [""])[0] != TOKEN:
            self.send_error(403, "bad token")
            return
        if path.startswith("/status"):
            name = path[len("/status"):].strip("/")
            if name:
                if name not in ALLOWED:
                    self.send_error(404, "unknown job")
                    return
                return self.reply(200, read_status(name))
            return self.reply(200, {n: read_status(n, with_log=False) for n in ALLOWED})
        name = path[len("/run/"):].strip("/")
        if name not in ALLOWED:
            self.send_error(404, "unknown job")
            return
        code, st = launch(name)
        self.reply(code, st)

class S(socketserver.ThreadingTCPServer):
    allow_reuse_address = True
    daemon_threads = True

S(("127.0.0.1", PORT), H).serve_forever()
