"""Python 3.11+, standard library. One saved operation, no automatic retries.
python quest.py saved-intent.json new-response.json
python quest.py --curl saved-intent.json new-response.json  # uses installed curl
"""
import json
import os
import re
import subprocess
import sys
import tempfile
import urllib.error
import urllib.parse
import urllib.request

IDENT = r"[A-Za-z0-9_-]{1,128}"
PATH = re.compile(r"^/(?:chat|requests/" + IDENT + r"|sessions(?:/" + IDENT + r"(?:/(?:end|discard|history))?)?|session/status|data/(?:export|erase)|quest/(?:catalog|sessions(?:/" + IDENT + r"(?:/(?:state|summary))?)?|attachments(?:/" + IDENT + r")?|learners/" + IDENT + r"/profile|data/export(?:/attachments/" + IDENT + r")?))$")
UUID = re.compile(r"^[a-f0-9]{8}(-[a-f0-9]{4}){3}-[a-f0-9]{12}$")
UPLOAD_BYTES = 4 * ((8 * 1024 * 1024 + 2) // 3) + 16384
BOUND = 8 * 1024 * 1024


class NoRedirect(urllib.request.HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        raise ValueError("Redirect refused; verify the assigned API host")


def make_request(intent, key):
    base = urllib.parse.urlsplit(intent["base_url"])
    user, method, path = intent["external_user_id"], intent["method"], intent["path"]
    if (base.scheme != "https" or not base.netloc or base.username or base.password or base.query or base.fragment
            or base.path not in ("/v1", "/v1/") or not PATH.fullmatch(path)
            or not UUID.fullmatch(intent["assignment_id"]) or not re.fullmatch(IDENT, user)
            or not re.fullmatch(r"[\x21-\x7e]{1,512}", key) or method not in ("GET", "POST", "DELETE")):
        raise ValueError("Invalid saved intent or credentials")
    query = intent.get("query", {})
    if any(name not in ("limit", "cursor", "session_id") for name in query):
        raise ValueError("Unexpected query field")
    if path.startswith("/quest/learners/") and path != "/quest/learners/" + user + "/profile":
        raise ValueError("Profile user mismatch")
    url = intent["base_url"].rstrip("/") + path + (("?" + urllib.parse.urlencode(query)) if query else "")
    headers = {"Authorization": "Bearer " + key, "X-Assignment-Id": intent["assignment_id"], "X-User-Id": user, "FL-API-Version": "2026-12-01"}
    body = None
    if method != "GET":
        if not UUID.fullmatch(intent.get("idempotency_key", "")):
            raise ValueError("Save an operation UUID before dispatch")
        headers.update({"Idempotency-Key": intent["idempotency_key"], "Content-Type": "application/json"})
        body = json.dumps(intent.get("body", {}), ensure_ascii=False, separators=(",", ":")).encode("utf-8")
        if len(body) > (UPLOAD_BYTES if path == "/quest/attachments" else 131072):
            raise ValueError("Request exceeds bound")
    elif "body" in intent:
        raise ValueError("GET cannot have a body")
    return url, method, headers, body


def verify(value, require_end=False):
    q, s, c = value.get("quest", {}), value.get("safety_status", {}), value.get("commercial", {})
    parts = value.get("parts", [])
    charge = c.get("charge", {})
    if (not value.get("request_id") or not value.get("session_id") or value.get("stop_reason") != "end_turn"
            or type(value.get("session_ended")) is not bool or require_end and not value["session_ended"]
            or set(q) != {"mode", "posture", "composition_hash", "turn_index", "team_dialog", "repairs", "validation_failures"}
            or not re.fullmatch(r"[a-z0-9][a-z0-9-]{0,99}", q.get("mode", ""))
            or q.get("posture") not in ("guided", "feedback", "explain") or not re.fullmatch(r"[a-f0-9]{64}", q.get("composition_hash", ""))
            or type(q.get("turn_index")) is not int or not 0 <= q["turn_index"] <= 9007199254740991 or type(q.get("team_dialog")) is not bool
            or any(not isinstance(q.get(k), list) or len(q[k]) > 64 or any(not isinstance(v, str) or not re.fullmatch(r"[a-z_]+(?::[a-z_]+)?", v) for v in q[k]) for k in ("repairs", "validation_failures"))
            or s.get("type") != "safety_status" or s.get("schema_version") != 1 or s.get("level") not in ("unknown", "clear", "watch", "warning", "critical")
            or type(s.get("evaluated")) is not bool or s["evaluated"] != (s["level"] != "unknown") or s.get("response_action") not in ("none", "support", "escalate") or s.get("source") not in ("governed_turn", "backstop")
            or any(not isinstance(c.get(k), str) or not c[k] for k in ("listing", "band", "rate_version"))
            or type(c.get("billable_units")) is not int or c["billable_units"] not in (0, 1)
            or type(charge.get("amount_micro_usd")) is not int or not 0 <= charge["amount_micro_usd"] <= 9007199254740991 or charge.get("currency") != "USD"
            or c.get("settlement") not in ("reserved", "settled", "released") or (c["billable_units"] == 0 and charge["amount_micro_usd"] != 0)
            or c["settlement"] == "released" and c["billable_units"] != 0 or not isinstance(parts, list) or len(parts) > 4096
            or any(p.get("part") not in (("conversation", "display", "frame") if q["team_dialog"] else ("conversation", "display")) or not isinstance(p.get("text"), str) for p in parts)
            or any(not any(p["part"] == name and p["text"].strip() for p in parts) for name in ("conversation", "display"))):
        raise ValueError("Incomplete or unverified Quest result; recover the original request")
    return {"request_id": value["request_id"], "session_id": value["session_id"], "parts": [{"part": p["part"], "text": p["text"]} for p in parts],
            "quest": q, "safety_status": {k: s[k] for k in ("type", "schema_version", "evaluated", "level", "response_action", "source")},
            "commercial": {**{k: c[k] for k in ("listing", "band", "rate_version", "billable_units", "settlement")}, "charge": {k: charge[k] for k in ("amount_micro_usd", "currency")}},
            "session_ended": value["session_ended"], "stop_reason": "end_turn"}


def parse_stream(source, require_end=False, expected_session=None):
    request_id = session_id = safety = quest = commercial = None
    ended = complete = False
    parts = []
    for frame in source.replace("\r\n", "\n").split("\n\n"):
        data = "\n".join(line[5:].lstrip() for line in frame.split("\n") if line.startswith("data:"))
        if not data:
            continue
        event = json.loads(data)
        kind = event.get("type")
        if complete or kind == "error":
            raise ValueError("Interrupted or invalid stream; recover original request " + (request_id or ""))
        if kind == "metadata":
            if request_id or not isinstance(event.get("request_id"), str) or not isinstance(event.get("sessionId"), str) or expected_session and event["sessionId"] != expected_session:
                raise ValueError("Invalid stream identity")
            request_id, session_id = event["request_id"], event["sessionId"]
        else:
            if not request_id:
                raise ValueError("Missing stream identity")
            if kind == "content":
                parts.append({"part": event.get("part"), "text": event.get("text")})
            elif kind == "safety_status":
                if safety:
                    raise ValueError("Duplicate safety receipt")
                safety = event
            elif kind == "quest_receipt":
                if quest:
                    raise ValueError("Duplicate Quest receipt")
                quest = event.get("receipt")
            elif kind == "session_ended":
                if event.get("sessionId") != session_id:
                    raise ValueError("Invalid closeout identity")
                ended = True
            elif kind == "done":
                if event.get("sessionId") != session_id or event.get("commercial", {}).get("request_id") != request_id or event.get("stopReason") != "end_turn":
                    raise ValueError("Invalid final identity")
                commercial, complete = event["commercial"], True
    if not complete:
        raise ValueError("Incomplete stream; recover original request " + (request_id or ""))
    return verify({"request_id": request_id, "session_id": session_id, "parts": parts, "safety_status": safety or {}, "quest": quest or {}, "commercial": commercial or {}, "session_ended": ended, "stop_reason": "end_turn"}, require_end)


def parse_response(intent, source, media):
    path = intent["path"]
    if media == "text/event-stream":
        return parse_stream(source, path.endswith("/end"), intent.get("body", {}).get("session_id") or (path.split("/")[2] if path.endswith("/end") else None))
    if media != "application/json":
        raise ValueError("Unexpected response type")
    data = json.loads(source)
    if path.startswith("/requests/"):
        if data.get("request_id") != path.split("/")[2] or data.get("status") not in ("committed", "in_progress", "unavailable"):
            raise ValueError("Invalid recovery identity")
        if data["status"] == "committed":
            return {"status": data["status"], **verify({**data.get("result", {}), "request_id": data["request_id"], "session_id": data.get("session_id")})}
        if "result" in data:
            raise ValueError("Uncommitted result")
        return {k: data.get(k) for k in ("status", "request_id", "session_id")}
    if path == "/chat" or path.endswith("/end"):
        raise ValueError("Generation requires a verified stream")
    if "operation_id" in data and "operation_id" in intent.get("body", {}) and data["operation_id"] != intent["body"]["operation_id"]:
        raise ValueError("Mismatched operation receipt")
    return data


def transport(intent, key, use_curl=False):
    url, method, headers, body = make_request(intent, key)
    if use_curl:
        # Secret headers never appear in argv. Temporary private files are removed on exit.
        with tempfile.TemporaryDirectory(prefix="quest-example-") as directory:
            header_file, body_file, output = [os.path.join(directory, x) for x in ("headers", "body", "response")]
            with open(header_file, "x", encoding="utf-8") as stream:
                os.chmod(header_file, 0o600)
                stream.write("".join(k + ": " + v + "\n" for k, v in headers.items()))
            args = ["curl", "--silent", "--show-error", "--proto", "=https", "--max-time", "120", "--max-filesize", str(BOUND),
                    "--request", method, "--header", "@" + header_file, "--output", output, "--write-out", "%{http_code}\n%{content_type}"]
            if body is not None:
                with open(body_file, "xb") as stream:
                    os.chmod(body_file, 0o600)
                    stream.write(body)
                args += ["--data-binary", "@" + body_file]
            done = subprocess.run(args + ["--url", url], check=True, capture_output=True, text=True, timeout=125)
            status, media = done.stdout.split("\n", 1)
            with open(output, "rb") as stream:
                data = stream.read(BOUND + 1)
            return int(status), media.split(";")[0].strip(), data
    opener = urllib.request.build_opener(NoRedirect())
    request = urllib.request.Request(url, data=body, headers=headers, method=method)
    try:
        response = opener.open(request, timeout=120)
    except urllib.error.HTTPError as error:
        response = error
    with response:
        return response.status, response.headers.get_content_type(), response.read(BOUND + 1)


def main():
    args = sys.argv[1:]
    use_curl = args and args[0] == "--curl"
    if use_curl:
        args = args[1:]
    if len(args) != 2:
        raise ValueError("python quest.py [--curl] saved-intent.json new-response.json (or .bin)")
    with open(args[0], "rb") as source:
        saved = source.read(UPLOAD_BYTES + 4097)
    if len(saved) > UPLOAD_BYTES + 4096:
        raise ValueError("Saved intent too large")
    intent = json.loads(saved)
    with open(args[1], "xb") as output:
        os.chmod(args[1], 0o600)
        status, media, data = transport(intent, os.environ.get("FOREVERLEARNING_API_KEY", ""), use_curl)
        if len(data) > BOUND:
            raise ValueError("Response exceeds bound")
        if not 200 <= status < 300:
            raise ValueError("HTTP " + str(status) + "; retain the original body and operation identity")
        binary_path = ((intent["method"] == "GET" and re.fullmatch(r"/quest/attachments/[^/]+", intent["path"]))
                       or (intent["method"] == "POST" and re.fullmatch(r"/quest/data/export/attachments/[^/]+", intent["path"])))
        if binary_path and media in ("image/png", "image/jpeg", "image/webp", "text/plain", "text/markdown"):
            output.write(data)
        else:
            output.write((json.dumps({"status": status, "data": parse_response(intent, data.decode("utf-8"), media)}, indent=2) + "\n").encode("utf-8"))
        output.flush()
        os.fsync(output.fileno())
    print("Saved response. Check status and completion before another operation.")


if __name__ == "__main__":
    try:
        main()
    except Exception:
        print("Request not confirmed. Retain the saved intent; inspect the original request before continuing.", file=sys.stderr)
        sys.exit(1)
