import argparse
import base64
import json
from pathlib import Path
from urllib.parse import urlparse


def image_url_issues(body):
    issues = set()
    for message in body.get("messages", []):
        content = message.get("content")
        if not isinstance(content, list):
            continue
        for part in content:
            if part.get("type") == "input_image":
                issues.add("chat-image-shape")
            if part.get("type") != "image_url":
                continue
            value = part.get("image_url")
            if not isinstance(value, dict) or not isinstance(value.get("url"), str):
                issues.add("chat-image-shape")
                continue
            url = value["url"]
            if url.startswith("data:image/"):
                try:
                    head, data = url.split(",", 1)
                    if ";base64" not in head or not base64.b64decode(data, validate=True):
                        raise ValueError("Invalid data URL")
                except (ValueError, base64.binascii.Error):
                    issues.add("image-data-invalid")
            else:
                parsed = urlparse(url)
                if parsed.scheme != "https" or not parsed.netloc or parsed.username or parsed.password:
                    issues.add("image-url-not-fetchable")
    return issues


def protocol_issues(request):
    endpoint, body = request["endpoint"], request["body"]
    issues = set()
    if endpoint == "/v1/chat/completions":
        if not isinstance(body.get("messages"), list):
            issues.add("chat-messages-required")
        if "input" in body or "previous_response_id" in body:
            issues.add("chat-responses-fields")
        issues |= image_url_issues(body)
    elif endpoint == "/v1/responses":
        if "input" not in body:
            issues.add("responses-input-required")
        if "messages" in body:
            issues.add("responses-chat-fields")
    elif endpoint == "/v1/messages":
        if not isinstance(body.get("max_tokens"), int) or body["max_tokens"] <= 0:
            issues.add("messages-max-tokens-required")
        for message in body.get("messages", []):
            for part in message.get("content", []) if isinstance(message.get("content"), list) else []:
                if part.get("type") in {"image_url", "input_image"}:
                    issues.add("messages-image-shape")
    else:
        issues.add("unknown-protocol")
    return issues


def history_issues(body):
    issues, pending, seen = set(), set(), set()
    for message in body.get("messages", []):
        role = message.get("role")
        if role == "tool":
            identity = message.get("tool_call_id")
            if identity not in pending:
                issues.add("tool-result-orphan")
            else:
                pending.remove(identity)
            continue
        if pending:
            issues.add("tool-results-incomplete")
            pending.clear()
        for call in message.get("tool_calls", []):
            identity = call.get("id")
            if role != "assistant" or not isinstance(identity, str) or not identity:
                issues.add("tool-call-shape")
                continue
            if identity in seen:
                issues.add("tool-call-id-duplicate")
            seen.add(identity)
            pending.add(identity)
    if pending:
        issues.add("tool-results-incomplete")
    return issues


def main():
    parser = argparse.ArgumentParser(description="Offline fixture validation; no API request")
    parser.add_argument("files", nargs="*")
    args = parser.parse_args()
    paths = [Path(value) for value in args.files] or sorted(Path(__file__).parent.glob("*-fixtures.json"))
    checked = 0
    for path in paths:
        if path.stat().st_size > 128 * 1024:
            raise ValueError("Fixture file too large")
        data = json.loads(path.read_text())
        for case in data["cases"]:
            check = history_issues if case["kind"] == "tool-history" else protocol_issues
            before = check(case["before"])
            after = check(case["after"])
            if before != set(case["expectedIssues"]) or after:
                raise AssertionError(f"Fixture mismatch: {case['id']}")
            checked += 1
    print(json.dumps({"cases": checked, "checks": checked * 2, "status": "passed", "boundary": "Local shape and history tests only; does not fetch images, execute tools or validate all vendor fields."}, ensure_ascii=False))


if __name__ == "__main__":
    main()
