"""Shared helpers for transcript role semantics.

These functions normalize behavior across runtime, summarization, and export
paths when tool outputs are represented as either:
1) native OpenAI tool turns (role=tool), or
2) XML carrier turns (role=user with <tool_response> blocks).
"""
from __future__ import annotations

import re
from typing import Any

_TOOL_RESPONSE_BLOCK_RE = re.compile(
    r"<tool_response>\s*([\s\S]*?)\s*</tool_response>",
    re.IGNORECASE,
)


def extract_inner_tool_response_text(content: Any) -> str:
    """Return concatenated inner text of <tool_response> blocks, else empty."""
    if not isinstance(content, str) or "<tool_response" not in content.lower():
        return ""
    parts = [m.group(1).strip() for m in _TOOL_RESPONSE_BLOCK_RE.finditer(content) if m.group(1).strip()]
    return "\n\n".join(parts).strip()


def is_tool_result_carrier_message(msg: dict[str, Any]) -> bool:
    """True for role=user turns that carry synthetic tool responses."""
    if not isinstance(msg, dict) or (msg.get("role") or "").strip() != "user":
        return False
    content = msg.get("content")
    if isinstance(content, str):
        return content.lstrip().startswith("<tool_response>")
    return False


# Harness-injected user-role guidance (loop-guard nudges, recovery prompts) and
# compression summaries. These carry role=user for prompt-boundary reasons but are
# NOT the human's voice: they must not satisfy "a real user message survived
# trimming", must not become fine-tune user turns, and must not drive recall
# signals. Prefix fallbacks cover messages reloaded from storage without flags.
# Motivating trace: 6be8ae2b — emergency trim kept only guard nudges, dropped the
# actual task request, and the model regenerated identical work until hard stop.
_INJECTED_GUIDANCE_PREFIXES: tuple[str, ...] = (
    "Heads up (recorded, not authoritative):",
    "Recovery nudge (recorded, not authoritative):",
)

_SUMMARY_CARRIER_PREFIX = "Summary of earlier conversation (older messages were compressed"


def is_injected_guidance_message(msg: dict[str, Any]) -> bool:
    """True for harness-injected user-role guidance (guard nudges, retries)."""
    if not isinstance(msg, dict) or (msg.get("role") or "").strip() != "user":
        return False
    if msg.get("_injected_guidance"):
        return True
    content = msg.get("content")
    if isinstance(content, str):
        stripped = content.lstrip()
        return stripped.startswith(_INJECTED_GUIDANCE_PREFIXES)
    return False


def is_summary_carrier_message(msg: dict[str, Any]) -> bool:
    """True for the synthetic user turn that carries a compressed-context summary."""
    if not isinstance(msg, dict) or (msg.get("role") or "").strip() != "user":
        return False
    content = msg.get("content")
    if isinstance(content, str):
        return content.lstrip().startswith(_SUMMARY_CARRIER_PREFIX)
    return False


def is_real_user_message(msg: dict[str, Any]) -> bool:
    """True if this is a genuine human user turn.

    Excludes tool-response carriers, idle ticks, harness-injected guidance
    (guard nudges), and compressed-context summary carriers.
    """
    if not isinstance(msg, dict) or (msg.get("role") or "").strip() != "user":
        return False
    if msg.get("_idle_tick"):
        return False
    if is_injected_guidance_message(msg) or is_summary_carrier_message(msg):
        return False
    content = msg.get("content")
    if isinstance(content, str):
        return not content.lstrip().startswith("<tool_response>")
    if isinstance(content, list):
        for part in content:
            if isinstance(part, dict) and part.get("type") == "text":
                text = (part.get("text") or "").lstrip()
                if text and not text.startswith("<tool_response>"):
                    return True
        return False
    return False


def tool_result_text_for_trim(msg: dict[str, Any]) -> str:
    """Return tool-result text across native and XML transcript shapes."""
    if not isinstance(msg, dict):
        return ""
    role = (msg.get("role") or "").strip()
    content = msg.get("content")
    if role == "tool" and isinstance(content, str):
        return content
    if role == "user" and isinstance(content, str):
        return extract_inner_tool_response_text(content)
    return ""
