package conversation

import (
	"context"
	"database/sql"
	"fmt"
	"time"

	"gorm.io/gorm"

	"customer-support-system/internal/ai"
	"customer-support-system/internal/database"
	"customer-support-system/internal/models"
	"customer-support-system/pkg/logger"
)

// ConversationService handles conversation operations
type ConversationService struct {
	db        *gorm.DB
	aiService *ai.AIService
}

// NewConversationService creates a new conversation service
func NewConversationService() *ConversationService {
	return &ConversationService{
		db:        database.GetDB(),
		aiService: ai.NewAIService(),
	}
}

// CreateConversation creates a new conversation
func (s *ConversationService) CreateConversation(userID uint, req *models.CreateConversationRequest) (*models.Conversation, error) {
	conversation := models.Conversation{
		Title:      req.Title,
		UserID:     userID,
		Department: req.Department,
		Priority:   req.Priority,
		Tags:       req.Tags,
		Status:     "active",
	}

	if err := s.db.Create(&conversation).Error; err != nil {
		logger.WithError(err).WithField("user_id", userID).Error("Failed to create conversation")
		return nil, fmt.Errorf("failed to create conversation: %w", err)
	}

	logger.WithField("conversation_id", conversation.ID).Info("Conversation created successfully")

	return &conversation, nil
}

// GetConversation retrieves a conversation by ID
func (s *ConversationService) GetConversation(conversationID uint, userID uint) (*models.Conversation, error) {
	var conversation models.Conversation
	if err := s.db.Where("id = ? AND user_id = ?", conversationID, userID).First(&conversation).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("conversation not found")
		}
		logger.WithError(err).WithField("conversation_id", conversationID).Error("Failed to get conversation")
		return nil, fmt.Errorf("failed to get conversation: %w", err)
	}

	return &conversation, nil
}

// ListConversations retrieves a list of conversations for a user
func (s *ConversationService) ListConversations(userID uint, page, pageSize int, status string) (*models.ConversationListResponse, error) {
	var conversations []models.Conversation
	var total int64

	query := s.db.Model(&models.Conversation{}).Where("user_id = ?", userID)

	if status != "" {
		query = query.Where("status = ?", status)
	}

	// Get total count
	if err := query.Count(&total).Error; err != nil {
		logger.WithError(err).WithField("user_id", userID).Error("Failed to count conversations")
		return nil, fmt.Errorf("failed to count conversations: %w", err)
	}

	// Get paginated results
	offset := (page - 1) * pageSize
	if err := query.Order("last_message_at DESC").Offset(offset).Limit(pageSize).Find(&conversations).Error; err != nil {
		logger.WithError(err).WithField("user_id", userID).Error("Failed to list conversations")
		return nil, fmt.Errorf("failed to list conversations: %w", err)
	}

	return &models.ConversationListResponse{
		Conversations: conversations,
		Total:         total,
		Page:          page,
		PageSize:      pageSize,
	}, nil
}

// UpdateConversation updates a conversation
func (s *ConversationService) UpdateConversation(conversationID uint, userID uint, req *models.UpdateConversationRequest) (*models.Conversation, error) {
	var conversation models.Conversation
	if err := s.db.Where("id = ? AND user_id = ?", conversationID, userID).First(&conversation).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("conversation not found")
		}
		logger.WithError(err).WithField("conversation_id", conversationID).Error("Failed to get conversation")
		return nil, fmt.Errorf("failed to get conversation: %w", err)
	}

	// Update fields
	if req.Title != "" {
		conversation.Title = req.Title
	}
	if req.Status != "" {
		conversation.Status = req.Status
	}
	if req.Department != "" {
		conversation.Department = req.Department
	}
	if req.Priority != "" {
		conversation.Priority = req.Priority
	}
	if req.Tags != "" {
		conversation.Tags = req.Tags
	}
	if req.AgentID != nil {
		conversation.AgentID = req.AgentID
	}

	if err := s.db.Save(&conversation).Error; err != nil {
		logger.WithError(err).WithField("conversation_id", conversationID).Error("Failed to update conversation")
		return nil, fmt.Errorf("failed to update conversation: %w", err)
	}

	logger.WithField("conversation_id", conversationID).Info("Conversation updated successfully")

	return &conversation, nil
}

