#!/usr/bin/env python3
"""A stand-in for the GitHub CLI's `gh api`, for tests/test_publish_release.py and
tests/test_release_workflow.py. Serves the releases in $FAKEGH_RELEASES (a JSON list,
drafts included) and the tags in $FAKEGH_TAGS (space-separated); an upload's body is
written to $FAKEGH_UPLOAD. Every call is appended to $FAKEGH_LOG as a JSON line.
Anything else fails; `gh release upload` works only when a release-workflow test
explicitly sets FAKEGH_ALLOW_RELEASE_UPLOAD=1. POST .../releases (release.yml's draft step) answers as release
$FAKEGH_NEW_ID (default 9001) without adding it to the listing, like GitHub's list
lagging a fresh draft. With jq installed, --jq filters a response the way gh does
(raw strings, compact JSON)."""
import json
import os
import shutil
import subprocess
import sys
from urllib.parse import parse_qs

args = sys.argv[1:]
with open(os.environ["FAKEGH_LOG"], "a") as f:
    f.write(json.dumps(args) + "\n")
if args[:2] == ["release", "upload"] and os.environ.get("FAKEGH_ALLOW_RELEASE_UPLOAD") == "1":
    releases = json.load(open(os.environ["FAKEGH_RELEASES"]))
    if os.environ.get("FAKEGH_UPLOAD_ERROR"):
        sys.exit(os.environ["FAKEGH_UPLOAD_ERROR"])
    if not any(r.get("tag_name") == args[2] for r in releases):
        sys.exit("release not found")
    sys.exit(0)
if not args or args[0] != "api":
    sys.exit("fakegh: only `gh api` is supported: %r" % args)

method, endpoint, fields, inp, jq, i = "GET", None, {}, None, None, 1
while i < len(args):
    a = args[i]
    if a == "-X":
        method = args[i + 1]; i += 2
    elif a in ("-f", "-F"):
        k, _, v = args[i + 1].partition("="); fields[k] = v; i += 2
    elif a == "--input":
        inp = args[i + 1]; i += 2
    elif a == "--jq":
        jq = args[i + 1]; i += 2
    elif a == "-H":
        i += 2
    elif a.startswith("-"):
        i += 1
    elif endpoint is None:
        endpoint = a; i += 1
    else:
        sys.exit("fakegh: unexpected argument %r" % a)

releases = json.load(open(os.environ["FAKEGH_RELEASES"]))
repo = "repos/saphid/frame-control/"
gate = json.load(open(os.environ["FAKEGH_GATES"])) if os.environ.get("FAKEGH_GATES") else {}
commit = gate.get("tag_commit", "a" * 40)
tag_object = "b" * 40
main_jobs = ("checks", "e2e", "ios", "wireless-windows",
             "server-tests (ubuntu-latest, 3.13)", "server-tests (macos-latest, 3.12)",
             "server-tests (windows-latest, 3.12)")
release_jobs = ("draft", "checksums",
                "build (windows-latest, dist:win, app/dist/*.exe app/dist/*.zip)",
                "build (ubuntu-latest, dist:linux, app/dist/*.AppImage app/dist/*.deb)",
                "build (macos-15-intel, dist:mac:x64, app/dist/*.dmg app/dist/*.zip)",
                "build (macos-latest, dist, app/dist/*.dmg app/dist/*.zip)")
decky_jobs = ("decky-runtime (ubuntu-latest)", "decky-runtime (ubuntu-24.04-arm)")


