#!/usr/bin/env python3
import base64
import json
import os
import sys


root = os.environ["FAKE_GH_DIR"]
scenario_path = os.path.join(root, "scenario.json")
remote_path = os.path.join(root, "remote.json")
calls_path = os.path.join(root, "calls.jsonl")
with open(scenario_path, encoding="utf-8") as stream:
    scenario = json.load(stream)
try:
    with open(remote_path, encoding="utf-8") as stream:
        remote = json.load(stream)
except FileNotFoundError:
    remote = {"issues": scenario.get("issues", []), "failed_once": False, "reopen_failed_once": False, "run_queries": 0, "comment_posts": 0}

with open(calls_path, "a", encoding="utf-8") as stream:
    stream.write(json.dumps({"args": sys.argv[1:], "gh_token": os.environ.get("GH_TOKEN"), "github_token": os.environ.get("GITHUB_TOKEN")}) + "\n")

if sys.argv[1:3] == ["auth", "status"]:
    sys.exit(0 if scenario.get("auth_ok", True) else 1)
if len(sys.argv) < 3 or sys.argv[1] != "api":
    sys.exit(2)

args = sys.argv[2:]
endpoint = args[args.index("--hostname") + 2]
method = "GET"
if "--method" in args:
    method = args[args.index("--method") + 1]
fields = {}
field_items = []
for index, arg in enumerate(args):
    if arg in ("-f", "-F"):
        key, value = args[index + 1].split("=", 1)
        fields[key] = value
        field_items.append((key, value))

def save():
    with open(remote_path, "w", encoding="utf-8") as stream:
        json.dump(remote, stream)

def page_items(items):
    page = int(fields.get("page", "1"))
    per_page = int(fields.get("per_page", "100"))
    start = (page - 1) * per_page
    return items[start:start + per_page]

repo = scenario.get("repository", "owner/repo")
if endpoint.endswith("/labels") or "/labels/" in endpoint:
    print(json.dumps({"name": endpoint.rsplit("/", 1)[-1]}))
elif "/actions/workflows/" in endpoint and endpoint.endswith("/runs"):
    created = fields.get("created", "")
    responses = scenario.get("partition_responses", {})
    remote["run_queries"] += 1
    save()
    runs = responses.get(created, scenario.get("runs", []))
    total = 1000 if scenario.get("cap_first_query") and remote["run_queries"] == 1 else len(runs)
    print(json.dumps({"total_count": total, "workflow_runs": page_items(runs)}))
elif "/actions/workflows/" in endpoint:
    workflow = endpoint.rsplit("/", 1)[-1]
    path = ".github/workflows/unit-test.yml" if "unit-test" in workflow else ".github/workflows/integrations.yml"
    print(json.dumps({"id": 1 if "unit-test" in path else 2, "path": path}))
elif endpoint.endswith("/artifacts"):
    run_id = endpoint.split("/actions/runs/", 1)[1].split("/", 1)[0]
    artifacts = [item for item in scenario.get("artifacts", []) if str(item["run_id"]) == run_id]
    print(json.dumps({"total_count": len(artifacts), "artifacts": page_items(artifacts)}))
elif "/actions/artifacts/" in endpoint and endpoint.endswith("/zip"):
    artifact_id = endpoint.split("/actions/artifacts/", 1)[1].split("/", 1)[0]
    encoded = scenario["artifact_zips"][artifact_id]
    sys.stdout.buffer.write(base64.b64decode(encoded))
elif "/actions/runs/" in endpoint and "/attempts/" in endpoint:
    attempt = int(endpoint.rsplit("/", 1)[-1])
    run_id = int(endpoint.split("/actions/runs/", 1)[1].split("/", 1)[0])
    run = next(item for item in scenario["runs"] if item["id"] == run_id)
    result = dict(run)
    result["run_attempt"] = attempt
    result["run_started_at"] = scenario.get("attempt_times", {}).get(str(attempt), run["run_started_at"])
    print(json.dumps(result))
