"""Python 3, standard library only. Run on your server; keep keys private."""
import json
import os
import sys
from urllib.error import HTTPError, URLError
from urllib.parse import quote, urlencode, urlsplit
from urllib.request import HTTPRedirectHandler, Request, build_opener


class NoRedirect(HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        return None


def request(path, *, key, base, opener=None):
    origin = urlsplit(base)
    if origin.scheme != "https" or not origin.netloc or origin.username or origin.password or origin.path not in ("", "/") or origin.query or origin.fragment:
        raise ValueError("UKPT_API_URL must be a plain HTTPS origin")
    if not key or any(c in key for c in "\r\n"):
        raise ValueError("Set UKPT_API_KEY privately")
    try:
        with (opener or build_opener(NoRedirect())).open(Request(base.rstrip("/") + path, headers={"X-API-Key": key, "Accept": "application/json"}), timeout=15) as response:
            return json.load(response)
    except HTTPError as error:
        retry = error.headers.get("Retry-After", "unknown")
        raise RuntimeError(f"UKPT HTTP {error.code}; Retry-After: {retry}") from None
    except (URLError, ValueError):
        raise RuntimeError("UKPT unavailable or invalid JSON; keep previous results labelled stale") from None


def rail_to_bus(key, crs="PWY", base="https://api.ukpt.botric.co.uk", requester=request):
    crs = crs.strip().upper()
    if len(crs) != 3 or not crs.isascii() or not crs.isalnum():
        raise ValueError("Use a three-character CRS code")
    get = lambda path: requester(path, key=key, base=base)
    station = get("/v1/resolve?" + urlencode({"crs": crs}))
    if not isinstance(station.get("id"), str) or not station["id"]:
        raise ValueError("Unexpected station response")
    board = get(f"/v1/map/locations/{quote(station['id'], safe='')}/departures?limit=5")
    buses = get(f"/v2/rail/stations/{crs}/nearby-bus-services?distance=500&stop_limit=6&service_limit=30&include_calling_patterns=false")
    if not isinstance(board.get("departures"), list) or not isinstance(buses.get("services"), list):
        raise ValueError("Unexpected passenger response")
    return {"station": station, "departures": board, "buses": buses}


if __name__ == "__main__":
    try:
        print(json.dumps(rail_to_bus(os.environ.get("UKPT_API_KEY"), sys.argv[1] if len(sys.argv) > 1 else "PWY", os.environ.get("UKPT_API_URL", "https://api.ukpt.botric.co.uk")), indent=2))
    except (ValueError, RuntimeError) as error:
        print(str(error), file=sys.stderr)
        sys.exit(1)