// DeleteConversation deletes a conversation
func (s *ConversationService) DeleteConversation(conversationID uint, userID uint) error {
	var conversation models.Conversation
	if err := s.db.Where("id = ? AND user_id = ?", conversationID, userID).First(&conversation).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return fmt.Errorf("conversation not found")
		}
		logger.WithError(err).WithField("conversation_id", conversationID).Error("Failed to get conversation")
		return fmt.Errorf("failed to get conversation: %w", err)
	}

	if err := s.db.Delete(&conversation).Error; err != nil {
		logger.WithError(err).WithField("conversation_id", conversationID).Error("Failed to delete conversation")
		return fmt.Errorf("failed to delete conversation: %w", err)
	}

	logger.WithField("conversation_id", conversationID).Info("Conversation deleted successfully")

	return nil
}

// CreateMessage creates a new message in a conversation
func (s *ConversationService) CreateMessage(conversationID uint, userID uint, req *models.CreateMessageRequest) (*models.Message, error) {
	// Check if conversation exists and belongs to user
	var conversation models.Conversation
	if err := s.db.Where("id = ? AND user_id = ?", conversationID, userID).First(&conversation).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("conversation not found")
		}
		logger.WithError(err).WithField("conversation_id", conversationID).Error("Failed to get conversation")
		return nil, fmt.Errorf("failed to get conversation: %w", err)
	}

	// Create message
	message := models.Message{
		ConversationID: conversationID,
		UserID:         userID,
		Content:        req.Content,
		Type:           req.Type,
		Status:         "sent",
		IsAI:           false,
	}

	if err := s.db.Create(&message).Error; err != nil {
		logger.WithError(err).WithField("conversation_id", conversationID).Error("Failed to create message")
		return nil, fmt.Errorf("failed to create message: %w", err)
	}

	logger.WithField("message_id", message.ID).Info("Message created successfully")

	return &message, nil
}

// GetMessages retrieves messages in a conversation
func (s *ConversationService) GetMessages(conversationID uint, userID uint, page, pageSize int) (*models.MessageListResponse, error) {
	// Check if conversation exists and belongs to user
	var conversation models.Conversation
	if err := s.db.Where("id = ? AND user_id = ?", conversationID, userID).First(&conversation).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("conversation not found")
		}
		logger.WithError(err).WithField("conversation_id", conversationID).Error("Failed to get conversation")
		return nil, fmt.Errorf("failed to get conversation: %w", err)
	}

	var messages []models.Message
	var total int64

	// Get total count
	if err := s.db.Model(&models.Message{}).Where("conversation_id = ?", conversationID).Count(&total).Error; err != nil {
		logger.WithError(err).WithField("conversation_id", conversationID).Error("Failed to count messages")
		return nil, fmt.Errorf("failed to count messages: %w", err)
	}

	// Get paginated results
	offset := (page - 1) * pageSize
	if err := s.db.Where("conversation_id = ?", conversationID).Order("created_at ASC").Offset(offset).Limit(pageSize).Find(&messages).Error; err != nil {
		logger.WithError(err).WithField("conversation_id", conversationID).Error("Failed to get messages")
		return nil, fmt.Errorf("failed to get messages: %w", err)
	}

	return &models.MessageListResponse{
		Messages: messages,
		Total:    total,
		Page:     page,
		PageSize: pageSize,
	}, nil
}

