"""
external.py

This module contains functions for external integrations.
It provides:

  1. call_external_agent:
     - Sends a rendered prompt to an external agent (e.g., an LLM or language model API)
       and returns the generated content.

  2. chunk_content:
     - Optionally splits or processes the generated content into manageable chunks
       before saving.

Note:
  - The implementations here are placeholders. In a real-world scenario, you would
    replace them with calls to an actual external API (e.g., OpenAI, Hugging Face, etc.).
"""

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

from dotenv import load_dotenv

from ._config import _config

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.
    """
    from swarmauri.agents.QAAgent import QAAgent
    from swarmauri.messages.SystemMessage import SystemMessage

    from .GenericLLM import GenericLLM

    # Log the prompt (truncated if configured)
    truncated_prompt = prompt[:140] + "..." if _config["truncate"] else prompt
    if logger:
        logger.info(f"Sending prompt to external agent: \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", 3000))

    # Initialize the generic LLM manager
    llm_manager = GenericLLM()

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

    # 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)

    # Execute the prompt against the agent
    result = agent.exec(
        prompt,
        llm_kwargs={"max_tokens": max_tokens},
    )

    # 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.
    """
    try:
        # Remove any unwanted <think> blocks
        pattern = r"<think>[\s\S]*?</think>"
        cleaned_text = re.sub(pattern, "", full_content).strip()

        from swarmauri.chunkers.MdSnippetChunker import MdSnippetChunker

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