"""
Prompt Builder for DAGI Router

Цей модуль відповідає за побудову system prompts для агентів,
використовуючи дані з БД через city-service API.

Частина Agent System Prompts MVP v2
"""

import httpx
import logging
from typing import Dict, Any, Optional
from dataclasses import dataclass

logger = logging.getLogger(__name__)


@dataclass
class AgentSystemPrompt:
    """Результат побудови system prompt"""
    agent_id: str
    agent_name: Optional[str]
    has_prompts: bool
    system_prompt: str
    source: str  # "database", "fallback", "config"


class PromptBuilder:
    """
    Будує system prompts для агентів.
    
    Порядок пріоритетів:
    1. Промти з БД (через city-service API)
    2. Промти з router-config.yml
    3. Fallback default prompt
    """
    
    def __init__(
        self, 
        city_service_url: str = "http://daarion-city-service:7001",
        router_config: Optional[Dict[str, Any]] = None
    ):
        self.city_service_url = city_service_url.rstrip("/")
        self.router_config = router_config or {}
        self._http_client: Optional[httpx.AsyncClient] = None
    
    async def _get_http_client(self) -> httpx.AsyncClient:
        """Lazy initialization of HTTP client"""
        if self._http_client is None:
            self._http_client = httpx.AsyncClient(timeout=10.0)
        return self._http_client
    
    async def close(self):
        """Close HTTP client"""
        if self._http_client:
            await self._http_client.aclose()
            self._http_client = None
    
    async def get_system_prompt(self, agent_id: str) -> AgentSystemPrompt:
        """
        Отримати system prompt для агента.
        
        Спочатку пробує отримати з БД, потім з конфігу, потім fallback.
        """
        # Try database first
        db_prompt = await self._fetch_from_database(agent_id)
        if db_prompt and db_prompt.has_prompts:
            logger.info(f"Using database prompt for agent {agent_id}")
            return db_prompt
        
        # Try config
        config_prompt = self._get_from_config(agent_id)
        if config_prompt:
            logger.info(f"Using config prompt for agent {agent_id}")
            return config_prompt
        
        # Fallback
        logger.warning(f"No prompts found for agent {agent_id}, using fallback")
        return self._get_fallback_prompt(agent_id)
    
    async def _fetch_from_database(self, agent_id: str) -> Optional[AgentSystemPrompt]:
        """Fetch system prompt from city-service API"""
        try:
            client = await self._get_http_client()
            url = f"{self.city_service_url}/internal/agents/{agent_id}/system-prompt"
            
            response = await client.get(url)
            
            if response.status_code == 200:
                data = response.json()
                return AgentSystemPrompt(
                    agent_id=data.get("agent_id", agent_id),
                    agent_name=data.get("agent_name"),
                    has_prompts=data.get("has_prompts", False),
                    system_prompt=data.get("system_prompt", ""),
                    source="database"
                )
            else:
                logger.warning(f"City service returned {response.status_code} for agent {agent_id}")
                return None
                
        except httpx.RequestError as e:
            logger.error(f"Error fetching prompt from city-service: {e}")
            return None
        except Exception as e:
            logger.error(f"Unexpected error fetching prompt: {e}")
            return None
    
    def _get_from_config(self, agent_id: str) -> Optional[AgentSystemPrompt]:
        """Get system prompt from router config"""
        agents = self.router_config.get("agents", {})
        agent_config = agents.get(agent_id)
        
        if not agent_config:
            return None
        
        system_prompt = agent_config.get("system_prompt")
        if not system_prompt:
            return None
        
        return AgentSystemPrompt(
            agent_id=agent_id,
            agent_name=agent_config.get("description"),
            has_prompts=True,
            system_prompt=system_prompt.strip(),
            source="config"
        )
    
    def _get_fallback_prompt(self, agent_id: str) -> AgentSystemPrompt:
        """Generate fallback prompt for unknown agent"""
        fallback_prompt = (
            f"You are an AI agent (ID: {agent_id}) in the DAARION.city ecosystem.\n\n"
            "Guidelines:\n"
            "- Be helpful, accurate, and professional\n"
            "- Follow ethical guidelines and safety protocols\n"
            "- Respect user privacy and data protection\n"
            "- Ask for clarification when uncertain\n"
            "- Never execute harmful or unauthorized actions\n"
        )
        
        return AgentSystemPrompt(
            agent_id=agent_id,
            agent_name=None,
            has_prompts=False,
            system_prompt=fallback_prompt,
            source="fallback"
        )
    
    async def check_prompts_available(self, agent_ids: list[str]) -> Dict[str, bool]:
        """
        Check if prompts are available for multiple agents.
        Returns dict mapping agent_id to has_prompts boolean.
        """
        result = {}
        
        try:
            client = await self._get_http_client()
            url = f"{self.city_service_url}/internal/agents/prompts/status"
            
            response = await client.post(url, json={"agent_ids": agent_ids})
            
            if response.status_code == 200:
                data = response.json()
                result = data.get("status", {})
        except Exception as e:
            logger.error(f"Error checking prompts status: {e}")
        
        # Fill missing with config check
        for agent_id in agent_ids:
            if agent_id not in result:
                config_prompt = self._get_from_config(agent_id)
                result[agent_id] = config_prompt is not None
        
        return result


