"""A1: Interruption Analysis — per-session analysis of interruption events."""

from datetime import datetime

from .base import BaseAnalyzer
from .helpers import (
    detect_interrupt_pattern,
    extract_inline_guidance,
    categorize_tool,
    REJECTED_SIG,
    STOP_SIG,
)


class InterruptionAnalyzer(BaseAnalyzer):
    name = "a01_interruptions"

    def analyze(self, sessions_df, messages_df, tool_calls_df, tool_results_df, token_usage_df) -> dict:
        per_session = []
        total_interrupts = 0
        sessions_with = 0

        for session in self.iter_sessions(sessions_df):
            sid = session["id"]
            stream = self.build_message_stream(messages_df, tool_calls_df, tool_results_df, sid)
            if not stream:
                continue

            result = self._analyze_session(stream)
            result["session_id"] = sid
            per_session.append(result)

            if result["interrupt_count"] > 0:
                total_interrupts += result["interrupt_count"]
                sessions_with += 1

        return {
            "total_interrupts": total_interrupts,
            "sessions_with_interrupts": sessions_with,
            "total_sessions": len(per_session),
            "per_session": per_session,
        }

    def _analyze_session(self, stream: list[dict]) -> dict:
        total_msgs = len(stream)
        interrupts = []

        for i, m in enumerate(stream):
            if m["msg_type"] != "tool_result":
                continue

            output = m.get("result_output") or ""
            pattern = detect_interrupt_pattern(output)
            if pattern is None:
                continue

            pos_pct = round(i / max(total_msgs, 1) * 100, 1)

            # Walk backwards to find interrupted tool
            tool_name = "unknown"
            for j in range(i - 1, -1, -1):
                if stream[j]["msg_type"] == "tool_call" and stream[j].get("tool_name"):
                    tool_name = stream[j]["tool_name"]
                    break

            tool_category = categorize_tool(tool_name)

            # Extract guidance based on pattern type
            guidance_source = "abandoned"
            guidance_text = ""
            time_to_next_msg_sec = None

            if pattern["pattern"] == "inline":
                guidance_source = "inline"
                guidance_text = pattern["guidance_text"] or ""
            elif pattern["pattern"] == "stop":
                guidance_source = "stop"
                gt, td = self._find_next_user_guidance(stream, i, m)
                if gt:
                    guidance_text = gt
            elif pattern["pattern"] in ("rejected", "codex_abort"):
                gt, td = self._find_next_user_guidance(stream, i, m)
                if gt:
                    guidance_source = "next_msg"
                    guidance_text = gt
                    time_to_next_msg_sec = td

            interrupts.append({
                "msg_index": i,
                "tool_name": tool_name,
                "tool_category": tool_category,
                "position_pct": pos_pct,
                "pattern": pattern["pattern"],
                "guidance_source": guidance_source,
                "guidance_text": guidance_text[:500] if guidance_text else "",
                "time_to_next_msg_sec": time_to_next_msg_sec,
            })

        interrupt_count = len(interrupts)
        first_pos = interrupts[0]["position_pct"] if interrupts else None
        position_bucket = self._compute_position_bucket(interrupts)
        session_recovered = self._check_recovery(stream, interrupts)

        return {
            "interrupt_count": interrupt_count,
            "first_interrupt_position_pct": first_pos,
            "interrupt_position_bucket": position_bucket,
            "session_recovered": session_recovered,
            "interrupts": interrupts,
        }

    def _find_next_user_guidance(self, stream, start_idx, current_msg):
        current_ts = current_msg.get("timestamp")
        for j in range(start_idx + 1, len(stream)):
            nxt = stream[j]
            # Skip consecutive interrupt tool_results
            if nxt["msg_type"] == "tool_result":
                nxt_output = nxt.get("result_output") or ""
                if REJECTED_SIG in nxt_output or STOP_SIG in nxt_output:
                    continue
            if nxt["msg_type"] == "user" and nxt.get("content"):
                time_diff = None
                if current_ts and nxt.get("timestamp"):
                    try:
                        t1 = datetime.fromisoformat(str(current_ts).replace("Z", "+00:00"))
                        t2 = datetime.fromisoformat(str(nxt["timestamp"]).replace("Z", "+00:00"))
                        time_diff = round((t2 - t1).total_seconds(), 1)
                    except (ValueError, TypeError):
                        pass
                return nxt["content"][:500], time_diff
            if nxt["msg_type"] in ("assistant", "tool_call"):
                break
        return "", None

    def _compute_position_bucket(self, interrupts):
        if not interrupts:
            return None
        early = sum(1 for x in interrupts if x["position_pct"] < 33)
        late = sum(1 for x in interrupts if x["position_pct"] >= 66)
        total = len(interrupts)
        if early > total * 0.6:
            return "mostly_early"
        if late > total * 0.6:
            return "mostly_late"
        return "spread"

    def _check_recovery(self, stream, interrupts):
        if not interrupts:
            return False
        last_idx = interrupts[-1]["msg_index"]
        user_after = sum(1 for m in stream[last_idx + 1:] if m["msg_type"] == "user")
        return user_after > 2
