import os
import asyncio
from pathlib import Path
from typing import Dict, Any, Optional

from dotenv import load_dotenv
from fastapi import FastAPI, HTTPException
from fastapi.responses import JSONResponse

# Imports locales usando la estructura de paquetes
from app.tools.definitions import init_knowledge_base
from app.services.mcp import get_working_mcp_tools, close_mcp_connection
from app.agents.builder import create_agent
from app.core.models import QueryRequest, AgentQueryResponse, ErrorResponse, HealthResponse
from app.core.constants import ConsultaType, ToolNames, ErrorMessages

# Configuración Inicial
BASE_DIR = Path(__file__).resolve().parent.parent
# NO usar override=True para que Docker pueda inyectar mcp-server correctamente
load_dotenv(BASE_DIR / ".env", override=False)

openai_key = os.getenv("OPENAI_API_KEY")
if not openai_key:
    load_dotenv() 
    openai_key = os.getenv("OPENAI_API_KEY")

if not openai_key:
    raise ValueError("OPENAI_API_KEY no está definido en .env")

# Priorizar MCP_HTTP_URL de Docker, por defecto localhost para desarrollo local
MCP_URL = os.getenv("MCP_HTTP_URL", "http://localhost:8053/mcp")

# Variables globales de la APP
agent_executor = None
agent_memory = None
initialization_error = None

# FastAPI App
app = FastAPI(
    title="Asistente de Vuelos (Modular)",
    description="API Modularizada del Asistente del Aeropuerto de Cuenca.",
    root_path="/aeropuerto"
)

# ———————————————————————————————————————————
# Eventos de Ciclo de Vida
# ———————————————————————————————————————————

@app.on_event("startup")
async def startup_event():
    global agent_executor, agent_memory, initialization_error

    print(f"🚀 Iniciando servidor FastAPI | Root: {BASE_DIR}")
    
    # Lanzar la inicialización en segundo plano para no bloquear el inicio del servidor
    # y evitar el 502 Bad Gateway mientras se conecta al MCP
    asyncio.create_task(initialize_agent_background())

async def initialize_agent_background():
    global agent_executor, agent_memory, initialization_error
    
    print("🧠 Iniciando carga del agente en segundo plano...")
    try:
        # 1. Inicializar Base de Conocimiento (RAG)
        init_knowledge_base(BASE_DIR, openai_key)
        
        # 2. Obtener Herramientas MCP con paciencia
        print(f"🔌 Intentando conectar a MCP en {MCP_URL}...")
        mcp_tools = []
        try:
            mcp_tools = await get_working_mcp_tools(MCP_URL, retries=15, delay=5)
            if mcp_tools:
                print(f"📦 {len(mcp_tools)} herramientas MCP recibidas.")
            else:
                print("⚠️ No se recibieron herramientas MCP, el agente tendrá capacidades limitadas.")
        except Exception as e:
            print(f"⚠️ Error recuperando herramientas MCP: {e}")
        
        # 3. Crear Agente
        agent_executor, agent_memory = create_agent(openai_key, mcp_tools)
        
        print("🤖 Agente listo para recibir consultas.")
        initialization_error = None
        
    except Exception as e:
        error_msg = f"Fallo en inicialización de agente: {str(e)}"
        print(f"❌ {error_msg}")
        initialization_error = error_msg

@app.on_event("shutdown")
async def shutdown_event():
    await close_mcp_connection()

# ———————————————————————————————————————————
# Endpoints
# ———————————————————————————————————————————

@app.get("/")
def read_root():
    return {
        "message": "Asistente de Vuelos Activo",
        "structure": "Modular (app/)",
        "status": "ready" if agent_executor else "initializing",
        "mcp_url": MCP_URL
    }

@app.get("/health")
def health_check():
    if initialization_error:
        raise HTTPException(status_code=503, detail={"status": "unhealthy", "error": initialization_error})
    if not agent_executor:
        raise HTTPException(status_code=503, detail={"status": "initializing"})
    return HealthResponse(status="healthy", agent_ready=True, mcp_url=MCP_URL)

@app.post("/query")
async def handle_query(request: QueryRequest) -> Any:
    global agent_executor, agent_memory

    if not agent_executor:
        return JSONResponse(
            status_code=503, 
            content=ErrorResponse(error="AgentNotReady", details=ErrorMessages.AGENT_NOT_INITIALIZED).dict()
        )
    
    user_input = request.user_input.strip()
    if not user_input:
         return JSONResponse(
             status_code=400, 
             content=ErrorResponse(error="BadRequest", details=ErrorMessages.EMPTY_QUERY).dict()
         )

    print(f"\n👤 CONSULTA: {user_input}")

    try:
        result = await asyncio.wait_for(
            agent_executor.ainvoke({"input": user_input}),
            timeout=45.0
        )
        
        print("✅ Respuesta Generada")
        
        used_tools = [
            action.tool for action, _ in result.get('intermediate_steps', [])
        ]
        
        return AgentQueryResponse(
            input=user_input,
            response=result['output'],
            used_tools=used_tools,
            history_length=len(agent_memory.buffer) if agent_memory else 0
        )

    except asyncio.TimeoutError:
        return JSONResponse(
            status_code=504, 
            content=ErrorResponse(error="Timeout", details=ErrorMessages.TIMEOUT).dict()
        )
    except Exception as e:
        print(f"❌ Error en query: {e}")
        return JSONResponse(
            status_code=500,
            content=ErrorResponse(error="ServerError", details=str(e)).dict()
        )

@app.post("/reset")
async def reset_memory():
    if agent_memory:
        agent_memory.clear()
        return {"message": "Memoria reiniciada"}
    return JSONResponse(
        status_code=503, 
        content=ErrorResponse(error="AgentNotReady", details=ErrorMessages.AGENT_NOT_INITIALIZED).dict()
    )