def build_system_prompt_from_parts(
    prompts: Dict[str, Optional[str]],
    agent_info: Optional[Dict[str, Any]] = None,
    context: Optional[Dict[str, Any]] = None
) -> str:
    """
    Build system prompt from individual parts.
    
    This is a standalone function that can be used without PromptBuilder class.
    
    Args:
        prompts: Dict with keys "core", "safety", "governance", "tools"
        agent_info: Optional dict with agent metadata (name, kind, etc.)
        context: Optional dict with runtime context (node, microdao, etc.)
    
    Returns:
        Assembled system prompt string
    """
    parts = []
    
    # Core prompt (required)
    if prompts.get("core"):
        parts.append(prompts["core"])
    elif agent_info:
        agent_name = agent_info.get("display_name") or agent_info.get("name") or "Agent"
        agent_kind = agent_info.get("kind") or "assistant"
        parts.append(
            f"You are {agent_name}, an AI {agent_kind} in DAARION.city ecosystem. "
            f"Be helpful, accurate, and follow ethical guidelines."
        )
    else:
        parts.append("You are an AI assistant. Be helpful and accurate.")
    
    # Governance rules
    if prompts.get("governance"):
        parts.append("\n\n## Governance\n" + prompts["governance"])
    
    # Safety guidelines
    if prompts.get("safety"):
        parts.append("\n\n## Safety Guidelines\n" + prompts["safety"])
    
    # Tools instructions
    if prompts.get("tools"):
        parts.append("\n\n## Tools & Capabilities\n" + prompts["tools"])
    
    # Context additions
    if context:
        context_lines = []
        
        if context.get("node"):
            node = context["node"]
            context_lines.append(f"- **Node**: {node.get('name', 'Unknown')}")
        
        if context.get("district"):
            district = context["district"]
            context_lines.append(f"- **District**: {district.get('name', 'Unknown')}")
        
        if context.get("microdao"):
            microdao = context["microdao"]
            context_lines.append(f"- **MicroDAO**: {microdao.get('name', 'Unknown')}")
        
        if context.get("user_role"):
            context_lines.append(f"- **User Role**: {context['user_role']}")
        
        if context_lines:
            parts.append("\n\n## Current Context\n" + "\n".join(context_lines))
    
    return "\n".join(parts)


# Singleton instance for convenience
_prompt_builder: Optional[PromptBuilder] = None


async def get_prompt_builder(
    city_service_url: str = "http://daarion-city-service:7001",
    router_config: Optional[Dict[str, Any]] = None
) -> PromptBuilder:
    """Get or create singleton PromptBuilder instance"""
    global _prompt_builder
    
    if _prompt_builder is None:
        _prompt_builder = PromptBuilder(city_service_url, router_config)
    
    return _prompt_builder


async def get_agent_system_prompt(
    agent_id: str,
    city_service_url: str = "http://daarion-city-service:7001",
    router_config: Optional[Dict[str, Any]] = None
) -> str:
    """
    Convenience function to get system prompt for an agent.
    
    Usage in DAGI Router:
        system_prompt = await get_agent_system_prompt("daarwizz")
    """
    builder = await get_prompt_builder(city_service_url, router_config)
    result = await builder.get_system_prompt(agent_id)
    return result.system_prompt

