package service

import (
	"errors"
	"log"

	"github.com/2930134478/AI-CS/backend/models"
	"github.com/2930134478/AI-CS/backend/repository"
	"gorm.io/gorm"
)

// ErrConversationClosed indicates operations are attempted on a closed conversation.
var (
	// ErrConversationClosed 表示会话已关闭，不能继续发送消息。
	ErrConversationClosed = errors.New("conversation is closed")
	// ErrConversationNotFound 表示未找到指定的会话记录。
	ErrConversationNotFound = gorm.ErrRecordNotFound
)

// MessageService 负责消息领域的业务处理。
type MessageService struct {
	conversations *repository.ConversationRepository
	messages      *repository.MessageRepository
	hub           BroadcastHub
	aiService     *AIService // AI 服务（用于 AI 自动回复）
}

// NewMessageService 创建 MessageService 实例。
func NewMessageService(
	conversations *repository.ConversationRepository,
	messages *repository.MessageRepository,
	hub BroadcastHub,
	aiService *AIService,
) *MessageService {
	return &MessageService{
		conversations: conversations,
		messages:      messages,
		hub:           hub,
		aiService:     aiService,
	}
}

// CreateMessage 创建消息并通过 WebSocket 广播。
func (s *MessageService) CreateMessage(input CreateMessageInput) (*models.Message, error) {
	conv, err := s.conversations.GetByID(input.ConversationID)
	if err != nil {
		return nil, err
	}

	// B 方案：会话关闭后，如访客再次发消息则自动 reopen
	if conv.Status == "closed" {
		if input.SenderIsAgent {
			return nil, ErrConversationClosed
		}
		if err := s.conversations.UpdateFields(conv.ID, map[string]interface{}{
			"status": "open",
		}); err != nil {
			return nil, err
		}
		conv.Status = "open"
	}

	if input.SenderIsAgent && input.SenderID == 0 {
		return nil, errors.New("sender_id is required for agent messages")
	}

	message := &models.Message{
		ConversationID: input.ConversationID,
		SenderID:       input.SenderID,
		SenderIsAgent:  input.SenderIsAgent,
		Content:        input.Content,
		MessageType:    "user_message",
		ChatMode:       conv.ChatMode,
		IsRead:         false,
		FileURL:        input.FileURL,
		FileType:       input.FileType,
		FileName:       input.FileName,
		FileSize:       input.FileSize,
		MimeType:       input.MimeType,
	}

	if err := s.messages.Create(message); err != nil {
		return nil, err
	}

	// 如果客服发送消息，且会话的 agent_id 为 0，则更新为当前客服的 ID
	updateFields := map[string]interface{}{
		"updated_at": message.CreatedAt,
	}
	if input.SenderIsAgent && input.SenderID > 0 && conv.AgentID == 0 {
		updateFields["agent_id"] = input.SenderID
	}

	if err := s.conversations.UpdateFields(conv.ID, updateFields); err != nil {
		return nil, err
	}

	if s.hub != nil {
		// 1. 先广播到该对话的所有客户端（访客和已连接该对话的客服）
		s.hub.BroadcastMessage(message.ConversationID, "new_message", message)
		// 2. 如果是访客发送的消息，且对话模式是人工客服，才广播到所有客服
		//    这样即使客服没有连接到这个对话，也能收到新消息的通知
		//    注意：AI 模式下的访客消息不广播给客服（避免干扰）
		if !input.SenderIsAgent && conv.ChatMode == "human" {
			s.hub.BroadcastToAllAgents("new_message", message)
		}
	} else {
		log.Printf("⚠️ WebSocket Hub 为空，无法广播消息: 消息ID=%d, 对话ID=%d", message.ID, message.ConversationID)
	}

	// 3. 触发 AI 回复（文本/识图或生图，具体由 AI 配置的 model_type 决定）
	needAIReply := s.aiService != nil && conv.ChatMode == "ai" && (
		(!input.SenderIsAgent) || (conv.ConversationType == "internal" && input.SenderIsAgent))
	if needAIReply {
		go func() {
			// 用于查找 AI 配置的用户 ID：访客对话用 AgentID，内部对话用发送者（客服）ID
			userID := conv.AgentID
			if userID == 0 {
				userID = 1
			}
			if conv.ConversationType == "internal" && input.SenderID > 0 {
				userID = input.SenderID
			}

			opts := &GenerateAIResponseInput{
				UseKnowledgeBase: input.UseKnowledgeBase,
				UseLLM:           input.UseLLM,
				UseWebSearch:     input.UseWebSearch,
				NeedWebSearch:    input.NeedWebSearch,
			}
			if opts.UseKnowledgeBase == nil {
				t := true
				opts.UseKnowledgeBase = &t
			}
			if opts.UseLLM == nil {
				t := true
				opts.UseLLM = &t
			}
			if opts.UseWebSearch == nil {
				f := false
				opts.UseWebSearch = &f
			}
			// 多模态识图：当前条消息带图片时传给 AI
			if input.FileURL != nil && input.FileType != nil && *input.FileType == "image" {
				mime := ""
				if input.MimeType != nil {
					mime = *input.MimeType
				}
				opts.Attachment = &MessageAttachment{
					FileURL:  *input.FileURL,
					FileType: "image",
					MimeType: mime,
				}
			}
			aiResult, err := s.aiService.GenerateAIResponseWithOptions(message.ConversationID, input.Content, userID, opts)
			aiResponse := ""
			sourcesUsed := ""
			var aiMessageFileURL *string
			aiGenFailed := false
			if err != nil {
				log.Printf("❌ AI 生成回复失败: %v", err)
				aiResponse = "AI客服好像出了点差错，请联系人工客服解决"
				aiGenFailed = true
			} else {
				aiResponse = aiResult.Content
				sourcesUsed = aiResult.SourcesUsed
				aiMessageFileURL = aiResult.GeneratedFileURL
				aiGenFailed = aiResult.GenerationFailed
			}

			// 生图时前端依赖 file_type === "image" 才渲染图片，必须设置
			var aiMessageFileType *string
			if aiMessageFileURL != nil {
				t := "image"
				aiMessageFileType = &t
			}
			aiMessage := &models.Message{
				ConversationID:       message.ConversationID,
				SenderID:             0,
				SenderIsAgent:        true,
				Content:              aiResponse,
				MessageType:          "user_message",
				ChatMode:             conv.ChatMode,
				IsRead:               false,
				SourcesUsed:          sourcesUsed,
				FileURL:              aiMessageFileURL,
				FileType:             aiMessageFileType,
				IsAIGenerationFailed: aiGenFailed,
			}

			if err := s.messages.Create(aiMessage); err != nil {
				log.Printf("❌ 创建 AI 回复消息失败: %v", err)
				return
			}

			// 更新对话的更新时间
			if err := s.conversations.UpdateFields(conv.ID, map[string]interface{}{
				"updated_at": aiMessage.CreatedAt,
			}); err != nil {
				log.Printf("⚠️ 更新对话时间失败: %v", err)
			}

			// 广播 AI 回复消息
			if s.hub != nil {
				// AI 回复只广播给访客，不广播给客服（避免干扰）
				// 客服可以在会话页面手动开启"显示 AI 消息"来查看
				s.hub.BroadcastMessage(aiMessage.ConversationID, "new_message", aiMessage)
				// 不再广播到所有客服
				// s.hub.BroadcastToAllAgents("new_message", aiMessage)
			}
		}()
	}

	return message, nil
}

