#!/usr/bin/env python3
"""Small Readiness API client. Requires Python 3 and no third-party packages."""
import argparse
import json
import os
import sys
import time
import urllib.error
import urllib.parse
import urllib.request
import uuid


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


def request(base, path, token=None, body=None, key=None):
    url = urllib.parse.urljoin(base.rstrip("/") + "/", path)
    target = urllib.parse.urlsplit(url)
    origin = urllib.parse.urlsplit(base)
    if (target.scheme, target.netloc) != (origin.scheme, origin.netloc):
        raise ValueError("The API returned a status URL outside this API origin.")
    headers = {"Accept": "application/json", "User-Agent": "ReadinessCLI/1.0"}
    if token:
        headers["Authorization"] = "Bearer " + token
    if body is not None:
        headers["Content-Type"] = "application/json"
        headers["Idempotency-Key"] = key or str(uuid.uuid4())
    req = urllib.request.Request(url, data=json.dumps(body).encode() if body is not None else None, headers=headers)
    try:
        with urllib.request.build_opener(NoRedirect()).open(req, timeout=20) as response:
            return json.load(response), response.headers
    except urllib.error.HTTPError as error:
        try:
            problem = json.load(error)
            detail = problem.get("detail", "API request failed")
        except (ValueError, AttributeError):
            detail = "API request failed"
        retry = error.headers.get("Retry-After")
        raise ValueError(f"HTTP {error.code}: {detail}" + (f" Retry after {retry} seconds." if retry else "")) from None


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--base-url", default=os.getenv("READINESS_API_URL", "https://agentnsight.com"))
    commands = parser.add_subparsers(dest="command", required=True)
    scan = commands.add_parser("scan", help="Start a scan and wait for its report")
    scan.add_argument("url")
    scan.add_argument("--site-type", choices=["auto", "content", "api", "commerce"], default="auto")
    scan.add_argument("--idempotency-key")
    scan.add_argument("--timeout", type=int, default=180)
    for name in ["score", "checks"]:
        command = commands.add_parser(name, help="Read stored data without starting work")
        if name == "score":
            command.add_argument("domain")
    for command in [scan, *[commands.choices[n] for n in ["score", "checks"]]]:
        command.add_argument("--json", action="store_true")
        if command is not commands.choices["checks"]:
            command.add_argument("--min-score", type=int, choices=range(101), metavar="0..100")
    args = parser.parse_args()
    token = os.getenv("READINESS_API_TOKEN")
    if args.command == "scan":
        if not token:
            parser.error("Set READINESS_API_TOKEN to your account bearer token.")
        data, headers = request(args.base_url, "/api/v1/scans", token, {
            "url": args.url, "capture_profile": "static_v28", "site_type": args.site_type
        }, args.idempotency_key)
        deadline = time.monotonic() + args.timeout
        while data["scan"]["status"] not in ["completed", "failed"]:
            if time.monotonic() >= deadline:
                raise ValueError("The scan is still pending. Read " + data["scan"]["status_url"])
            time.sleep(min(5, max(1, int(headers.get("Retry-After", "3")))))
            data, headers = request(args.base_url, data["scan"]["status_url"], token)
        report = data["scan"]
    elif args.command == "score":
        report, _ = request(args.base_url, "/api/v1/scores/" + urllib.parse.quote(args.domain, safe=""))
    else:
        report, _ = request(args.base_url, "/api/v1/checks")
    if args.json or args.command == "checks":
        print(json.dumps(report, indent=2))
    else:
        evaluation = report.get("evaluation") or {}
        print(f"{report['domain']}: {evaluation.get('score') if evaluation.get('score') is not None else 'No score'} / 100")
        summary = evaluation.get("summary") or {}
        coverage = f"{summary['coverage_bps'] / 100:.1f}%" if summary.get("coverage_bps") is not None else "not measured"
        print(f"Model {evaluation.get('criteria_set', {}).get('version')}; scope {summary.get('site_type', 'legacy')}; coverage {coverage}")
        print(urllib.parse.urljoin(args.base_url, "/scans/" + report["scan_id"]))
    threshold = getattr(args, "min_score", None)
    if threshold is not None:
        evaluation = report.get("evaluation") or {}
        score = evaluation.get("score")
        if report.get("status") != "completed" or score is None:
            return 2
        return 0 if score >= threshold else 1
    return 0


if __name__ == "__main__":
    try:
        sys.exit(main())
    except (ValueError, KeyError, OSError, urllib.error.URLError) as error:
        print(str(error), file=sys.stderr)
        sys.exit(2)
