"""Run one bounded request using connection details from a validated API.

Keep the supplied idempotency key with this request before sending it.
No automatic retries: a timeout may leave execution and billing state unknown.
Requires Python 3.9+; uses only the standard library.
"""
import json
import os
import re
import sys
from urllib.error import HTTPError, URLError
from urllib.parse import quote, urlsplit
from urllib.request import Request


# Only retain diagnostic fields; error responses can also echo request secrets.
DIAGNOSTIC_FIELDS = frozenset((
    "error", "code", "error_code", "message", "detail", "details", "diagnostics",
    "trace_id", "request_id", "run_id", "status", "reason", "retryable",
    "retry_after", "stage", "type", "summary", "within_scope_passed",
    "traceId", "classification", "retryAfterSeconds"))
SAFE_HEADERS = ("Content-Type", "Retry-After", "X-Request-ID", "X-Trace-ID", "Traceparent")


def safe_diagnostics(value, api_key):
    if isinstance(value, dict):
        return {key: safe_diagnostics(item, api_key)
                for key, item in value.items() if key in DIAGNOSTIC_FIELDS}
    if isinstance(value, list):
        return [safe_diagnostics(item, api_key) for item in value]
    if isinstance(value, str):
        value = value.replace(api_key, "[REDACTED]") if api_key else value
        value = re.sub(r"(?i)bearer\s+[^\s\"'<>]+", "Bearer [REDACTED]", value)
        # Free-form text may contain credentials other than the current API key.
        value = re.sub(r"(?i)(authorization|api[_-]?key|access[_-]?token|password|secret)"
                       r"(\s*[:=]\s*)[^\s,;\"'<>]+", r"\1\2[REDACTED]", value)
        return value
    return value


def http_failure(error, api_key):
    headers = {name.lower(): safe_diagnostics(error.headers[name], api_key)
               for name in SAFE_HEADERS if error.headers and error.headers.get(name)}
    # Bound error reads; never print raw HTML, malformed JSON, or arbitrary headers.
    with error:
        raw = error.read(65537)
    result = {"http_status": error.code, "headers": headers}
    if len(raw) > 65536:
        result["body_status"] = "too_large"
    else:
        try:
            body = json.loads(raw)
            if isinstance(body, dict):
                result["body"] = safe_diagnostics(body, api_key)
            else:
                result["body_status"] = "not_an_object"
        except (ValueError, UnicodeDecodeError):
            result["body_status"] = "not_json"
    print(json.dumps(result, indent=2))
    print(f"HTTP {error.code}: diagnostics saved; inspect your workspace before retrying.",
          file=sys.stderr)
    return 1


def run():
    names = ("CLEANEDWEB_API_BASE", "CLEANEDWEB_SOURCE_ID",
             "CLEANEDWEB_DEFINITION_VERSION", "CLEANEDWEB_API_KEY",
             "CLEANEDWEB_IDEMPOTENCY_KEY")
    values = {name: os.environ.get(name, "") for name in names}
    if not all(values.values()):
        raise ValueError("Set all five CLEANEDWEB connection and request environment variables.")
    base = values[names[0]].rstrip("/")
    origin = urlsplit(base)
    if (origin.scheme != "https" or not origin.hostname or origin.username
            or origin.password or origin.path or origin.query or origin.fragment):
        raise ValueError("CLEANEDWEB_API_BASE must be the supplied HTTPS origin.")
    version = values[names[2]]
    if not re.fullmatch(r"[0-9a-f]{64}", version):
        raise ValueError("Use the saved 64-character lowercase definition version.")
    if not re.fullmatch(r"[A-Za-z0-9._:-]{1,200}", values[names[4]]):
        raise ValueError("Use a saved valid idempotency key for this request.")
    payload = {"expected_version": version,
               "input": {"max_pages": 1, "detail_limit": 0}}
    request = Request(
        f"{base}/v1/sources/{quote(values[names[1]], safe='')}/run",
        data=json.dumps(payload).encode(),
        headers={"Authorization": f"Bearer {values[names[3]]}",
                 "Content-Type": "application/json",
                 "Idempotency-Key": values[names[4]]}, method="POST")
    # Refuse redirects rather than forwarding credentials to another endpoint.
    from urllib.request import HTTPRedirectHandler, build_opener

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

    try:
        with build_opener(NoRedirect).open(request, timeout=60) as response:
            result = json.load(response)
    except HTTPError as error:
        return http_failure(error, values[names[3]])
    # Preserve records AND summary before deciding whether output is usable.
    print(json.dumps(result, indent=2))
    if not isinstance(result, dict) or not isinstance(result.get("records"), list):
        raise ValueError("Unexpected response: inspect the generated contract.")
    summary = result.get("summary")
    metadata = result.get("metadata")
    if (not isinstance(summary, dict)
            or not isinstance(metadata, dict)
            or summary.get("definition_version") != version
            or metadata.get("definition_version") != version
            or summary.get("within_scope_passed") is not True
            or summary.get("status") not in ("completed", "bounded")):
        print("Result needs review; do not treat it as complete or retry automatically.", file=sys.stderr)
        return 2
    return 0


if __name__ == "__main__":
    try:
        sys.exit(run())
    except (URLError, TimeoutError, OSError):
        print("Connection failed; execution state may be unknown. Check your workspace before retrying.", file=sys.stderr)
        sys.exit(1)
    except (ValueError, TypeError):
        print("Invalid connection details or response; inspect the generated contract.", file=sys.stderr)
        sys.exit(1)
