package dashboard

import (
	"agent-desk/internal/pkg/httpx"
	"encoding/json"
	"strings"

	"agent-desk/internal/models"
	"agent-desk/internal/pkg/constants"
	"agent-desk/internal/pkg/dto/request"
	"agent-desk/internal/pkg/dto/response"
	"agent-desk/internal/pkg/enums"
	"agent-desk/internal/pkg/i18nx"
	"agent-desk/internal/pkg/toolx"
	"agent-desk/internal/pkg/utils"
	"agent-desk/internal/services"

	"agent-desk/internal/pkg/httpx/params"

	"github.com/gin-gonic/gin"
	"github.com/mlogclub/simple/sqls"
	"github.com/mlogclub/simple/web"
)

func AIAgentAnyList(ctx *gin.Context) {
	if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionAIAgentView); err != nil {
		httpx.WriteJSON(ctx, err)
		return
	}
	cnd := params.NewPagedSqlCnd(ctx,
		params.QueryFilter{ParamName: "status"},
		params.QueryFilter{ParamName: "name", Op: params.Like},
		params.QueryFilter{ParamName: "code", Op: params.Like},
	).Asc("sort_no").Desc("id")
	list, paging := services.AIAgentService.FindPageByCnd(cnd)
	results := make([]response.AIAgentResponse, 0, len(list))
	for _, item := range list {
		results = append(results, buildAIAgentResponseWithLocale(&item, i18nx.Locale(ctx)))
	}
	httpx.WriteJSON(ctx, &web.PageResult{Results: results, Page: paging})
}

func AIAgentGetList_all(ctx *gin.Context) {
	if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionAIAgentView); err != nil {
		httpx.WriteJSON(ctx, err)
		return
	}
	list := services.AIAgentService.Find(sqls.NewCnd().Where("status = ?", enums.StatusOk).Desc("sort_no").Desc("id"))
	results := make([]response.AIAgentResponse, 0, len(list))
	for _, item := range list {
		results = append(results, buildAIAgentResponseWithLocale(&item, i18nx.Locale(ctx)))
	}
	httpx.WriteJSON(ctx, results)
}

func AIAgentGetBy(ctx *gin.Context) {
	id, ok := httpx.GetPathInt64(ctx, "id")
	if !ok {
		return
	}
	if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionAIAgentView); err != nil {
		httpx.WriteJSON(ctx, err)
		return
	}
	item := services.AIAgentService.Get(id)
	if item == nil {
		httpx.WriteJSON(ctx, httpx.JsonErrorMsg(ctx, "error.e0002"))
		return
	}
	httpx.WriteJSON(ctx, buildAIAgentResponseWithLocale(item, i18nx.Locale(ctx)))
}

func AIAgentPostCreate(ctx *gin.Context) {
	operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionAIAgentCreate)
	if err != nil {
		httpx.WriteJSON(ctx, err)
		return
	}
	req := request.CreateAIAgentRequest{}
	if err := params.ReadJSON(ctx, &req); err != nil {
		httpx.WriteJSON(ctx, err)
		return
	}
	item, err := services.AIAgentService.CreateAIAgent(req, operator)
	if err != nil {
		httpx.WriteJSON(ctx, err)
		return
	}
	httpx.WriteJSON(ctx, buildAIAgentResponseWithLocale(item, i18nx.Locale(ctx)))
}

func AIAgentPostUpdate(ctx *gin.Context) {
	operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionAIAgentUpdate)
	if err != nil {
		httpx.WriteJSON(ctx, err)
		return
	}
	req := request.UpdateAIAgentRequest{}
	if err := params.ReadJSON(ctx, &req); err != nil {
		httpx.WriteJSON(ctx, err)
		return
	}
	if err := services.AIAgentService.UpdateAIAgent(req, operator); err != nil {
		httpx.WriteJSON(ctx, err)
		return
	}
	httpx.WriteJSON(ctx, nil)
}

