from src.domain.entities.conversation import Conversation, ConversationMessage, MessageContent, MessageType
from src.domain.ports.repositories.conversation_repository import ConversationRepositoryPort
from src.domain.ports.repositories.charts_repository import ChartsRepositoryPort
from src.application.ports.llm_service_port import LLMServicePort
from src.application.dtos.conversation import (
    CreateConversationRequestDTO,
    SendMessageRequestDTO,
    ConversationResponseDTO,
    ConversationDetailResponseDTO,
    MessageResponseDTO,
    MessageContentDTO
)
from src.application.dtos.LLM import LLMRequestDTO
import uuid
from datetime import datetime, timezone
from typing import List, Optional
import logging
#from src.application.ports.conversation_service_port import ConversationServicePort

logger = logging.getLogger(__name__)

class ConversationService:
    def __init__(
        self,
        conversation_repo: ConversationRepositoryPort,
        charts_repo: ChartsRepositoryPort,
        llm_service: LLMServicePort
    ):
        self._conversation_repo = conversation_repo
        self._charts_repo = charts_repo
        self._llm_service = llm_service

    async def create_conversation(
        self, 
        user_id: str, 
        request: CreateConversationRequestDTO
    ) -> ConversationResponseDTO:
        """Create a new conversation"""
        try:
            referenced_charts = []
            
            # If initial chart is provided, verify it exists and belongs to the user
            if request.initial_chart_image_id:
                chart_image = await self._charts_repo.get_by_id(request.initial_chart_image_id)
                if not chart_image:
                    raise ValueError("Chart image not found")
                
                if chart_image.user_id != user_id:
                    raise ValueError("Chart image does not belong to user")
                
                referenced_charts.append(request.initial_chart_image_id)

            # Create new conversation
            conversation = Conversation(
                id=str(uuid.uuid4()),
                user_id=user_id,
                title=request.title,
                created_at=datetime.now(timezone.utc),
                updated_at=datetime.now(timezone.utc),
                is_active=True,
                messages=[],
                referenced_charts=referenced_charts
            )

            # Save to repository
            await self._conversation_repo.create_conversation(conversation)

            return ConversationResponseDTO(
                id=conversation.id,
                title=conversation.title,
                created_at=conversation.created_at.isoformat(),
                updated_at=conversation.updated_at.isoformat(),
                message_count=0,
                chart_count=len(referenced_charts)
            )

        except Exception as e:
            logger.error(f"Error creating conversation: {str(e)}")
            raise

    async def get_user_conversations(
        self, 
        user_id: str, 
        limit: int = 20
    ) -> List[ConversationResponseDTO]:
        """Get all conversations for a user"""
        try:
            conversations = await self._conversation_repo.get_conversations_by_user_id(
                user_id, limit
            )

            return [
                ConversationResponseDTO(
                    id=conv.id,
                    title=conv.title,
                    created_at=conv.created_at.isoformat(),
                    updated_at=conv.updated_at.isoformat(),
                    message_count=len(conv.messages),
                    chart_count=len(conv.referenced_charts)
                )
                for conv in conversations
            ]

        except Exception as e:
            logger.error(f"Error getting user conversations: {str(e)}")
            raise

    async def get_conversation_details(
        self, 
        conversation_id: str, 
        user_id: str
    ) -> ConversationDetailResponseDTO:
        """Get conversation details with all messages"""
        try:
            conversation = await self._conversation_repo.get_conversation_by_id(conversation_id)
            
            if not conversation:
                raise ValueError("Conversation not found")
            
            if conversation.user_id != user_id:
                raise ValueError("Conversation does not belong to user")

            # Convert messages to DTOs
            messages = []
            for msg in conversation.messages:
                content_dto = MessageContentDTO(
                    text=msg.content.text,
                    chart_image_id=msg.content.chart_image_id
                )
                
                message_dto = MessageResponseDTO(
                    id=msg.id,
                    message_type=msg.message_type,
                    content=content_dto,
                    timestamp=msg.timestamp.isoformat(),
                    sender_id=msg.sender_id
                )
                messages.append(message_dto)

            return ConversationDetailResponseDTO(
                id=conversation.id,
                title=conversation.title,
                created_at=conversation.created_at.isoformat(),
                updated_at=conversation.updated_at.isoformat(),
                messages=messages,
                referenced_charts=conversation.referenced_charts
            )

        except Exception as e:
            logger.error(f"Error getting conversation details: {str(e)}")
            raise

    async def send_message(
        self, 
        conversation_id: str, 
        user_id: str, 
        request: SendMessageRequestDTO
    ) -> MessageResponseDTO:
        """Send a message in a conversation and get AI response"""
        try:
            # Get conversation
            conversation = await self._conversation_repo.get_conversation_by_id(conversation_id)
            
            if not conversation:
                raise ValueError("Conversation not found")
            
            if conversation.user_id != user_id:
                raise ValueError("Conversation does not belong to user")

            # If chart is included in message, verify it exists and belongs to user
            if request.chart_image_id:
                chart_image = await self._charts_repo.get_by_id(request.chart_image_id)
                if not chart_image:
                    raise ValueError("Chart image not found")
                
                if chart_image.user_id != user_id:
                    raise ValueError("Chart image does not belong to user")
                
                # Add to referenced charts if not already there
                if request.chart_image_id not in conversation.referenced_charts:
                    conversation.referenced_charts.append(request.chart_image_id)

            # Create user message content
            user_content = MessageContent(
                text=request.message,
                chart_image_id=request.chart_image_id
            )

            # Create user message
            user_message = ConversationMessage(
                id=str(uuid.uuid4()),
                conversation_id=conversation_id,
                sender_id=user_id,
                message_type=MessageType.TEXT,
                content=user_content,
                timestamp=datetime.now(timezone.utc)
            )

            # Add user message to conversation
            conversation.messages.append(user_message)
            conversation.updated_at = datetime.now(timezone.utc)

            # Get AI response if there's text in the message
            assistant_message = None
            if request.message.strip():
                # Build context from conversation history
                context = self._build_conversation_context(conversation.messages[:-1])  # Exclude current message
                
                # Create enhanced question with context
                enhanced_question = self._create_enhanced_question(request.message, context)

                # Get the most recent chart for analysis (or the one in current message)
                chart_to_analyze = request.chart_image_id
                if not chart_to_analyze and conversation.referenced_charts:
                    chart_to_analyze = conversation.referenced_charts[-1]  # Most recent chart

                if chart_to_analyze:
                    chart_image = await self._charts_repo.get_by_id(chart_to_analyze)
                    if chart_image:
                        # Get image bytes (handles both file path and direct data)
                        image_bytes = chart_image.get_image_bytes()
                    
                    # Get AI response using LLM service
                    llm_request = LLMRequestDTO(
                        image_bytes=image_bytes,
                        question=enhanced_question
                    )

                    llm_response = await self._llm_service.analyze(llm_request)

                    # Create assistant message
                    assistant_content = MessageContent.create_text(llm_response.answer)
                    assistant_message = ConversationMessage(
                        id=str(uuid.uuid4()),
                        conversation_id=conversation_id,
                        sender_id="system",  # AI assistant
                        message_type=MessageType.TEXT,
                        content=assistant_content,
                        timestamp=datetime.now(timezone.utc)
                    )

                    # Add assistant message to conversation
                    conversation.messages.append(assistant_message)
                    conversation.updated_at = datetime.now(timezone.utc)

            # Save updated conversation
            await self._conversation_repo.update_conversation(conversation)

            # Return the assistant message if it exists, otherwise return user message
            message_to_return = assistant_message if assistant_message else user_message
            
            content_dto = MessageContentDTO(
                text=message_to_return.content.text,
                chart_image_id=message_to_return.content.chart_image_id
            )
            
            return MessageResponseDTO(
                id=message_to_return.id,
                message_type=message_to_return.message_type,
                content=content_dto,
                timestamp=message_to_return.timestamp.isoformat(),
                sender_id=message_to_return.sender_id
            )

        except Exception as e:
            logger.error(f"Error sending message: {str(e)}")
            raise

    def _build_conversation_context(self, messages: List[ConversationMessage]) -> str:
        """Build context string from conversation history"""
        if not messages:
            return ""
        
        # Take last 5 messages for context to avoid token limits
        recent_messages = messages[-5:]
        
        context_parts = []
        for i, msg in enumerate(recent_messages, 1):
            role = "User" if msg.sender_id != "system" else "Assistant"
            content_text = msg.content.text or ""
            if msg.content.chart_image_id:
                content_text += f" [Chart: {msg.content.chart_image_id}]"
            context_parts.append(f"Turn {i} - {role}: {content_text}")
        
        return " | ".join(context_parts)

    def _create_enhanced_question(self, current_question: str, context: str) -> str:
        """Create enhanced question with conversation context"""
        if not context:
            return current_question
        
        return f"""Previous conversation context: {context}

Current question: {current_question}

Please provide a response that takes into account the conversation history and directly addresses the current question about the chart."""

    async def delete_conversation(self, conversation_id: str, user_id: str) -> bool:
        """Delete a conversation (soft delete)"""
        try:
            conversation = await self._conversation_repo.get_conversation_by_id(conversation_id)
            
            if not conversation:
                raise ValueError("Conversation not found")
            
            if conversation.user_id != user_id:
                raise ValueError("Conversation does not belong to user")

            # Soft delete
            conversation.is_active = False
            conversation.updated_at = datetime.now(timezone.utc)
            
            await self._conversation_repo.update_conversation(conversation)
            return True

        except Exception as e:
            logger.error(f"Error deleting conversation: {str(e)}")
            raise

    async def get_conversation_statistics(self, user_id: str) -> dict:
        """Get conversation statistics for a user"""
        try:
            conversations = await self._conversation_repo.get_conversations_by_user_id(user_id, limit=1000)
            
            total_conversations = len(conversations)
            total_messages = sum(len(conv.messages) for conv in conversations)
            active_conversations = len([conv for conv in conversations if conv.is_active])
            total_charts = sum(len(conv.referenced_charts) for conv in conversations)
            
            return {
                "total_conversations": total_conversations,
                "active_conversations": active_conversations,
                "total_messages": total_messages,
                "total_charts": total_charts,
                "average_messages_per_conversation": total_messages / total_conversations if total_conversations > 0 else 0,
                "average_charts_per_conversation": total_charts / total_conversations if total_conversations > 0 else 0
            }

        except Exception as e:
            logger.error(f"Error getting conversation statistics: {str(e)}")
            raise
