import re
import json
import uuid
from typing import Tuple, List
from xulcan.protocol.tools import ToolCall

class OutputExtractor:
    """Pipeline de limpieza y extracción para modelos locales rebeldes."""

    @staticmethod
    def strip_think_tags(raw_content: str) -> Tuple[str, str | None]:
        """Escudo Anti-Think para modelos de razonamiento (ej. DeepSeek R1)."""
        if not raw_content or "<think>" not in raw_content:
            return raw_content, None
            
        think_match = re.search(r'<think>(.*?)</think>', raw_content, flags=re.DOTALL)
        reasoning = think_match.group(1).strip() if think_match else None
        clean_content = re.sub(r'<think>.*?</think>\n?', '', raw_content, flags=re.DOTALL).strip()
        
        return clean_content, reasoning

    @staticmethod
    def extract_rebel_json(content: str) -> Tuple[List[ToolCall], str]:
        """Busca JSONs perdidos en el texto de modelos que no usan la API oficial."""
        tool_calls =[]
        
        # 1. Buscar en bloques markdown
        json_match = re.search(r'```(?:json)?\s*(.*?)\s*```', content, flags=re.DOTALL)
        target_str = json_match.group(1) if json_match else content

        # 2. Búsqueda matemática de llaves {}
        start_idx = target_str.find('{')
        end_idx = target_str.rfind('}')

        if start_idx != -1 and end_idx != -1 and end_idx > start_idx:
            extracted_json = target_str[start_idx:end_idx+1]
            try:
                tool_data = json.loads(extracted_json)
                call_info = None
                
                if "tool_call" in tool_data:
                    call_info = tool_data["tool_call"]
                elif "name" in tool_data and "arguments" in tool_data:
                    call_info = tool_data

                if call_info:
                    args = call_info.get("arguments", {})
                    # Fix para modelos que escapan el JSON como string
                    if isinstance(args, str):
                        args = json.loads(args)

                    tool_calls.append(ToolCall(
                        id=f"call_{uuid.uuid4().hex[:8]}",
                        name=call_info.get("name", ""),
                        arguments=args
                    ))
                    # Limpiamos el texto visible
                    content = content.replace(extracted_json, "").strip()
                    content = re.sub(r'```(?:json)?|```', '', content).strip()
            except json.JSONDecodeError:
                pass

        return tool_calls, content