func AIAgentPostDelete(ctx *gin.Context) {
	operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionAIAgentDelete)
	if err != nil {
		httpx.WriteJSON(ctx, err)
		return
	}
	req := request.DeleteAIAgentRequest{}
	if err := params.ReadJSON(ctx, &req); err != nil {
		httpx.WriteJSON(ctx, err)
		return
	}
	if err := services.AIAgentService.DeleteAIAgent(req.ID, operator); err != nil {
		httpx.WriteJSON(ctx, err)
		return
	}
	httpx.WriteJSON(ctx, nil)
}

func AIAgentPostUpdate_sort(ctx *gin.Context) {
	if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionAIAgentUpdate); err != nil {
		httpx.WriteJSON(ctx, err)
		return
	}
	var ids []int64
	if err := params.ReadJSON(ctx, &ids); err != nil {
		httpx.WriteJSON(ctx, err)
		return
	}
	if err := services.AIAgentService.UpdateSort(ids); err != nil {
		httpx.WriteJSON(ctx, err)
		return
	}
	httpx.WriteJSON(ctx, nil)
}

func AIAgentPostUpdate_status(ctx *gin.Context) {
	operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionAIAgentUpdate)
	if err != nil {
		httpx.WriteJSON(ctx, err)
		return
	}
	req := request.UpdateAIAgentStatusRequest{}
	if err := params.ReadJSON(ctx, &req); err != nil {
		httpx.WriteJSON(ctx, err)
		return
	}
	if err := services.AIAgentService.UpdateStatus(req.ID, req.Status, operator); err != nil {
		httpx.WriteJSON(ctx, err)
		return
	}
	httpx.WriteJSON(ctx, nil)
}

func buildAIAgentResponse(item *models.AIAgent) response.AIAgentResponse {
	return buildAIAgentResponseWithLocale(item, i18nx.DefaultLocale)
}

