"""
SealVera interceptors — auto-patch OpenAI (and other LLM clients) to log all calls.
"""

import json
import datetime
import threading
import functools
from .capture import consume


def _infer_decision(text: str) -> str:
    """
    Extract a decision label from LLM output text.

    Tries JSON parsing first so structured responses (OpenAI json_object mode,
    OpenRouter, Anthropic) have their 'decision' field extracted directly.
    Falls back to keyword scan for plain-text responses.
    """
    if not text:
        return "completed"

    # ── 1. Try JSON parse — covers json_object mode and structured outputs ──
    stripped = text.strip()
    if stripped.startswith("{"):
        try:
            parsed = json.loads(stripped)
            # Direct 'decision' key
            if isinstance(parsed.get("decision"), str) and parsed["decision"]:
                return parsed["decision"].upper()
            # Fallback keys used by some agents
            for key in ("action", "status", "outcome", "result", "verdict"):
                if isinstance(parsed.get(key), str) and parsed[key]:
                    val = parsed[key].upper()
                    # Only return if it looks like a decision word, not a description
                    if any(w in val for w in ("APPROV", "REJECT", "DENY", "DENIED", "FLAG",
                                              "CLEAR", "BLOCK", "ALLOW", "COMPLET", "REVIEW",
                                              "PEND", "APPROV", "PASS", "FAIL")):
                        return val
        except Exception:
            pass

    # ── 2. Keyword scan for plain-text responses ───────────────────────────
    upper = text.upper()
    # Order matters — check more specific terms first
    for keyword, label in [
        ("APPROVED",       "APPROVED"),
        ("DENIED",         "DENIED"),
        ("REJECTED",       "REJECTED"),
        ("FLAGGED",        "FLAGGED"),
        ("PENDING_REVIEW", "PENDING_REVIEW"),
        ("PENDING REVIEW", "PENDING_REVIEW"),
        ("FAST_TRACK",     "FAST_TRACK"),
        ("HIGH_RISK",      "HIGH_RISK"),
        ("CONDITIONAL",    "CONDITIONAL"),
        ("ALLOWED",        "APPROVED"),
        ("BLOCKED",        "REJECTED"),
        ("CLEARED",        "APPROVED"),
        ("PASSED",         "APPROVED"),
        ("FAILED",         "REJECTED"),
    ]:
        if keyword in upper:
            return label

    return "completed"


def patch_openai(openai_module_or_client, send_log_fn, config: dict):
    """
    Patch the openai client to log all chat.completions.create calls.

    Args:
        openai_module_or_client: The openai module or a client instance
        send_log_fn: Function to send logs (from client.py)
        config: SealVera config dict
    """


    # Support both module-level patching and client instance patching
    if hasattr(openai_module_or_client, "chat"):
        # It's a client instance
        clients = [openai_module_or_client]
    elif hasattr(openai_module_or_client, "OpenAI"):
        # It's the openai module — patch the class
        clients = []
        _patch_openai_class(openai_module_or_client.OpenAI, send_log_fn, config)
        return
    else:
        clients = [openai_module_or_client]

    for client in clients:
        _patch_openai_instance(client, send_log_fn, config)


def _patch_openai_class(OpenAI_class, send_log_fn, config):
    """Patch the OpenAI class so all instances are patched."""
    original_init = OpenAI_class.__init__

    @functools.wraps(original_init)
    def patched_init(self, *args, **kwargs):
        original_init(self, *args, **kwargs)
        _patch_openai_instance(self, send_log_fn, config)

    OpenAI_class.__init__ = patched_init
    print("[SealVera] ✓ Patched OpenAI class")


def _patch_openai_instance(client, send_log_fn, config):
    """Patch a specific OpenAI client instance."""


    # Use `is True` so MagicMock attributes (which are truthy but not True)
    # don't cause the guard to fire on mocked clients in user test suites.
    if getattr(client, "_sealvera_patched", None) is True:
        return
    client._sealvera_patched = True

    original_create = client.chat.completions.create

    @functools.wraps(original_create)
    def patched_create(*args, **kwargs):
        started_at = datetime.datetime.utcnow().isoformat() + "Z"

        # Auto-reasoning injection for OpenAI-style messages
        if "messages" in kwargs:
            from .reasoning import inject_auto_reasoning_messages
            kwargs = {**kwargs, "messages": inject_auto_reasoning_messages(kwargs["messages"], config)}

        response = original_create(*args, **kwargs)

        try:
            choice = response.choices[0]
            content = choice.message.content or ""
            model = response.model  # model name from response (e.g. "gpt-4o-mini")
            messages = kwargs.get("messages", args[0] if args else [])

            action = "llm_call"
            if messages:
                last = messages[-1]
                words = str(last.get("content", "")).split()[:6]
                action = " ".join(words)[:40] or "llm_call"

            # Extract reasoning_steps from JSON response if present (native evidence)
            reasoning_steps = None
            evidence_source = None
            try:
                parsed = json.loads(content)
                if isinstance(parsed.get("reasoning_steps"), list):
                    reasoning_steps = parsed["reasoning_steps"]
                    evidence_source = "native"
            except Exception:
                pass

            captured = consume()

            def log_async():
                try:
                    send_log_fn({
                        "timestamp": started_at,
                        "agent": config.get("agent", "ai-agent"),
                        "action": action,
                        "decision": _infer_decision(content),
                        "model": model,           # top-level — server reads entry.model
                        "model_used": model,      # alias for compatibility
                        "input": {"messages": messages, "model": kwargs.get("model")},
                        "output": {"content": content, "model": model},
                        "reasoning": "",
                        "reasoning_steps": reasoning_steps,
                        "evidence_source": evidence_source,
                        "raw_context": {"captured_params": captured, "provider": "openai"},
                        "provider": "openai"
                    })
                except Exception as e:
                    if config.get("debug"):
                        print(f"[SealVera] Log failed (non-fatal): {e}")

            threading.Thread(target=log_async, daemon=True).start()
        except Exception as e:
            if config.get("debug"):
                print(f"[SealVera] Interceptor error (non-fatal): {e}")

        return response

    client.chat.completions.create = patched_create
    print("[SealVera] ✓ Patched openai.chat.completions.create")
