"""Helpers for calling external agents.

These functions forward rendered prompts to language model APIs and
optionally split the results into manageable chunks. The current
implementations are placeholders and should be replaced with real API
calls.
"""

import os
import re
import traceback
from typing import Any, Dict, Optional

import colorama
from colorama import Fore, Style
from dotenv import load_dotenv

from ._config import _config

# Initialize colorama for auto-resetting colors
colorama.init(autoreset=True)

# ANSI escape sequence for underlining (colorama doesn't support underline directly)
UNDERLINE = "\033[4m"


load_dotenv()


def call_external_agent(
    prompt: str, agent_env: Dict[str, str], logger: Optional[Any] = None
) -> str:
    """
    Sends the rendered prompt to an external agent (e.g., a language model) and returns the generated content.

    Parameters:
      prompt (str): The prompt string to send to the external agent.
      agent_env (dict): A dictionary containing configuration for the external agent (e.g., API key, model name).
        Expected keys:
        - provider: The LLM provider to use (e.g., "openai", "anthropic", "deepinfra", "llamacpp")
        - api_key: API key for the provider
        - model_name: The specific model to use
        - max_tokens: Maximum number of tokens to generate
        - temperature: Temperature for generation (0.0-1.0)

    Returns:
      str: The content generated by the external agent.
    """
    try:
        from swarmauri.agents.QAAgent import QAAgent
        from swarmauri.messages.SystemMessage import SystemMessage

        from ._llm import GenericLLM
    except Exception as e:
        error_details = traceback.format_exc()  # Get full traceback details
        raise ImportError(
            f"\n\n{Fore.YELLOW}Exception: {Fore.RED}({e}){Fore.YELLOW}"
            f"\nTraceback details:\n{Fore.RED}{error_details}{Fore.YELLOW}\n"
            "\nThis often happens when an SDK package is installed in editable mode and a file has been "
            "corrupted with a bad overwrite. "
            "\nSee the issue thread here: "
            f"{Fore.BLUE}{UNDERLINE}https://github.com/swarmauri/swarmauri-sdk/issues/1300{Style.RESET_ALL}\n"
        )
    # Log the prompt (truncated if configured)
    truncated_prompt = prompt[:140] + "..." if _config["truncate"] else prompt
    if logger:
        logger.info(f"Sending prompt to external llm: \n\t{truncated_prompt}\n")

    # Extract configuration from agent_env
    provider = os.getenv("PROVIDER") or agent_env.get("provider", "deepinfra").lower()
    api_key = os.getenv(f"{provider.upper()}_API_KEY") or agent_env.get("api_key")
    model_name = agent_env.get("model_name")
    max_tokens = int(agent_env.get("max_tokens", 8192))

    # Initialize the generic LLM manager
    llm_manager = GenericLLM()

    # Special case for LlamaCpp which doesn't need an API key
    if provider.lower() == "llamacpp":
        try:
            llm = llm_manager.get_llm(
                provider=provider,
                api_key=api_key,
                model_name="localhost",
                allowed_models=["localhost"],
            )
        except Exception as e:
            logger.error(str(e))
    else:
        try:
            # Get an instance of the requested LLM
            llm = llm_manager.get_llm(
                provider=provider,
                api_key=api_key,
                model_name=model_name,
            )
        except Exception as e:
            logger.error(str(e))

    # Create QAAgent with the configured LLM
    system_context = "You are a software developer."
    agent = QAAgent(llm=llm)

    agent.conversation.system_context = SystemMessage(content=system_context)

    try:
        # Execute the prompt against the agent
        result = agent.exec(
            prompt,
            llm_kwargs={"max_tokens": max_tokens},
        )
    except KeyboardInterrupt:
        raise KeyboardInterrupt("'Interrupted...'")

    # Process and chunk the content
    content = chunk_content(result, logger)

    # Clean up
    del agent
    return content


def chunk_content(full_content: str, logger: Optional[Any] = None) -> str:
    """
    Optionally splits the content into chunks. Returns either a single chunk
    or the full content.
    """
    # Remove any unwanted <think> blocks first
    pattern = r"<think>[\s\S]*?</think>"
    cleaned_text = re.sub(pattern, "", full_content).strip()

    try:
        from swarmauri.chunkers.MdSnippetChunker import MdSnippetChunker

        chunker = MdSnippetChunker()
        chunks = chunker.chunk_text(cleaned_text)
        if len(chunks) > 1:
            logger.warning(
                "MdSnippetChunker found more than one snippet, took the first."
            )
            return chunks[0][2]
        try:
            return chunks[0][2]
        except IndexError:
            logger.warning("MdSnippetChunker found no chunks, using cleaned_text.")
            return cleaned_text
    except ImportError:
        if logger:
            logger.warning(
                "MdSnippetChunker not found. Returning full content without chunking."
            )
        return cleaned_text
    except Exception as e:
        if logger:
            logger.error(f"[ERROR] Failed to chunk content: {e}")
        return cleaned_text
