"""B4: Failure Root Cause Analysis using LLM (gpt-4o-mini).

Only runs for sessions with error cascades or undo requests.
"""

import json
import os
import re

from dotenv import load_dotenv

from .base import BaseAnalyzer
from .helpers import has_error_cascade, has_undo_request, is_shell_error
from .llm_cache import get_cached, set_cache

load_dotenv(os.path.join(os.path.dirname(__file__), "..", "..", ".prod.env"), override=True)

VALID_ROOT_CAUSES = {
    "wrong_approach", "missing_context", "tool_limitation",
    "user_changed_mind", "ai_hallucination",
}

SYSTEM_PROMPT = """You are diagnosing why an AI coding session failed. Given the context of errors and user messages from a session that had failures, classify the root cause as ONE of:

- wrong_approach: AI took fundamentally wrong technical approach
- missing_context: AI lacked necessary context about codebase/requirements
- tool_limitation: AI's tools couldn't handle the task
- user_changed_mind: User changed requirements mid-session
- ai_hallucination: AI fabricated information (wrong APIs, nonexistent files, etc.)

Respond with ONLY valid JSON, no markdown:
{"failure_root_cause": "...", "failure_summary": "one-line explanation"}"""

_ERROR_PAT = re.compile(
    r"exit code [1-9]|Error:|FAILED|error:|command not found|No such file|"
    r"ERR!|Traceback|exit status [1-9]",
    re.IGNORECASE,
)
_UNDO_PAT = re.compile(
    r"\bundo\b|\brevert (this|that|the)\b|\broll back\b|\bgo back to\b|\bput it back\b",
    re.IGNORECASE,
)


def _extract_failure_context(stream: list[dict], max_chars: int = 3000) -> str:
    """Extract relevant error context from a unified message stream."""
    parts = []
    total = 0

    for m in stream:
        mt = m.get("msg_type", "")
        include = False

        if mt == "tool_result":
            output = m.get("result_output") or ""
            if _ERROR_PAT.search(output):
                include = True
                content = output
        elif mt == "user":
            content = m.get("content") or ""
            if _UNDO_PAT.search(content) or _ERROR_PAT.search(content):
                include = True
        else:
            continue

        if include:
            snippet = content[:500]
            if total + len(snippet) > max_chars:
                break
            parts.append(f"[{mt}]: {snippet}")
            total += len(snippet)

    return "\n---\n".join(parts) if parts else ""


class FailureRootCauseAnalyzer(BaseAnalyzer):
    name = "b04_failure_root_cause"

    def analyze(self, sessions_df, messages_df, tool_calls_df, tool_results_df, token_usage_df) -> dict:
        try:
            from openai import OpenAI
            api_key = os.environ.get("OPENAI_API_KEY")
            if not api_key:
                return {"error": "OPENAI_API_KEY not set", "per_session": []}
            client = OpenAI()
        except ImportError:
            return {"error": "openai package not installed", "per_session": []}

        per_session = []
        stats = {"total": 0, "skipped": 0, "cached": 0, "llm_called": 0, "errors": 0}

        for session in self.iter_sessions(sessions_df):
            sid = session["id"]
            stats["total"] += 1

            stream = self.build_message_stream(messages_df, tool_calls_df, tool_results_df, sid)
            if not stream:
                stats["skipped"] += 1
                continue

            if not has_error_cascade(stream) and not has_undo_request(stream):
                stats["skipped"] += 1
                continue

            context = _extract_failure_context(stream)
            if not context:
                stats["skipped"] += 1
                continue

            cache_key = f"failure_root_cause:{context}"
            cached = get_cached(cache_key)
            if cached:
                stats["cached"] += 1
                per_session.append({"session_id": sid, **cached})
                continue

            try:
                resp = client.chat.completions.create(
                    model="gpt-4o-mini", temperature=0,
                    messages=[
                        {"role": "system", "content": SYSTEM_PROMPT},
                        {"role": "user", "content": context[:4000]},
                    ],
                )
                result = json.loads(resp.choices[0].message.content.strip())
                if result.get("failure_root_cause") not in VALID_ROOT_CAUSES:
                    result["failure_root_cause"] = "wrong_approach"
                set_cache(cache_key, result)
                stats["llm_called"] += 1
                per_session.append({"session_id": sid, **result})
            except Exception as e:
                stats["errors"] += 1
                print(f"  [b04] LLM error for {sid[:8]}: {e}")

        print(f"  [b04] Stats: {stats}")
        return {"stats": stats, "total_sessions": len(per_session), "per_session": per_session}
