//
//  Copyright 2026 The InfiniFlow Authors. All Rights Reserved.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//

package handler

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"ragflow/internal/common"
	"strings"

	"github.com/gin-gonic/gin"

	"ragflow/internal/service"
)

// ChatSessionHandler chat session (conversation) handler
type ChatSessionHandler struct {
	chatSessionService *service.ChatSessionService
	userService        *service.UserService
}

// NewChatSessionHandler create chat session handler
func NewChatSessionHandler(chatSessionService *service.ChatSessionService, userService *service.UserService) *ChatSessionHandler {
	return &ChatSessionHandler{
		chatSessionService: chatSessionService,
		userService:        userService,
	}
}

// SetChatSession create or update a chat session
// @Summary Set chat session
// @Description Create or update a chat session. If is_new is true, creates new chat session; otherwise updates existing one.
// @Tags chat_session
// @Accept json
// @Produce json
// @Param request body service.SetChatSessionRequest true "chat session configuration"
// @Success 200 {object} service.SetChatSessionResponse
// @Router /v1/conversation/set [post]
func (h *ChatSessionHandler) SetChatSession(c *gin.Context) {
	user, errorCode, errorMessage := GetUser(c)
	if errorCode != common.CodeSuccess {
		jsonError(c, errorCode, errorMessage)
		return
	}
	userID := user.ID

	// Parse request body
	var req service.SetChatSessionRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"code":    400,
			"message": err.Error(),
		})
		return
	}

	// Call service to set chat session
	result, err := h.chatSessionService.SetChatSession(userID, &req)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"code":    500,
			"message": err.Error(),
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"code":    0,
		"data":    result,
		"message": "success",
	})
}

// RemoveChatSessionsRequest remove chat sessions request
type RemoveChatSessionsRequest struct {
	ConversationIDs []string `json:"conversation_ids" binding:"required"`
}

// RemoveChatSessions remove/delete chat sessions
// @Summary Remove Chat Sessions
// @Description Remove chat sessions by their IDs. Only the owner of the chat session can perform this operation.
// @Tags chat_session
// @Accept json
// @Produce json
// @Param request body RemoveChatSessionsRequest true "chat session IDs to remove"
// @Success 200 {object} map[string]interface{}
// @Router /v1/conversation/rm [post]
func (h *ChatSessionHandler) RemoveChatSessions(c *gin.Context) {
	user, errorCode, errorMessage := GetUser(c)
	if errorCode != common.CodeSuccess {
		jsonError(c, errorCode, errorMessage)
		return
	}
	userID := user.ID

	// Parse request body
	var req RemoveChatSessionsRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"code":    400,
			"message": err.Error(),
		})
		return
	}

	// Call service to remove chat sessions
	if err := h.chatSessionService.RemoveChatSessions(userID, req.ConversationIDs); err != nil {
		// Check if it's an authorization error
		if err.Error() == "Only owner of chat session authorized for this operation" {
			c.JSON(http.StatusForbidden, gin.H{
				"code":    403,
				"data":    false,
				"message": err.Error(),
			})
			return
		}
		c.JSON(http.StatusInternalServerError, gin.H{
			"code":    500,
			"message": err.Error(),
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"code":    0,
		"data":    true,
		"message": "success",
	})
}