def answer(response, fallback):
    """Print response through --jq when jq is here, else the fixed fallback text."""
    if jq and shutil.which("jq"):
        out = subprocess.run(["jq", "-r", "-c", jq], input=json.dumps(response),
                             stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
        sys.stdout.write(out.stdout)
        if out.returncode:
            sys.exit("fakegh: jq failed: " + out.stderr)
    else:
        print(fallback)


if method == "GET" and endpoint.startswith(repo + "git/ref/tags/"):
    tag = endpoint.rsplit("/", 1)[1]
    if tag not in os.environ.get("FAKEGH_TAGS", "").split():
        sys.exit("gh: Not Found (HTTP 404)")
    print(json.dumps({"ref": "refs/tags/" + tag,
                      "object": {"type": "tag", "sha": tag_object}}))
elif method == "GET" and endpoint == repo + "git/tags/" + tag_object:
    print(json.dumps({"object": {"type": "commit", "sha": commit}}))
elif method == "GET" and endpoint.startswith(repo + "actions/workflows/"):
    path, _, query = endpoint.partition("?")
    workflow = path.split("/")[5]
    branch = parse_qs(query).get("branch", [""])[0]
    is_main = workflow != "release.yml"
    run_id = {"checks.yml": 501, "release.yml": 502, "decky-runtime.yml": 503}[workflow]
    run = {"id": run_id, "head_sha": gate.get("main_sha" if is_main else "release_sha", commit),
           "head_branch": branch, "event": "push", "run_attempt": 1,
           "status": gate.get("decky_status" if run_id == 503 else ("main_status" if is_main else "release_status"), "completed"),
           "conclusion": gate.get("decky_conclusion" if run_id == 503 else ("main_conclusion" if is_main else "release_conclusion"), "success"),
           "html_url": "https://github.com/saphid/frame-control/actions/runs/%s" % run_id}
    print(json.dumps({"workflow_runs": [run]}))
elif method == "GET" and endpoint.startswith(repo + "actions/runs/"):
    run_id = endpoint[len(repo + "actions/runs/"):].split("/")[0]
    names = {"501": main_jobs, "502": release_jobs, "503": decky_jobs}[run_id]
    names = [name for name in names if name != gate.get("omit_job")]
    jobs = [{"name": name, "status": "completed",
             "conclusion": gate.get("bad_job_conclusion", "success") if name == gate.get("bad_job") else "success"}
            for name in names]
    print(json.dumps({"total_count": len(jobs), "jobs": jobs}))
elif method == "GET" and endpoint.startswith(repo + "releases?"):
    # The fallback is what --jq '.[]' prints.
    answer(releases, "\n".join(json.dumps(r) for r in releases))
elif method == "POST" and endpoint == repo + "releases":
    new = {"id": int(os.environ.get("FAKEGH_NEW_ID", "9001")), "tag_name": fields.get("tag_name"),
           "name": fields.get("name"), "draft": fields.get("draft") == "true",
           "body": fields.get("body"), "assets": [],
           "html_url": "https://github.com/saphid/frame-control/releases/tag/untagged-be29e900f7855d794136"}
    answer(new, json.dumps(new))
elif method == "DELETE" and endpoint.startswith(repo + "releases/assets/"):
    pass
elif method == "POST" and endpoint.startswith("https://uploads.github.com/" + repo + "releases/"):
    with open(inp) as src, open(os.environ["FAKEGH_UPLOAD"], "w") as dst:
        dst.write(src.read())
    print("{}")
elif method == "PATCH" and endpoint.startswith(repo + "releases/"):
    rid = endpoint[len(repo + "releases/"):]
    if not rid.isdigit():  # what GitHub said to v0.4.2's PATCH of releases/ (an empty id)
        sys.exit("gh: Not Found (HTTP 404)")
    if os.environ.get("FAKEGH_ALLOW_RELEASE_UPLOAD") == "1":
        for item in releases:
            if str(item["id"]) == rid:
                item.update(fields)
        with open(os.environ["FAKEGH_RELEASES"], "w") as f:
            json.dump(releases, f)
    page = "https://github.com/saphid/frame-control/releases/tag/%s" % fields.get("tag_name")
    answer({"id": int(rid), "tag_name": fields.get("tag_name"), "html_url": page},
           "published %s at %s" % (fields.get("tag_name"), page))
else:
    sys.exit("fakegh: unhandled %s %s" % (method, endpoint))
