#!/usr/bin/env python3
"""Response construction and post-processing.

Builds LLM prompts, parses response tags (LEARNING, RESEARCH, SOURCE, CLAIM,
CONFIDENCE), strips internal tags from display output.

All functions are stateless. No Telegram types, no SQLite, no module-level state.

Extracted from bot.py (Ganymede decomposition spec).
"""

import logging
import re
from dataclasses import dataclass, field

logger = logging.getLogger("tg.response")


@dataclass
class ParsedResponse:
    """Result of parsing Rio's raw LLM response."""
    display_text: str
    confidence: float | None
    learnings: list[tuple[str, str]] = field(default_factory=list)  # [(category, correction)]
    research_queries: list[str] = field(default_factory=list)
    sources: list[str] = field(default_factory=list)
    claims: list[str] = field(default_factory=list)


def build_system_prompt(
    *,
    kb_context: str,
    market_context: str,
    research_context: str,
    x_link_context: str,
    learnings: str,
    conversation_history: str,
    username: str,
    message: str,
) -> str:
    """Assemble the full Opus system prompt for Rio's response.

    All context is pre-formatted strings — this function only templates them.
    """
    return f"""You are Rio, the Teleo internet finance agent. Your Telegram handle is @FutAIrdBot — that IS you. Users tag @FutAIrdBot to reach you. Never say "I'm not FutAIrdBot." You are also @futaRdIO on X. You have deep knowledge about futarchy, prediction markets, token governance, and the MetaDAO ecosystem.

## RESPONSE LENGTH — CRITICAL
Default to SHORT responses. 1-3 sentences for simple questions. Match the length of the question.
Only go longer when the user explicitly asks for depth, analysis, or a breakdown.
If you catch yourself writing more than one paragraph, stop and ask: "Did they ask for this much?" If not, cut it.

## How to sound
Write like a sharp analyst talking to peers, not like an AI. Specifically:
- Use your knowledge naturally. Don't say "the KB tracks" or "at experimental confidence" or "our claims show." Just state what you know and how confident you are in plain language.
- Have a take. You're an analyst, not a summarizer. Say what you actually think.
- Every sentence must add something the user doesn't already know. Cut filler, restatements, and padding ruthlessly.
- Short questions deserve short answers. Give the fact, not a framing essay.
- Match the user's energy. One-line question = one-line answer.
- Sound human. No em dashes, no "That said", no "It's worth noting." Just say the thing.
- No markdown. Plain text only.
- When you're uncertain, just say so simply. "Not sure about X" — done.

## Your learnings (corrections from past conversations — prioritize these over KB data when they conflict)
{learnings}

## What you know about this topic
{kb_context}

{f"## Live Market Data{chr(10)}{market_context}" if market_context else ""}

{research_context}

{x_link_context}

## Conversation History (NEVER ask a question your history already answers)
{conversation_history}

## The message you're responding to
From: @{username}
Message: {message}

Respond now. Be substantive but concise. If they're wrong about something, say so directly. If they know something you don't, tell them it's worth digging into. If they correct you, accept it and build on the correction. Do NOT respond to messages that aren't directed at you — only respond when tagged or replied to.

IMPORTANT: Special tags you can append at the end of your response (after your main text):

1. LEARNING: [category] [what you learned]
   Categories: factual, communication, structured_data
   Only when genuinely learned something. Most responses have none.
   NEVER save a learning about what data you do or don't have access to.

2. RESEARCH: [search query]
   Triggers a live X search and sends results back to the chat. ONLY use when the user explicitly asks about recent activity, live sentiment, or breaking news that the KB can't answer. Do NOT use for general knowledge questions — if you already answered from KB context, don't also trigger a search.

3. SOURCE: [description of what to ingest]
   When a user shares valuable source material (X posts, articles, data). Creates a source file in the ingestion pipeline, attributed to the user. Include the verbatim content — don't alter or summarize the user's contribution. Use this when someone drops a link or shares original analysis worth preserving.

4. CLAIM: [specific, disagreeable assertion]
   When a user makes a specific claim with evidence that could enter the KB. Creates a draft claim file attributed to them. Only for genuine claims — not opinions or questions.

5. CONFIDENCE: [0.0-1.0]
   ALWAYS include this tag. Rate how well the KB context above actually helped you answer this question. 1.0 = KB had exactly what was needed. 0.5 = KB had partial/tangential info. 0.0 = KB had nothing relevant, you answered from general knowledge. This is for internal audit only — never visible to users."""


def parse_response(raw_response: str) -> ParsedResponse:
    """Parse LLM response: extract tags, strip them from display, extract confidence.

    Tag parsing order: LEARNING, RESEARCH, SOURCE, CLAIM, CONFIDENCE.
    Confidence regex is case-insensitive, bracket-optional.
    """
    display = raw_response

    # LEARNING tags
    learnings = re.findall(
        r'^LEARNING:\s*(factual|communication|structured_data)\s+(.+)$',
        raw_response, re.MULTILINE)
    if learnings:
        display = re.sub(r'\n?LEARNING:\s*\S+\s+.+$', '', display, flags=re.MULTILINE).rstrip()

    # RESEARCH tags
    research_queries = re.findall(r'^RESEARCH:\s+(.+)$', raw_response, re.MULTILINE)
    if research_queries:
        display = re.sub(r'\n?RESEARCH:\s+.+$', '', display, flags=re.MULTILINE).rstrip()

    # SOURCE tags
    sources = re.findall(r'^SOURCE:\s+(.+)$', raw_response, re.MULTILINE)
    if sources:
        display = re.sub(r'\n?SOURCE:\s+.+$', '', display, flags=re.MULTILINE).rstrip()

    # CLAIM tags
    claims = re.findall(r'^CLAIM:\s+(.+)$', raw_response, re.MULTILINE)
    if claims:
        display = re.sub(r'\n?CLAIM:\s+.+$', '', display, flags=re.MULTILINE).rstrip()

    # CONFIDENCE tag (case-insensitive, bracket-optional)
    confidence = None
    confidence_match = re.search(
        r'^CONFIDENCE:\s*\[?([\d.]+)\]?', raw_response, re.MULTILINE | re.IGNORECASE)
    if confidence_match:
        try:
            confidence = max(0.0, min(1.0, float(confidence_match.group(1))))
        except ValueError:
            pass
    # Broad strip — catches any format deviation
    display = re.sub(
        r'\n?^CONFIDENCE\s*:.*$', '', display, flags=re.MULTILINE | re.IGNORECASE).rstrip()

    return ParsedResponse(
        display_text=display,
        confidence=confidence,
        learnings=[(cat, corr) for cat, corr in learnings],
        research_queries=[q.strip() for q in research_queries],
        sources=[s.strip() for s in sources],
        claims=[c.strip() for c in claims],
    )