// ListChatSessions list chat sessions for a dialog
// @Summary List Chat Sessions
// @Description Get list of chat sessions for a specific dialog
// @Tags chat_session
// @Accept json
// @Produce json
// @Param chat_id query string true "chat ID"
// @Success 200 {object} service.ListChatSessionsResponse
// @Router /api/v1/chats/<chat_id>/sessions [get]
func (h *ChatSessionHandler) ListChatSessions(c *gin.Context) {
	user, errorCode, errorMessage := GetUser(c)
	if errorCode != common.CodeSuccess {
		jsonError(c, errorCode, errorMessage)
		return
	}
	userID := user.ID

	// Get chat_id from query parameter
	chatID := c.Param("chat_id")
	if chatID == "" {
		c.JSON(http.StatusBadRequest, gin.H{
			"code":    400,
			"message": "chat_id is required",
		})
		return
	}

	// Call service to list chat sessions
	result, err := h.chatSessionService.ListChatSessions(userID, chatID)
	if err != nil {
		// Check if it's an authorization error
		if err.Error() == "Only owner of dialog authorized for this operation" {
			c.JSON(http.StatusForbidden, gin.H{
				"code":    403,
				"data":    false,
				"message": err.Error(),
			})
			return
		}
		c.JSON(http.StatusInternalServerError, gin.H{
			"code":    500,
			"message": err.Error(),
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"code":    0,
		"data":    result.Sessions,
		"message": "success",
	})
}

// CompletionRequest completion request
type CompletionRequest struct {
	ConversationID   string                   `json:"conversation_id" binding:"required"`
	Messages         []map[string]interface{} `json:"messages" binding:"required"`
	LLMID            string                   `json:"llm_id,omitempty"`
	Stream           *bool                    `json:"stream,omitempty"`
	Thinking         *bool                    `json:"thinking,omitempty"`
	Temperature      float64                  `json:"temperature,omitempty"`
	TopP             float64                  `json:"top_p,omitempty"`
	FrequencyPenalty float64                  `json:"frequency_penalty,omitempty"`
	PresencePenalty  float64                  `json:"presence_penalty,omitempty"`
	MaxTokens        int                      `json:"max_tokens,omitempty"`
}

// Completion chat completion
// @Summary Chat Completion
// @Description Send messages to the chat model and get a response. Supports streaming and non-streaming modes.
// @Tags chat_session
// @Accept json
// @Produce json
// @Param request body CompletionRequest true "completion request"
// @Success 200 {object} map[string]interface{}
// @Router /v1/conversation/completion [post]
func (h *ChatSessionHandler) Completion(c *gin.Context) {
	user, errorCode, errorMessage := GetUser(c)
	if errorCode != common.CodeSuccess {
		jsonError(c, errorCode, errorMessage)
		return
	}
	userID := user.ID

	// Parse request body
	var req CompletionRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"code":    400,
			"message": err.Error(),
		})
		return
	}

	// Build chat model config
	chatModelConfig := make(map[string]interface{})
	if req.Temperature != 0 {
		chatModelConfig["temperature"] = req.Temperature
	}
	if req.TopP != 0 {
		chatModelConfig["top_p"] = req.TopP
	}
	if req.FrequencyPenalty != 0 {
		chatModelConfig["frequency_penalty"] = req.FrequencyPenalty
	}
	if req.PresencePenalty != 0 {
		chatModelConfig["presence_penalty"] = req.PresencePenalty
	}
	if req.MaxTokens != 0 {
		chatModelConfig["max_tokens"] = req.MaxTokens
	}
	if req.Stream != nil {
		chatModelConfig["stream"] = *req.Stream
	}
	if req.Thinking != nil {
		chatModelConfig["thinking"] = *req.Thinking
	}

	// Process messages - filter out system messages and initial assistant messages
	var processedMessages []map[string]interface{}
	for i, m := range req.Messages {
		role, _ := m["role"].(string)
		if role == "system" {
			continue
		}
		if role == "assistant" && len(processedMessages) == 0 {
			continue
		}
		processedMessages = append(processedMessages, m)
		_ = i
	}

	// Get last message ID if present
	var messageID string
	if len(processedMessages) > 0 {
		if id, ok := processedMessages[len(processedMessages)-1]["id"].(string); ok {
			messageID = id
		}
	}

	// Call service
	if req.Stream != nil && *req.Stream {
		// Streaming response
		disableWriteDeadlineForSSE(c)
		c.Header("Content-Type", "text/event-stream")
		c.Header("Cache-Control", "no-cache")
		c.Header("Connection", "keep-alive")
		c.Header("X-Accel-Buffering", "no")

		// Create a channel for streaming data
		streamChan := make(chan string)
		reqCtx := c.Request.Context()
		go func() {
			defer close(streamChan)
			err := h.chatSessionService.CompletionStream(reqCtx, userID, req.ConversationID, processedMessages, req.LLMID, chatModelConfig, messageID, streamChan)
			if err != nil {
				streamChan <- fmt.Sprintf("data: %s\n\n", err.Error())
			}
		}()

		// Stream data to client
		c.Stream(func(w io.Writer) bool {
			data, ok := <-streamChan
			if !ok {
				return false
			}
			c.Writer.Write([]byte(data))
			return true
		})
	} else {
		// Non-streaming response
		result, err := h.chatSessionService.Completion(userID, req.ConversationID, processedMessages, req.LLMID, chatModelConfig, messageID)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{
				"code":    500,
				"message": err.Error(),
			})
			return
		}

		c.JSON(http.StatusOK, gin.H{
			"code":    0,
			"data":    result,
			"message": "",
		})
	}
}