// ListMessages 返回会话内的消息列表。
// includeAIMessages: 是否包含 AI 消息（默认 false，不包含）
// 如果 includeAIMessages == false，过滤掉所有 chat_mode == "ai" 的消息
// 这样就能准确区分 AI 模式下的消息和人工模式下的消息，即使对话模式切换了也能正确过滤
func (s *MessageService) ListMessages(conversationID uint, includeAIMessages bool) ([]models.Message, error) {
	messages, err := s.messages.ListByConversationID(conversationID)
	if err != nil {
		return nil, err
	}

	// 如果不包含 AI 消息，过滤掉所有 chat_mode == "ai" 的消息
	// 这样，无论对话当前是什么模式，都能准确过滤掉 AI 模式下的所有消息
	// 包括：访客在 AI 模式下发送的消息、AI 回复消息
	if !includeAIMessages {
		filtered := make([]models.Message, 0, len(messages))
		for _, msg := range messages {
			// 只显示 chat_mode != "ai" 的消息（人工模式下的消息）
			// 如果 chat_mode 为空（兼容历史数据），则根据 SenderID 和 SenderIsAgent 判断
			if msg.ChatMode != "" {
				// 有 chat_mode 字段，直接根据字段过滤
				if msg.ChatMode != "ai" {
					filtered = append(filtered, msg)
				}
			} else {
				// 兼容历史数据：chat_mode 为空时，使用旧逻辑
				// 过滤掉 AI 回复消息（SenderID == 0 && SenderIsAgent == true）
				if msg.SenderID != 0 || !msg.SenderIsAgent {
					filtered = append(filtered, msg)
				}
			}
		}
		return filtered, nil
	}

	return messages, nil
}

// MarkMessagesRead 将消息标记为已读并通知监听方。
func (s *MessageService) MarkMessagesRead(conversationID uint, readerIsAgent bool) (*MarkMessagesReadResult, error) {
	messageIDs, unreadRemaining, readAt, err := s.messages.MarkMessagesRead(conversationID, !readerIsAgent)
	if err != nil {
		return nil, err
	}

	result := &MarkMessagesReadResult{
		ConversationID: conversationID,
		MessageIDs:     messageIDs,
		UnreadCount:    unreadRemaining,
		ReadAt:         readAt,
	}

	if s.hub != nil && len(messageIDs) > 0 {
		s.hub.BroadcastMessage(conversationID, "messages_read", map[string]interface{}{
			"message_ids":     messageIDs,
			"reader_is_agent": readerIsAgent,
			"read_at":         readAt,
			"unread_count":    unreadRemaining,
			"conversation_id": conversationID, // 确保 payload 中也包含 conversation_id
		})
	}

	return result, nil
}
