"""
API Routes for LLM Agent Service
"""

from fastapi import APIRouter, HTTPException, Depends
from pydantic import BaseModel, Field
from typing import List, Optional, Dict, Any, AsyncGenerator
from sse_starlette.sse import EventSourceResponse
import json
from loguru import logger

from ..core.llm_client import LLMClientFactory, LLMMessage, BaseLLMClient
from ..core.prompt_manager import PromptManager
from ..core.context_manager import ContextManager
from ..tools import (
    ToolRegistry,
    GetSignalTool,
    GetAnalysisTool,
    CheckPortfolioTool,
    GetPositionsTool,
    ExecuteTradeTool,
    SetAlertTool,
    ExplainConceptTool,
    GetCourseInfoTool
)
from ..tools.signals import GetNewsTool
from ..tools.trading import CalculatePositionSizeTool
from ..tools.portfolio import GetTradeHistoryTool
from ..config import settings

# Initialize router
router = APIRouter(prefix="/api/v1", tags=["llm-agent"])

# Initialize components
llm_client: Optional[BaseLLMClient] = None
prompt_manager = PromptManager()
context_manager = ContextManager()
tool_registry = ToolRegistry()


# Pydantic Models
class ChatMessage(BaseModel):
    role: str = Field(..., description="Message role: user, assistant, or system")
    content: str = Field(..., description="Message content")


class ChatRequest(BaseModel):
    user_id: str = Field(..., description="User identifier")
    conversation_id: str = Field(..., description="Conversation identifier")
    message: str = Field(..., description="User message")
    user_plan: str = Field(default="free", description="User subscription plan")
    stream: bool = Field(default=True, description="Enable streaming response")


class AnalysisRequest(BaseModel):
    user_id: str
    symbol: str = Field(..., description="Trading symbol to analyze")
    user_plan: str = Field(default="free")


class StrategyRequest(BaseModel):
    user_id: str
    symbol: str
    risk_tolerance: str = Field(default="moderate", description="conservative, moderate, or aggressive")
    user_plan: str = Field(default="free")


class ExplainRequest(BaseModel):
    concept: str = Field(..., description="Trading concept to explain")
    level: str = Field(default="beginner", description="Explanation level")


class HealthResponse(BaseModel):
    status: str
    llm_provider: str
    llm_available: bool
    active_contexts: int
    available_tools: int


# Dependency to get LLM client
async def get_llm_client() -> BaseLLMClient:
    global llm_client
    if llm_client is None:
        llm_client = LLMClientFactory.create_client(
            provider=settings.llm_provider,
            base_url=settings.ollama_base_url,
            model=settings.llm_model
        )
    return llm_client


# Initialize tool registry on startup
def init_tools():
    """Initialize and register all tools"""
    # Signal tools
    tool_registry.register(GetSignalTool(settings.ml_engine_url))
    tool_registry.register(GetAnalysisTool(settings.data_service_url))
    tool_registry.register(GetNewsTool(settings.data_service_url))

    # Portfolio tools
    tool_registry.register(CheckPortfolioTool(settings.backend_url))
    tool_registry.register(GetPositionsTool(settings.backend_url))
    tool_registry.register(GetTradeHistoryTool(settings.backend_url))

    # Trading tools
    tool_registry.register(ExecuteTradeTool(settings.backend_url))
    tool_registry.register(SetAlertTool(settings.backend_url))
    tool_registry.register(CalculatePositionSizeTool())

    # Education tools
    tool_registry.register(ExplainConceptTool())
    tool_registry.register(GetCourseInfoTool())

    logger.info(f"Registered {len(tool_registry.tools)} tools")


# Routes

@router.get("/health", response_model=HealthResponse)
async def health_check(client: BaseLLMClient = Depends(get_llm_client)):
    """Health check endpoint"""
    llm_available = await client.health_check()

    return HealthResponse(
        status="healthy" if llm_available else "degraded",
        llm_provider=settings.llm_provider,
        llm_available=llm_available,
        active_contexts=context_manager.get_active_contexts_count(),
        available_tools=len(tool_registry.tools)
    )


