#!/usr/bin/env python3
"""Run a Nefissiez product audit. Python 3.10+, standard library only.

Set APIFY_TOKEN in your environment. Then:
  python run_audit.py catalog-input.json --output audit.csv --max-charge 0.5
This starts one billable Apify run. No automatic retry of the start request.
"""
import argparse
import json
import math
import os
import sys
import time
import urllib.error
import urllib.parse
import urllib.request
from pathlib import Path

BASE = "https://api.apify.com/v2"

class NoRedirect(urllib.request.HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        raise RuntimeError("Unexpected API redirect; request stopped.")

def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("input", type=Path)
    parser.add_argument("--output", type=Path, default=Path("audit.csv"))
    parser.add_argument("--max-charge", type=float, default=0.5)
    args = parser.parse_args()
    if not math.isfinite(args.max_charge) or args.max_charge <= 0:
        parser.error("--max-charge must be a positive finite USD amount")
    token = os.environ.get("APIFY_TOKEN", "").strip()
    if not token:
        parser.error("Set APIFY_TOKEN in your environment; never paste it into a shared file.")
    payload = json.loads(args.input.read_text(encoding="utf-8-sig"))
    if not isinstance(payload, dict) or not payload.get("catalog"):
        parser.error("This CSV runner requires a nonempty catalog array in the input file.")
    opener = urllib.request.build_opener(NoRedirect)
    def api(path, method="GET", body=None, raw=False):
        request = urllib.request.Request(BASE + path, method=method,
            headers={"Authorization": "Bearer " + token, "Content-Type": "application/json"},
            data=json.dumps(body).encode() if body is not None else None)
        with opener.open(request, timeout=65) as response:
            content = response.read(2_000_001)
            if len(content) > 2_000_000:
                raise RuntimeError("API response exceeds the runner's size limit.")
        if raw:
            return content
        result = json.loads(content)
        return result.get("data", result) if isinstance(result, dict) else result
    query = urllib.parse.urlencode({"build":"latest", "memory":256, "timeout":180, "maxTotalChargeUsd":args.max_charge, "waitForFinish":0})
    try:
        run = api("/actors/nefissiez~product-offer-extractor/runs?" + query, "POST", payload)
    except (TimeoutError, urllib.error.URLError):
        raise RuntimeError("The start response was not received. Check your Apify runs before trying again to avoid a duplicate run.") from None
    print("Started run:", run["id"], flush=True)
    print("Console: https://console.apify.com/actors/runs/" + run["id"], flush=True)
    deadline = time.monotonic() + 300
    while run["status"] in ("READY", "RUNNING", "TIMING-OUT", "ABORTING"):
        if time.monotonic() > deadline:
            raise RuntimeError("Stopped waiting. Check the run in Console; the Actor has a 180-second run timeout.")
        run = api("/actor-runs/" + urllib.parse.quote(run["id"], safe="") + "?waitForFinish=30")
    if run["status"] != "SUCCEEDED":
        raise RuntimeError("Run finished with status " + run["status"] + ". Check its log in Apify Console.")
    store = urllib.parse.quote(run["defaultKeyValueStoreId"], safe="")
    content = api("/key-value-stores/" + store + "/records/AUDIT.csv", raw=True)
    args.output.write_bytes(content)
    summary = api("/key-value-stores/" + store + "/records/SUMMARY")
    print("Saved report:", args.output)
    print("Verdicts:", json.dumps(summary.get("auditCounts", {})))
    print("Stop reason:", summary.get("stopReason"))
    if summary.get("stopReason") != "completed":
        print("The report is partial. Review the spending cap before running any remaining pages.")

if __name__ == "__main__":
    try:
        main()
    except urllib.error.HTTPError as error:
        print("Apify returned HTTP", error.code, "— check your token, input and account limits.", file=sys.stderr)
        sys.exit(1)
    except (ValueError, OSError, RuntimeError, KeyError) as error:
        print(str(error), file=sys.stderr)
        sys.exit(1)
