# qa-copilot/contract_gen.py
"""
API contract test generation from OpenAPI/Swagger specs (#16).

Parses an OpenAPI 3.x spec, extracts endpoints with request/response schemas,
and uses LLM to generate contract tests.
"""
from __future__ import annotations

import json
from llm_providers import llm_json


def extract_endpoints(spec: dict) -> list[dict]:
    endpoints = []
    paths = spec.get("paths", {})
    for path, methods in paths.items():
        for method, details in methods.items():
            if method.upper() not in ("GET", "POST", "PUT", "PATCH", "DELETE"):
                continue
            ep = {
                "path": path,
                "method": method.upper(),
                "operation_id": details.get("operationId", ""),
                "summary": details.get("summary", ""),
                "responses": {},
                "request_body": None,
            }
            for status, resp_detail in details.get("responses", {}).items():
                schema = None
                content = resp_detail.get("content", {})
                for media_type, media_detail in content.items():
                    if "schema" in media_detail:
                        schema = media_detail["schema"]
                        break
                ep["responses"][status] = {
                    "description": resp_detail.get("description", ""),
                    "schema": schema,
                }
            req_body = details.get("requestBody", {})
            if req_body:
                content = req_body.get("content", {})
                for media_type, media_detail in content.items():
                    if "schema" in media_detail:
                        ep["request_body"] = {
                            "required": req_body.get("required", False),
                            "schema": media_detail["schema"],
                        }
                        break
            endpoints.append(ep)
    return endpoints


def build_contract_prompt(endpoints: list[dict], framework: str = "restassured") -> str:
    lines = [
        f"Generate {framework} API contract tests for the following endpoints.",
        "For each endpoint, generate tests that verify:",
        "- HTTP status codes match the spec",
        "- Response body matches the schema (required fields, types)",
        "- Invalid requests return appropriate error codes",
        "",
        "Endpoints:",
    ]
    for ep in endpoints:
        lines.append(f"\n### {ep['method']} {ep['path']}")
        if ep["summary"]:
            lines.append(f"Summary: {ep['summary']}")
        for status, resp in ep["responses"].items():
            desc = resp["description"]
            lines.append(f"  Response {status}: {desc}")
            if resp["schema"]:
                lines.append(f"  Schema: {json.dumps(resp['schema'], indent=2)}")
        if ep["request_body"]:
            required = "required" if ep["request_body"]["required"] else "optional"
            lines.append(f"  Request body ({required}): {json.dumps(ep['request_body']['schema'], indent=2)}")
    return "\n".join(lines)


_CONTRACT_SCHEMA = {
    "type": "object",
    "properties": {
        "tests": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "name": {"type": "string"},
                    "method": {"type": "string"},
                    "path": {"type": "string"},
                    "assertions": {"type": "array", "items": {"type": "string"}},
                    "code": {"type": "string"},
                },
                "required": ["name", "method", "path", "assertions"],
            },
        }
    },
    "required": ["tests"],
}


def generate_contract_tests(
    endpoints: list[dict],
    provider: str = "ollama",
    model: str = "",
    framework: str = "restassured",
) -> dict:
    prompt = build_contract_prompt(endpoints, framework=framework)
    system = (
        "You are a QA engineer generating API contract tests. "
        "Return a JSON object with a 'tests' array. Each test has: "
        "name, method, path, assertions (list of strings), and optionally code."
    )
    try:
        result = llm_json(
            system_prompt=system,
            user_prompt=prompt,
            provider=provider,
            model=model,
            temperature=0.2,
            schema=_CONTRACT_SCHEMA,
        )
        if isinstance(result, dict) and "tests" in result:
            return result
        return {"tests": []}
    except Exception as e:
        print(f"  ⚠  Contract generation failed: {e}")
        return {"tests": []}
