"""C1: Developer Behavioral Profile.

Aggregates across all sessions for a developer to produce a single profile dict.
Uses pricing.py for cost calculations — does NOT reimplement cost logic.
"""

import os
import re
import sys
from collections import Counter

import pandas as pd

sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))

from .base import BaseAnalyzer
from .helpers import (
    clean_user_content, get_first_user_prompt, detect_interrupt_pattern,
    categorize_tool, EDIT_TOOLS, EXPLORE_TOOLS,
)
from utils.pricing import calc_cost

_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,
)


class DeveloperProfiler(BaseAnalyzer):
    name = "c01_developer_profile"

    def analyze(self, sessions_df, messages_df, tool_calls_df, tool_results_df, token_usage_df) -> dict:
        if sessions_df.empty:
            return {"session_count": 0}

        total_sessions = len(sessions_df)
        sessions_with_interrupts = 0
        sessions_with_undos = 0
        sessions_with_edits = 0
        sessions_with_commits = 0
        first_prompt_lengths = []
        prompts_per_session = []
        autonomy_ratios = []
        explore_edit_ratios = []
        total_cost = 0.0
        source_counter = Counter()

        for session in self.iter_sessions(sessions_df):
            sid = session["id"]
            source = session.get("source") or "unknown"
            source_counter[source] += 1

            stream = self.build_message_stream(messages_df, tool_calls_df, tool_results_df, sid)
            if not stream:
                prompts_per_session.append(0)
                continue

            # User messages
            user_msgs = [m for m in stream if m["msg_type"] == "user"]
            prompts_per_session.append(len(user_msgs))

            # First prompt
            msg_list = [{**m, "msg_type": m["msg_type"]} for m in stream]
            fp = get_first_user_prompt(msg_list, source)
            if fp:
                first_prompt_lengths.append(len(fp))

            # Interrupts
            has_interrupt = any(
                detect_interrupt_pattern(m.get("result_output") or "") is not None
                for m in stream if m["msg_type"] == "tool_result"
            )
            if has_interrupt:
                sessions_with_interrupts += 1

            # Undo
            for m in user_msgs:
                content = (m.get("content") or "").lower()
                if _UNDO_PAT.search(content):
                    sessions_with_undos += 1
                    break

            # Tool calls from stream
            tool_msgs = [m for m in stream if m.get("tool_name")]
            tool_count = len(tool_msgs)

            # Edit/commit
            has_edit = any(m.get("tool_name") in EDIT_TOOLS for m in tool_msgs)
            has_commit = any(
                "git commit" in (m.get("tool_input") or "")
                for m in tool_msgs
            )
            if has_edit:
                sessions_with_edits += 1
            if has_commit:
                sessions_with_commits += 1

            # Autonomy ratio
            if user_msgs:
                autonomy_ratios.append(tool_count / len(user_msgs))

            # Explore/edit ratio
            explore_count = sum(1 for m in tool_msgs if m.get("tool_name") in EXPLORE_TOOLS)
            edit_count = sum(1 for m in tool_msgs if m.get("tool_name") in EDIT_TOOLS)
            explore_edit_ratios.append(explore_count / max(edit_count, 1))

        # Cost from token_usage_df
        if not token_usage_df.empty:
            for sid in sessions_df["id"].tolist():
                sid_tokens = token_usage_df[token_usage_df["session_id"] == sid]
                if sid_tokens.empty:
                    continue
                for _, row in sid_tokens.iterrows():
                    model = row.get("model") or ""
                    cost = calc_cost(
                        model,
                        input_tokens=row.get("input_tokens", 0) or 0,
                        output_tokens=row.get("output_tokens", 0) or 0,
                        cached_tokens=row.get("cached_tokens", 0) or 0,
                    )
                    total_cost += cost

        # Active days
        if "first_seen" in sessions_df.columns:
            dates = pd.to_datetime(sessions_df["first_seen"]).dt.date
            active_days = dates.nunique()
        else:
            active_days = 1

        profile = {
            "session_count": total_sessions,
            "sessions_per_active_day": round(total_sessions / max(active_days, 1), 2),
            "preferred_source": source_counter.most_common(1)[0][0] if source_counter else "unknown",
            "avg_prompts_per_session": round(sum(prompts_per_session) / max(len(prompts_per_session), 1), 2),
            "avg_first_prompt_length": round(sum(first_prompt_lengths) / max(len(first_prompt_lengths), 1), 1),
            "interrupt_rate": round(sessions_with_interrupts / max(total_sessions, 1), 3),
            "undo_rate": round(sessions_with_undos / max(total_sessions, 1), 3),
            "edit_session_pct": round(sessions_with_edits / max(total_sessions, 1), 3),
            "commit_session_pct": round(sessions_with_commits / max(total_sessions, 1), 3),
            "avg_autonomy_ratio": round(sum(autonomy_ratios) / max(len(autonomy_ratios), 1), 2),
            "exploration_tendency": round(sum(explore_edit_ratios) / max(len(explore_edit_ratios), 1), 2),
            "total_cost_usd": round(total_cost, 2),
        }

        return {"total_sessions": total_sessions, "profile": profile}