// UpdateMessage updates a message
func (s *ConversationService) UpdateMessage(messageID uint, userID uint, req *models.UpdateMessageRequest) (*models.Message, error) {
	var message models.Message
	if err := s.db.Where("id = ? AND user_id = ?", messageID, userID).First(&message).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("message not found")
		}
		logger.WithError(err).WithField("message_id", messageID).Error("Failed to get message")
		return nil, fmt.Errorf("failed to get message: %w", err)
	}

	// Update fields
	if req.Content != "" {
		message.Content = req.Content
	}
	if req.Status != "" {
		message.Status = req.Status
	}

	if err := s.db.Save(&message).Error; err != nil {
		logger.WithError(err).WithField("message_id", messageID).Error("Failed to update message")
		return nil, fmt.Errorf("failed to update message: %w", err)
	}

	logger.WithField("message_id", messageID).Info("Message updated successfully")

	return &message, nil
}

// DeleteMessage deletes a message
func (s *ConversationService) DeleteMessage(messageID uint, userID uint) error {
	var message models.Message
	if err := s.db.Where("id = ? AND user_id = ?", messageID, userID).First(&message).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return fmt.Errorf("message not found")
		}
		logger.WithError(err).WithField("message_id", messageID).Error("Failed to get message")
		return fmt.Errorf("failed to get message: %w", err)
	}

	if err := s.db.Delete(&message).Error; err != nil {
		logger.WithError(err).WithField("message_id", messageID).Error("Failed to delete message")
		return fmt.Errorf("failed to delete message: %w", err)
	}

	logger.WithField("message_id", messageID).Info("Message deleted successfully")

	return nil
}

// SendMessageWithAI sends a message and gets an AI response
func (s *ConversationService) SendMessageWithAI(conversationID uint, userID uint, content string) (*models.Message, *models.Message, error) {
	// Check if conversation exists and belongs to user
	var conversation models.Conversation
	if err := s.db.Where("id = ? AND user_id = ?", conversationID, userID).First(&conversation).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, nil, fmt.Errorf("conversation not found")
		}
		logger.WithError(err).WithField("conversation_id", conversationID).Error("Failed to get conversation")
		return nil, nil, fmt.Errorf("failed to get conversation: %w", err)
	}

	// Create user message
	userMessage := models.Message{
		ConversationID: conversationID,
		UserID:         userID,
		Content:        content,
		Type:           "text",
		Status:         "sent",
		IsAI:           false,
	}

	if err := s.db.Create(&userMessage).Error; err != nil {
		logger.WithError(err).WithField("conversation_id", conversationID).Error("Failed to create user message")
		return nil, nil, fmt.Errorf("failed to create user message: %w", err)
	}

	// Get conversation history
	var messages []models.Message
	if err := s.db.Where("conversation_id = ?", conversationID).Order("created_at ASC").Find(&messages).Error; err != nil {
		logger.WithError(err).WithField("conversation_id", conversationID).Error("Failed to get conversation history")
		return nil, nil, fmt.Errorf("failed to get conversation history: %w", err)
	}

	// Analyze complexity of the message
	complexity := s.aiService.AnalyzeComplexity(content)

	// Get AI response
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	aiResponse, err := s.aiService.Query(ctx, content, messages, complexity)
	if err != nil {
		logger.WithError(err).WithField("conversation_id", conversationID).Error("Failed to get AI response")
		return &userMessage, nil, fmt.Errorf("failed to get AI response: %w", err)
	}

	// Create AI message
	aiMessage := models.Message{
		ConversationID: conversationID,
		UserID:         userID, // AI responses are associated with the user who asked the question
		Content:        aiResponse,
		Type:           "text",
		Status:         "sent",
		IsAI:           true,
		AIModel:        "gpt-4", // This should be determined based on which AI model was used
	}

	if err := s.db.Create(&aiMessage).Error; err != nil {
		logger.WithError(err).WithField("conversation_id", conversationID).Error("Failed to create AI message")
		return &userMessage, nil, fmt.Errorf("failed to create AI message: %w", err)
	}

	logger.WithFields(map[string]interface{}{
		"conversation_id": conversationID,
		"user_message_id": userMessage.ID,
		"ai_message_id":   aiMessage.ID,
	}).Info("AI response created successfully")

	return &userMessage, &aiMessage, nil
}