func (h *ChatSessionHandler) GetSession(c *gin.Context) {
	user, errorCode, errorMessage := GetUser(c)
	if errorCode != common.CodeSuccess {
		jsonError(c, errorCode, errorMessage)
		return
	}

	userID := user.ID
	chatID, sessionID := c.Param("chat_id"), c.Param("session_id")

	result, code, err := h.chatSessionService.GetSession(userID, chatID, sessionID)
	if err != nil {
		jsonError(c, code, err.Error())
		return
	}
	jsonResponse(c, common.CodeSuccess, result, "success")
}

// CreateSession create a session in a dialog
func (h *ChatSessionHandler) CreateSession(c *gin.Context) {
	user, errorCode, errorMessage := GetUser(c)
	if errorCode != common.CodeSuccess {
		jsonError(c, errorCode, errorMessage)
		return
	}

	userID := strings.TrimSpace(user.ID)
	if userID == "" {
		jsonError(c, common.CodeBadRequest, "user_id is required")
		return
	}

	chatID := strings.TrimSpace(c.Param("chat_id"))
	if chatID == "" {
		jsonError(c, common.CodeBadRequest, "chat_id is required")
		return
	}

	req := map[string]interface{}{}
	if err := json.NewDecoder(c.Request.Body).Decode(&req); err != nil {
		if errors.Is(err, io.EOF) {
			req = map[string]interface{}{}
		} else {
			jsonError(c, common.CodeArgumentError, err.Error())
			return
		}
	}
	if req == nil {
		req = map[string]interface{}{}
	}

	result, code, err := h.chatSessionService.CreateSession(userID, chatID, req)
	if err != nil {
		if code == common.CodeAuthenticationError {
			jsonResponse(c, code, false, err.Error())
			return
		}
		jsonError(c, code, err.Error())
		return
	}

	jsonResponse(c, common.CodeSuccess, result, "success")
}

// DeleteSessions delete a session in a dialog
func (h *ChatSessionHandler) DeleteSessions(c *gin.Context) {
	user, errorCode, errorMessage := GetUser(c)
	if errorCode != common.CodeSuccess {
		jsonError(c, errorCode, errorMessage)
		return
	}

	chatID := strings.TrimSpace(c.Param("chat_id"))
	if chatID == "" {
		jsonError(c, common.CodeBadRequest, "chat_id is required")
		return
	}

	userID := strings.TrimSpace(user.ID)
	if userID == "" {
		jsonError(c, common.CodeBadRequest, "user_id is required")
		return
	}

	req := map[string]interface{}{}
	if err := json.NewDecoder(c.Request.Body).Decode(&req); err != nil {
		if errors.Is(err, io.EOF) {
			req = map[string]interface{}{}
		} else {
			jsonError(c, common.CodeArgumentError, err.Error())
			return
		}
	}
	if req == nil {
		req = map[string]interface{}{}
	}

	result, message, code, err := h.chatSessionService.DeleteSessions(userID, chatID, req)
	if err != nil {
		if code == common.CodeAuthenticationError {
			jsonResponse(c, code, false, err.Error())
			return
		}
		jsonError(c, code, err.Error())
		return
	}

	jsonResponse(c, common.CodeSuccess, result, message)
}

func (h *ChatSessionHandler) UpdateSession(c *gin.Context) {
	user, errorCode, errorMessage := GetUser(c)
	if errorCode != common.CodeSuccess {
		jsonError(c, errorCode, errorMessage)
		return
	}

	userID := user.ID
	chatID, sessionID := c.Param("chat_id"), c.Param("session_id")

	req := map[string]any{}
	if err := c.ShouldBindJSON(&req); err != nil {
		if errors.Is(err, io.EOF) {
			jsonError(c, common.CodeArgumentError, "Request body cannot be empty")
			return
		}
		jsonError(c, common.CodeArgumentError, "Invalid request: "+err.Error())
		return
	}
	if len(req) == 0 {
		jsonError(c, common.CodeArgumentError, "Request body cannot be empty")
		return
	}

	result, code, err := h.chatSessionService.UpdateSession(userID, chatID, sessionID, req)
	if err != nil {
		jsonError(c, code, err.Error())
		return
	}
	jsonResponse(c, common.CodeSuccess, result, "success")
}
