"""
LLMReasoningAgent

⚠️ IMPORTANT DESIGN SHIFT (INTENTIONAL):

- Correctness is NEVER decided by an LLM
- Deterministic HTTP + mutation rules come FIRST
- This file no longer REQUIRES OpenAI to function
- LLM can be added later ONLY for explanation enrichment

This fixes:
- happy_path incorrectly marked FAILED
- random "unknown" classifications
- non-deterministic release decisions
"""


class LLMReasoningAgent:
    """
    Classifies API test execution outcomes.

    INPUT (execution_result):
    {
        "name": str,
        "mutation": dict | None,
        "response": {
            "status_code": int,
            "body": dict | str
        }
    }

    OUTPUT:
    {
        "type": str,
        "confidence": float,
        "explanation": str
    }
    """

    def reason(self, execution_result: dict) -> dict:
        test_name = execution_result.get("name")
        mutation = execution_result.get("mutation")
        response = execution_result.get("response") or {}

        status_code = response.get("status_code")
        response_body = response.get("body")

        # =========================================================
        # 1️⃣ HARD SUCCESS RULE — HAPPY PATH
        # =========================================================
        if (
                test_name == "happy_path"
                and mutation is None
                and isinstance(status_code, int)
                and 200 <= status_code < 300
        ):
            return {
                "type": "success",
                "confidence": 1.0,
                "explanation": "Happy path executed successfully (2xx response)"
            }

        # =========================================================
        # 2️⃣ NO RESPONSE / TRANSPORT FAILURE
        # =========================================================
        if status_code is None:
            return {
                "type": "unknown",
                "confidence": 0.3,
                "explanation": "No HTTP response received from server"
            }

        # =========================================================
        # 3️⃣ HTTP-LEVEL HARD RULES (NO AI)
        # =========================================================
        # Method misuse tests: 404/405 are expected (endpoint not available for that method)
        if status_code in (404, 405):
            if (
                isinstance(test_name, str)
                and test_name.startswith("wrong_method_")
            ) or (mutation and mutation.get("strategy") == "method_misuse"):
                return {
                    "type": "method_not_allowed" if status_code == 405 else "not_found",
                    "confidence": 1.0,
                    "explanation": f"HTTP {status_code} for wrong-method negative test (expected)"
                }

        if status_code == 405:
            # 405 outside method-misuse tests can still be informative
            return {
                "type": "method_not_allowed",
                "confidence": 1.0,
                "explanation": "HTTP 405 Method Not Allowed"
            }

        if status_code == 404:
            return {
                "type": "not_found",
                "confidence": 0.9,
                "explanation": "HTTP 404 Not Found"
            }

        if status_code in (401, 403):
            return {
                "type": "auth_issue",
                "confidence": 1.0,
                "explanation": "Authentication or authorization failure"
            }

        if status_code >= 500:
            return {
                "type": "infra",
                "confidence": 1.0,
                "explanation": "Server-side 5xx failure"
            }

        # =====================================================
        # 4️⃣ MUTATION-AWARE SEMANTIC CLASSIFICATION
        # =====================================================
        if mutation:
            strategy = mutation.get("strategy")

            # Header tampering / content negotiation tests
            if status_code == 406 and strategy == "headers":
                return {
                    "type": "content_negotiation",
                    "confidence": 1.0,
                    "explanation": "406 Not Acceptable after Accept/header mutation (expected)"
                }

            # Header tampering accepted (2xx) can be risky depending on the mutation
            if 200 <= status_code < 300 and strategy == "headers":
                return {
                    "type": "headers_accepted",
                    "confidence": 0.8,
                    "explanation": "Header mutation still returned 2xx (review if expected)"
                }

            # Wrong-method negative test accepted => risk
            if 200 <= status_code < 300 and strategy == "method_misuse":
                return {
                    "type": "method_risk",
                    "confidence": 1.0,
                    "explanation": "Wrong HTTP method was accepted with 2xx (unexpected)"
                }

            if status_code == 400 and strategy == "missing_param":
                return {
                    "type": "missing_param",
                    "confidence": 0.9,
                    "explanation": "400 response after required parameter removal"
                }

            # Missing param accepted (2xx) is often weak validation (risky)
            if 200 <= status_code < 300 and strategy == "missing_param":
                return {
                    "type": "missing_param_accepted",
                    "confidence": 1.0,
                    "explanation": "Parameter removal still returned 2xx (potential missing validation)"
                }

            if status_code == 400 and strategy == "null_param":
                return {
                    "type": "missing_param",
                    "confidence": 0.9,
                    "explanation": "400 response after nullifying parameter"
                }

            if 200 <= status_code < 300 and strategy == "null_param":
                return {
                    "type": "null_param_accepted",
                    "confidence": 1.0,
                    "explanation": "Nullified parameter still returned 2xx (potential weak validation)"
                }

            if status_code == 400 and strategy == "invalid_param":
                return {
                    "type": "invalid_param",
                    "confidence": 0.9,
                    "explanation": "400 response after invalid parameter mutation"
                }

            if 200 <= status_code < 300 and strategy == "invalid_param":
                return {
                    "type": "invalid_param_accepted",
                    "confidence": 1.0,
                    "explanation": "Invalid parameter still returned 2xx (potential weak validation)"
                }

            # ✅ FIX: security payload blocked → SAFE
            if status_code >= 400 and status_code < 500 and strategy == "security":
                return {
                    "type": "security",
                    "confidence": 1.0,
                    "explanation": (
                        "Security payload was rejected with client error "
                        "(treated as safe input validation)"
                    )
                }

            # 🚨 security payload accepted → RISK
            if status_code >= 200 and status_code < 300 and strategy == "security":
                return {
                    "type": "security_risk",
                    "confidence": 1.0,
                    "explanation": (
                        "Security payload was accepted (potential vulnerability)"
                    )
                }

        # =========================================================
        # 5️⃣ BUSINESS LOGIC / GENERIC SUCCESS
        # =========================================================
        if 200 <= status_code < 300:
            return {
                "type": "success",
                "confidence": 0.8,
                "explanation": "2xx response without mutation-induced failure"
            }

        # =========================================================
        # 6️⃣ FALLBACK
        # =========================================================
        return {
            "type": "unknown",
            "confidence": 0.6,
            "explanation": "No deterministic rule matched this response"
        }