// GetConversationStats retrieves statistics for a conversation
func (s *ConversationService) GetConversationStats(conversationID uint, userID uint) (*models.ConversationStats, error) {
	// Check if conversation exists and belongs to user
	var conversation models.Conversation
	if err := s.db.Where("id = ? AND user_id = ?", conversationID, userID).First(&conversation).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("conversation not found")
		}
		logger.WithError(err).WithField("conversation_id", conversationID).Error("Failed to get conversation")
		return nil, fmt.Errorf("failed to get conversation: %w", err)
	}

	var stats models.ConversationStats

	// Get total messages count
	if err := s.db.Model(&models.Message{}).Where("conversation_id = ?", conversationID).Count(&stats.TotalMessages).Error; err != nil {
		logger.WithError(err).WithField("conversation_id", conversationID).Error("Failed to count messages")
		return nil, fmt.Errorf("failed to count messages: %w", err)
	}

	// Get average sentiment
	var avgSentiment sql.NullFloat64
	if err := s.db.Model(&models.Message{}).Where("conversation_id = ?", conversationID).Select("AVG(sentiment)").Scan(&avgSentiment).Error; err != nil {
		logger.WithError(err).WithField("conversation_id", conversationID).Error("Failed to calculate average sentiment")
		return nil, fmt.Errorf("failed to calculate average sentiment: %w", err)
	}
	if avgSentiment.Valid {
		stats.AverageSentiment = avgSentiment.Float64
	}

	// Get first and last message timestamps
	var firstMessage, lastMessage models.Message
	if err := s.db.Where("conversation_id = ?", conversationID).Order("created_at ASC").First(&firstMessage).Error; err != nil {
		logger.WithError(err).WithField("conversation_id", conversationID).Error("Failed to get first message")
		return nil, fmt.Errorf("failed to get first message: %w", err)
	}
	stats.FirstMessageAt = firstMessage.CreatedAt

	if err := s.db.Where("conversation_id = ?", conversationID).Order("created_at DESC").First(&lastMessage).Error; err != nil {
		logger.WithError(err).WithField("conversation_id", conversationID).Error("Failed to get last message")
		return nil, fmt.Errorf("failed to get last message: %w", err)
	}
	stats.LastMessageAt = lastMessage.CreatedAt

	// Calculate average response time (time between user messages and AI responses)
	// This is a simplified calculation and could be improved
	if stats.TotalMessages > 1 {
		var responseTimes []int64
		var prevMessage models.Message
		isUserMessage := false

		var allMessages []models.Message
		if err := s.db.Where("conversation_id = ?", conversationID).Order("created_at ASC").Find(&allMessages).Error; err != nil {
			logger.WithError(err).WithField("conversation_id", conversationID).Error("Failed to get messages for response time calculation")
			return nil, fmt.Errorf("failed to get messages for response time calculation: %w", err)
		}

		for _, msg := range allMessages {
			if !msg.IsAI {
				if isUserMessage {
					// Consecutive user messages, skip
					prevMessage = msg
					continue
				}
				isUserMessage = true
				prevMessage = msg
			} else {
				if isUserMessage {
					// AI response to user message, calculate response time
					responseTime := msg.CreatedAt.Sub(prevMessage.CreatedAt).Seconds()
					responseTimes = append(responseTimes, int64(responseTime))
				}
				isUserMessage = false
			}
		}

		if len(responseTimes) > 0 {
			var totalResponseTime int64 = 0
			for _, rt := range responseTimes {
				totalResponseTime += rt
			}
			stats.ResponseTime = totalResponseTime / int64(len(responseTimes))
		}
	}

	return &stats, nil
}