@router.post("/chat")
async def chat(
    request: ChatRequest,
    client: BaseLLMClient = Depends(get_llm_client)
):
    """
    Chat with the trading copilot
    Supports both streaming and non-streaming responses
    """
    try:
        # Get or create context
        system_prompt = prompt_manager.get_system_prompt(request.user_plan)
        context = context_manager.build_context(
            user_id=request.user_id,
            conversation_id=request.conversation_id,
            system_prompt=system_prompt,
            user_plan=request.user_plan
        )

        # Add user message
        context.add_message("user", request.message)

        # Get available tools for user's plan
        tools = tool_registry.get_tool_definitions(request.user_plan)

        # Convert context to LLM messages
        messages = [
            LLMMessage(role=msg["role"], content=msg["content"])
            for msg in context.get_messages()
        ]

        if request.stream:
            # Return streaming response
            async def generate_stream():
                full_response = ""
                async for chunk in await client.generate(
                    messages=messages,
                    tools=tools,
                    stream=True
                ):
                    full_response += chunk
                    yield {
                        "event": "message",
                        "data": json.dumps({"content": chunk, "done": False})
                    }

                # Save assistant response
                context.add_message("assistant", full_response)

                # Send completion event
                yield {
                    "event": "message",
                    "data": json.dumps({"content": "", "done": True})
                }

            return EventSourceResponse(generate_stream())

        else:
            # Non-streaming response
            response = await client.generate(
                messages=messages,
                tools=tools,
                stream=False
            )

            # Handle tool calls if present
            if response.tool_calls:
                # Execute tools
                tool_results = []
                for tool_call in response.tool_calls:
                    result = await tool_registry.execute_tool(
                        name=tool_call["function"]["name"],
                        parameters={**tool_call["function"]["arguments"], "user_id": request.user_id},
                        user_plan=request.user_plan
                    )
                    tool_results.append(result)

                # Add tool results to context and get final response
                # (This would require another LLM call with tool results)
                # For now, just return the tool results
                context.add_message("assistant", response.content)

                return {
                    "response": response.content,
                    "tool_calls": response.tool_calls,
                    "tool_results": tool_results,
                    "conversation_id": request.conversation_id
                }

            # Save assistant response
            context.add_message("assistant", response.content)

            return {
                "response": response.content,
                "conversation_id": request.conversation_id,
                "usage": response.usage
            }

    except Exception as e:
        logger.error(f"Chat error: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/analyze")
async def analyze_symbol(
    request: AnalysisRequest,
    client: BaseLLMClient = Depends(get_llm_client)
):
    """
    Get comprehensive analysis for a symbol
    """
    try:
        # Build analysis prompt
        analysis_prompt = prompt_manager.get_analysis_prompt(
            symbol=request.symbol,
            analysis_type="complete"
        )

        system_prompt = prompt_manager.get_system_prompt(request.user_plan)

        messages = [
            LLMMessage(role="system", content=system_prompt),
            LLMMessage(role="user", content=analysis_prompt)
        ]

        tools = tool_registry.get_tool_definitions(request.user_plan)

        # Generate analysis
        response = await client.generate(
            messages=messages,
            tools=tools,
            stream=False,
            temperature=0.3  # Lower temperature for more consistent analysis
        )

        return {
            "symbol": request.symbol,
            "analysis": response.content,
            "usage": response.usage
        }

    except Exception as e:
        logger.error(f"Analysis error: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/strategy")
async def generate_strategy(
    request: StrategyRequest,
    client: BaseLLMClient = Depends(get_llm_client)
):
    """
    Generate a trading strategy for a symbol
    """
    try:
        strategy_prompt = prompt_manager.get_strategy_prompt(
            symbol=request.symbol,
            risk_tolerance=request.risk_tolerance
        )

        system_prompt = prompt_manager.get_system_prompt(request.user_plan)

        messages = [
            LLMMessage(role="system", content=system_prompt),
            LLMMessage(role="user", content=strategy_prompt)
        ]

        tools = tool_registry.get_tool_definitions(request.user_plan)

        response = await client.generate(
            messages=messages,
            tools=tools,
            stream=False,
            temperature=0.4
        )

        return {
            "symbol": request.symbol,
            "risk_tolerance": request.risk_tolerance,
            "strategy": response.content,
            "usage": response.usage
        }

    except Exception as e:
        logger.error(f"Strategy generation error: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/explain")
async def explain_concept(request: ExplainRequest):
    """
    Explain a trading concept
    """
    try:
        tool = ExplainConceptTool()
        result = await tool.execute(
            concept=request.concept,
            level=request.level
        )

        if not result["success"]:
            raise HTTPException(status_code=400, detail=result["error"]["message"])

        return result["data"]

    except Exception as e:
        logger.error(f"Explain error: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.get("/tools")
async def list_tools(user_plan: str = "free"):
    """
    List available tools for a user plan
    """
    return {
        "plan": user_plan,
        "tools": tool_registry.list_tool_names(user_plan),
        "count": len(tool_registry.get_tools_for_plan(user_plan))
    }


@router.delete("/context/{user_id}/{conversation_id}")
async def clear_context(user_id: str, conversation_id: str):
    """
    Clear conversation context
    """
    context_manager.delete_context(user_id, conversation_id)
    return {"message": "Context cleared successfully"}


@router.get("/models")
async def list_models(client: BaseLLMClient = Depends(get_llm_client)):
    """
    List available LLM models (for Ollama)
    """
    if hasattr(client, 'list_models'):
        models = await client.list_models()
        return {"models": models, "current": settings.llm_model}
    else:
        return {"models": [settings.llm_model], "current": settings.llm_model}