func buildAIAgentResponseWithLocale(item *models.AIAgent, locale string) response.AIAgentResponse {
	ret := response.AIAgentResponse{
		ID:                  item.ID,
		Name:                item.Name,
		Description:         item.Description,
		Status:              item.Status,
		StatusName:          enums.GetStatusLabel(item.Status),
		AIConfigID:          item.AIConfigID,
		ServiceMode:         item.ServiceMode,
		ServiceModeName:     enums.GetIMConversationServiceModeLabel(item.ServiceMode),
		SystemPrompt:        item.SystemPrompt,
		WelcomeMessage:      item.WelcomeMessage,
		ReplyTimeoutSeconds: item.ReplyTimeoutSeconds,
		HandoffMode:         item.HandoffMode,
		HandoffModeName:     enums.GetAIAgentHandoffModeLabel(item.HandoffMode),
		FallbackMode:        item.FallbackMode,
		FallbackModeName:    enums.GetAIAgentFallbackModeLabel(item.FallbackMode),
		FallbackMessage:     item.FallbackMessage,
		KnowledgeIDs:        utils.SplitInt64s(item.KnowledgeIDs),
		SkillIDs:            utils.SplitInt64s(item.SkillIDs),
		KnowledgeBaseNames:  make([]string, 0),
		Skills:              make([]response.AIAgentSkillResponse, 0),
		Teams:               make([]response.AIAgentTeamResponse, 0),
		DirectTools:         make([]response.AIAgentMCPToolResponse, 0),
		GraphTools:          make([]string, 0),
		WorkflowVersionID:   item.WorkflowVersionID,
		WorkflowPublished:   item.WorkflowVersionID > 0,
		WorkflowState:       aiAgentWorkflowState(item.WorkflowVersionID),
		WorkflowStateText:   aiAgentWorkflowStateText(item.WorkflowVersionID),
		SortNo:              item.SortNo,
		CreatedAt:           item.CreatedAt.Format("2006-01-02 15:04:05"),
		UpdatedAt:           item.UpdatedAt.Format("2006-01-02 15:04:05"),
		CreateUserName:      item.CreateUserName,
		UpdateUserName:      item.UpdateUserName,
	}
	if aiConfig := services.AIConfigService.Get(item.AIConfigID); aiConfig != nil {
		ret.AIConfigName = aiConfig.Name
	}
	for _, id := range utils.SplitInt64s(item.TeamIDs) {
		if team := services.AgentTeamService.Get(id); team != nil {
			ret.Teams = append(ret.Teams, response.AIAgentTeamResponse{
				ID:   team.ID,
				Name: team.Name,
			})
		}
	}
	for _, id := range ret.KnowledgeIDs {
		if knowledgeBase := services.KnowledgeBaseService.Get(id); knowledgeBase != nil {
			ret.KnowledgeBaseNames = append(ret.KnowledgeBaseNames, knowledgeBase.Name)
		}
	}
	for _, id := range ret.SkillIDs {
		if skill := services.SkillDefinitionService.Get(id); skill != nil {
			ret.Skills = append(ret.Skills, response.AIAgentSkillResponse{
				ID:   skill.ID,
				Name: skill.Name,
			})
		}
	}
	if raw := strings.TrimSpace(item.AllowedMCPTools); raw != "" {
		var directTools []request.AIAgentMCPToolRequest
		if err := json.Unmarshal([]byte(raw), &directTools); err == nil {
			for _, tool := range directTools {
				toolCode := strings.TrimSpace(tool.ToolCode)
				if toolCode == "" {
					toolCode = toolx.BuildMCPToolCode(tool.ServerCode, tool.ToolName)
				}
				toolCode = toolx.NormalizeToolCodeAlias(toolCode)
				if toolx.IsAutoInjectedToolCode(toolCode) {
					continue
				}
				if toolx.IsAgentDirectGraphToolCode(toolCode) {
					ret.GraphTools = appendGraphToolCodeIfMissing(ret.GraphTools, toolCode)
					continue
				}
				serverCode := strings.TrimSpace(tool.ServerCode)
				toolName := strings.TrimSpace(tool.ToolName)
				if registeredServerCode, registeredToolName, ok := toolx.GetRegisteredToolIdentity(toolCode); ok {
					serverCode = registeredServerCode
					toolName = registeredToolName
				} else if parsedServerCode, parsedToolName := toolx.SplitMCPToolCode(toolCode); parsedServerCode != "" && parsedToolName != "" {
					serverCode = parsedServerCode
					toolName = parsedToolName
				}
				title := strings.TrimSpace(tool.Title)
				if title == "" {
					if registeredTitle := toolx.GetRegisteredToolTitleLocale(toolCode, locale); registeredTitle != "" {
						title = registeredTitle
					}
				}
				description := strings.TrimSpace(tool.Description)
				if description == "" {
					if registeredDescription := toolx.GetRegisteredToolDescriptionLocale(toolCode, locale); registeredDescription != "" {
						description = registeredDescription
					}
				}
				ret.DirectTools = append(ret.DirectTools, response.AIAgentMCPToolResponse{
					ToolCode:    toolCode,
					ServerCode:  serverCode,
					ToolName:    toolName,
					Title:       title,
					Description: description,
					Arguments:   tool.Arguments,
				})
			}
		}
	}
	if raw := strings.TrimSpace(item.AllowedGraphTools); raw != "" {
		var graphTools []string
		if err := json.Unmarshal([]byte(raw), &graphTools); err == nil {
			for _, toolCode := range graphTools {
				toolCode = toolx.NormalizeToolCodeAlias(strings.TrimSpace(toolCode))
				if !toolx.IsAgentDirectGraphToolCode(toolCode) {
					continue
				}
				ret.GraphTools = appendGraphToolCodeIfMissing(ret.GraphTools, toolCode)
			}
		}
	}
	return ret
}

func aiAgentWorkflowState(workflowVersionID int64) string {
	if workflowVersionID > 0 {
		return "published"
	}
	return "draft"
}

func aiAgentWorkflowStateText(workflowVersionID int64) string {
	if workflowVersionID > 0 {
		return "已发布"
	}
	return "未发布"
}

func appendGraphToolCodeIfMissing(items []string, toolCode string) []string {
	toolCode = strings.TrimSpace(toolCode)
	if toolCode == "" {
		return items
	}
	for _, item := range items {
		if strings.TrimSpace(item) == toolCode {
			return items
		}
	}
	return append(items, toolCode)
}