elif "/actions/runs/" in endpoint:
    run_id = int(endpoint.rsplit("/", 1)[-1])
    print(json.dumps(next(item for item in scenario["runs"] if item["id"] == run_id)))
elif endpoint == "graphql" and method == "POST":
    issues = [item for item in remote["issues"] if item.get("pull_request") is None]
    start = int(fields.get("cursor") or "0")
    nodes = []
    for issue in issues[start:start + 100]:
        nodes.append({
            "number": issue["number"],
            "state": issue["state"].upper(),
            "body": issue.get("body"),
            "closedAt": issue.get("closed_at"),
            "labels": {"nodes": [{"name": label["name"] if isinstance(label, dict) else label} for label in issue.get("labels", [])]},
        })
    next_start = start + len(nodes)
    has_next_page = next_start < len(issues)
    print(json.dumps({"data": {"repository": {"issues": {"nodes": nodes, "pageInfo": {"hasNextPage": has_next_page, "endCursor": str(next_start) if has_next_page else None}}}}}))
elif endpoint == "search/issues" and method == "GET":
    if scenario.get("search_error"):
        sys.exit(1)
    numbers = scenario.get("search_numbers")
    if numbers is None:
        query = fields.get("q", "")
        numbers = [issue["number"] for issue in remote["issues"] if issue.get("pull_request") is None and issue.get("body") and any(part in issue["body"] for part in query.split('"')[1::2])]
    print(json.dumps({"total_count": len(numbers), "items": [{"number": number} for number in numbers[:2]]}))
elif endpoint.endswith("/issues") and method == "GET":
    print(json.dumps(page_items(remote["issues"])))
elif "/issues/" in endpoint and method == "GET" and not endpoint.endswith("/comments"):
    number = int(endpoint.rsplit("/", 1)[-1])
    print(json.dumps(next(item for item in remote["issues"] if item["number"] == number)))
elif endpoint.endswith("/issues") and method == "POST":
    labels = [{"name": value} for key, value in field_items if key == "labels[]"]
    issue = {"number": len(remote["issues"]) + 1, "state": "open", "body": fields["body"], "title": fields["title"], "closed_at": None, "pull_request": None, "labels": labels}
    remote["issues"].append(issue)
    save()
    print(json.dumps(issue))
elif endpoint.endswith("/comments") and method == "GET":
    number = int(endpoint.split("/issues/", 1)[1].split("/", 1)[0])
    issue = next(item for item in remote["issues"] if item["number"] == number)
    print(json.dumps(page_items(issue.get("comments_data", []))))
elif endpoint.endswith("/comments") and method == "POST":
    remote["comment_posts"] += 1
    fail_at = scenario.get("fail_comment_at")
    if (scenario.get("fail_first_comment") and not remote["failed_once"]) or fail_at == remote["comment_posts"]:
        remote["failed_once"] = True
        save()
        print("simulated comment failure", file=sys.stderr)
        sys.exit(1)
    number = int(endpoint.split("/issues/", 1)[1].split("/", 1)[0])
    issue = next(item for item in remote["issues"] if item["number"] == number)
    issue.setdefault("comments_data", []).append({"body": fields["body"]})
    save()
    print(json.dumps({"id": len(issue["comments_data"])}))
elif "/issues/" in endpoint and method == "PATCH":
    number = int(endpoint.rsplit("/", 1)[-1])
    issue = next(item for item in remote["issues"] if item["number"] == number)
    if scenario.get("fail_first_reopen") and not remote["reopen_failed_once"]:
        remote["reopen_failed_once"] = True
        save()
        sys.exit(1)
    issue["state"] = fields["state"]
    save()
    print(json.dumps(issue))
else:
    print("unhandled fake gh endpoint: " + endpoint, file=sys.stderr)
    sys.exit(3)
