#!/usr/bin/env woltspace-python
"""Print a short-lived GitHub App installation token to stdout.

Usage:
    GH_TOKEN=$(gh-app-token) gh issue create ...

Reads GITHUB_APP_ID, GITHUB_APP_INSTALLATION_ID, and GITHUB_APP_PRIVATE_KEY
from the environment (or from WOLT_DIR/.env).

Runs on `woltspace-python` — the interpreter woltspace itself is installed on,
which owns PyJWT through the `connectors` extra. The image puts it on PATH; the
old shebang pointed at a per-project .venv that the packaged install has not
had since the container stopped baking the source tree.

Token is valid for ~1 hour. Generate a fresh one each time.
"""

import sys
import os
import time
import json
from pathlib import Path
from urllib.request import Request, urlopen

# ---------------------------------------------------------------------------
# Env loading — uses python-dotenv for reliable parsing (multi-line values, quotes)
# ---------------------------------------------------------------------------

def _get_env(key: str) -> str:
    val = os.environ.get(key)
    if val:
        return val
    from dotenv import dotenv_values
    wolt_dir = os.environ.get("WOLT_DIR", "")
    for candidate in [
        Path(wolt_dir) / ".env" if wolt_dir else None,
        Path("/workspace/wolts/.env"),
    ]:
        if candidate and candidate.exists():
            vals = dotenv_values(candidate)
            if key in vals and vals[key] is not None:
                return vals[key]
    return ""


# ---------------------------------------------------------------------------
# JWT — minimal RS256 implementation using only stdlib + PyJWT
# ---------------------------------------------------------------------------

def _make_jwt(app_id: str, private_key: str) -> str:
    """Sign a JWT for GitHub App auth. Valid for 9 minutes."""
    import jwt  # PyJWT
    now = int(time.time())
    payload = {
        "iat": now - 60,   # 60s leeway for clock skew
        "exp": now + 540,  # 9 minutes (GitHub max is 10)
        "iss": app_id,
    }
    return jwt.encode(payload, private_key, algorithm="RS256")


# ---------------------------------------------------------------------------
# Token exchange
# ---------------------------------------------------------------------------

def get_token() -> str:
    app_id = _get_env("GITHUB_APP_ID")
    installation_id = _get_env("GITHUB_APP_INSTALLATION_ID")
    private_key = _get_env("GITHUB_APP_PRIVATE_KEY").replace("\\n", "\n")

    missing = []
    if not app_id:
        missing.append("GITHUB_APP_ID")
    if not installation_id:
        missing.append("GITHUB_APP_INSTALLATION_ID")
    if not private_key:
        missing.append("GITHUB_APP_PRIVATE_KEY")
    if missing:
        print(f"error: missing env vars: {', '.join(missing)}", file=sys.stderr)
        sys.exit(1)

    # Validate PEM format
    if "BEGIN" not in private_key:
        print("error: GITHUB_APP_PRIVATE_KEY doesn't look like a PEM key", file=sys.stderr)
        sys.exit(1)

    app_jwt = _make_jwt(app_id, private_key)

    url = f"https://api.github.com/app/installations/{installation_id}/access_tokens"
    req = Request(url, method="POST", data=b"")
    req.add_header("Authorization", f"Bearer {app_jwt}")
    req.add_header("Accept", "application/vnd.github+json")
    req.add_header("X-GitHub-Api-Version", "2022-11-28")

    try:
        with urlopen(req, timeout=10) as resp:
            data = json.loads(resp.read())
            return data["token"]
    except Exception as e:
        print(f"error: token exchange failed: {e}", file=sys.stderr)
        sys.exit(1)


if __name__ == "__main__":
    print(get_token())
