package tech.easyflow.ai.publish;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.mybatisflex.core.query.QueryWrapper;
import org.springframework.stereotype.Component;
import tech.easyflow.ai.entity.Bot;
import tech.easyflow.ai.entity.BotCategory;
import tech.easyflow.ai.entity.BotDocumentCollection;
import tech.easyflow.ai.entity.BotMcp;
import tech.easyflow.ai.entity.BotPlugin;
import tech.easyflow.ai.entity.BotWorkflow;
import tech.easyflow.ai.entity.DocumentCollection;
import tech.easyflow.ai.entity.Mcp;
import tech.easyflow.ai.entity.Model;
import tech.easyflow.ai.entity.PluginItem;
import tech.easyflow.ai.entity.Workflow;
import tech.easyflow.ai.enums.PublishStatus;
import tech.easyflow.ai.service.BotCategoryService;
import tech.easyflow.ai.service.BotDocumentCollectionService;
import tech.easyflow.ai.service.BotMcpService;
import tech.easyflow.ai.service.BotPluginService;
import tech.easyflow.ai.service.BotService;
import tech.easyflow.ai.service.BotWorkflowService;
import tech.easyflow.ai.service.DocumentCollectionService;
import tech.easyflow.ai.service.McpService;
import tech.easyflow.ai.service.ModelService;
import tech.easyflow.ai.service.PluginItemService;
import tech.easyflow.ai.service.WorkflowService;
import tech.easyflow.approval.service.ApprovalInstanceService;
import tech.easyflow.approval.enums.ApprovalResourceType;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.satoken.util.SaTokenUtil;
import tech.easyflow.common.web.exceptions.BusinessException;
import tech.easyflow.system.entity.SysDept;
import tech.easyflow.system.service.CategoryPermissionService;
import tech.easyflow.system.service.SysDeptService;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * 聊天助手生命周期处理器。
 */
@Component
public class BotApprovalSubjectHandler extends AbstractAiResourceLifecycleHandler<Bot> {

    private final BotService botService;
    private final BotWorkflowService botWorkflowService;
    private final BotDocumentCollectionService botDocumentCollectionService;
    private final BotPluginService botPluginService;
    private final BotMcpService botMcpService;
    private final WorkflowService workflowService;
    private final DocumentCollectionService documentCollectionService;
    private final CategoryPermissionService categoryPermissionService;
    private final ModelService modelService;
    private final BotCategoryService botCategoryService;
    private final SysDeptService sysDeptService;
    private final PluginItemService pluginItemService;
    private final McpService mcpService;

    public BotApprovalSubjectHandler(BotService botService,
                                     BotWorkflowService botWorkflowService,
                                     BotDocumentCollectionService botDocumentCollectionService,
                                     BotPluginService botPluginService,
                                     BotMcpService botMcpService,
                                     WorkflowService workflowService,
                                     DocumentCollectionService documentCollectionService,
                                     ApprovalInstanceService approvalInstanceService,
                                     CategoryPermissionService categoryPermissionService,
                                     ModelService modelService,
                                     BotCategoryService botCategoryService,
                                     SysDeptService sysDeptService,
                                     PluginItemService pluginItemService,
                                     McpService mcpService,
                                     ObjectMapper objectMapper) {
        super(approvalInstanceService, objectMapper);
        this.botService = botService;
        this.botWorkflowService = botWorkflowService;
        this.botDocumentCollectionService = botDocumentCollectionService;
        this.botPluginService = botPluginService;
        this.botMcpService = botMcpService;
        this.workflowService = workflowService;
        this.documentCollectionService = documentCollectionService;
        this.categoryPermissionService = categoryPermissionService;
        this.modelService = modelService;
        this.botCategoryService = botCategoryService;
        this.sysDeptService = sysDeptService;
        this.pluginItemService = pluginItemService;
        this.mcpService = mcpService;
    }

    @Override
    public String resourceType() {
        return ApprovalResourceType.BOT.getCode();
    }

    @Override
    public void assertPublishedAccess(Object identifier, String denyMessage) {
        Bot bot = botService.getDetail(String.valueOf(identifier));
        if (bot == null || !PublishStatus.from(bot.getPublishStatus()).isExternallyVisible()
                || bot.getPublishedSnapshotJson() == null || bot.getPublishedSnapshotJson().isEmpty()) {
            throw new BusinessException(denyMessage);
        }
    }

    @Override
    protected Bot requireResource(BigInteger resourceId) {
        Bot bot = botService.getById(resourceId);
        if (bot == null) {
            throw new BusinessException("聊天助手不存在");
        }
        return bot;
    }

    @Override
    protected void assertManagePermission(Bot resource) {
        LoginAccount account = SaTokenUtil.getLoginAccount();
        boolean superAdmin = categoryPermissionService.isCurrentSuperAdmin();
        boolean creator = account != null && account.getId() != null && account.getId().equals(resource.getCreatedBy());
        if (!superAdmin && !creator) {
            throw new BusinessException("仅创建者或超级管理员可管理聊天助手");
        }
    }

    @Override
    protected BigInteger getCategoryId(Bot resource) {
        return resource.getCategoryId();
    }

    @Override
    protected BigInteger getDeptId(Bot resource) {
        return resource.getDeptId();
    }

    @Override
    protected String getTitle(Bot resource) {
        return resource.getTitle();
    }

    @Override
    protected PublishStatus getCurrentStatus(Bot resource) {
        return PublishStatus.from(resource.getPublishStatus());
    }

    @Override
    protected Map<String, Object> getPublishedSnapshot(Bot resource) {
        return resource.getPublishedSnapshotJson();
    }

    @Override
    protected Map<String, Object> buildResourceSnapshot(Bot bot) {
        Model model = resolveModel(bot.getModelId());
        BotCategory category = resolveCategory(bot.getCategoryId());
        SysDept dept = resolveDept(bot.getDeptId());
        Map<String, Object> snapshot = new LinkedHashMap<>();
        snapshot.put("id", bot.getId());
        snapshot.put("alias", bot.getAlias());
        snapshot.put("deptId", bot.getDeptId());
        snapshot.put("deptName", dept == null ? null : dept.getDeptName());
        snapshot.put("categoryId", bot.getCategoryId());
        snapshot.put("categoryName", category == null ? null : category.getCategoryName());
        snapshot.put("title", bot.getTitle());
        snapshot.put("description", bot.getDescription());
        snapshot.put("icon", bot.getIcon());
        snapshot.put("modelId", bot.getModelId());
        snapshot.put("modelName", resolveModelName(model));
        snapshot.put("modelOptions", bot.getModelOptions());
        snapshot.put("systemPrompt", resolveSystemPrompt(bot));
        snapshot.put("temperature", readNumberOption(bot.getModelOptions(), "temperature"));
        snapshot.put("topP", readNumberOption(bot.getModelOptions(), "topP"));
        snapshot.put("topK", readNumberOption(bot.getModelOptions(), "topK"));
        snapshot.put("maxReplyLength", readNumberOption(bot.getModelOptions(), "maxReplyLength"));
        snapshot.put("maxMessageCount", readNumberOption(bot.getModelOptions(), Bot.KEY_MAX_MESSAGE_COUNT));
        snapshot.put("status", bot.getStatus());
        snapshot.put("options", bot.getOptions());
        snapshot.put("anonymousEnabled", bot.isAnonymousEnabled());
        snapshot.put("workflowBindings", buildWorkflowBindings(bot.getId()));
        snapshot.put("knowledgeBindings", buildKnowledgeBindings(bot.getId()));
        snapshot.put("pluginBindings", buildPluginBindings(bot.getId()));
        snapshot.put("mcpBindings", buildMcpBindings(bot.getId()));
        return snapshot;
    }

    @Override
    protected void persistResourceState(BigInteger resourceId, PublishStatus publishStatus, BigInteger currentApprovalInstanceId) {
        Bot update = new Bot();
        update.setId(resourceId);
        update.setPublishStatus(publishStatus.getCode());
        update.setCurrentApprovalInstanceId(currentApprovalInstanceId);
        botService.updateById(update);
    }

    @Override
    protected void publishResource(BigInteger resourceId, Map<String, Object> resourceSnapshot, BigInteger operatorId) {
        Bot update = new Bot();
        update.setId(resourceId);
        update.setPublishStatus(PublishStatus.PUBLISHED.getCode());
        update.setCurrentApprovalInstanceId(null);
        update.setPublishedSnapshotJson(resourceSnapshot);
        update.setPublishedAt(new java.util.Date());
        update.setPublishedBy(operatorId);
        botService.updateById(update);
    }

    @Override
    protected void markResourceOffline(BigInteger resourceId) {
        Bot update = new Bot();
        update.setId(resourceId);
        update.setPublishStatus(PublishStatus.OFFLINE.getCode());
        update.setCurrentApprovalInstanceId(null);
        botService.updateById(update);
    }

    @Override
    protected void removeResource(BigInteger resourceId) {
        botService.removeById(resourceId);
    }

    @Override
    protected String resourceLabel() {
        return "聊天助手";
    }

    @Override
    protected void beforeRemove(BigInteger resourceId) {
        removeBotRelations(resourceId);
    }

    private List<Map<String, Object>> buildWorkflowBindings(BigInteger botId) {
        QueryWrapper queryWrapper = QueryWrapper.create().eq(BotWorkflow::getBotId, botId);
        List<BotWorkflow> relations = botWorkflowService.getMapper().selectListWithRelationsByQuery(queryWrapper);
        relations.sort(Comparator.comparing(BotWorkflow::getWorkflowId, Comparator.nullsLast(BigInteger::compareTo)));
        List<Map<String, Object>> result = new ArrayList<>();
        for (BotWorkflow relation : relations) {
            Workflow workflow = relation.getWorkflow();
            if (workflow == null || !PublishStatus.from(workflow.getPublishStatus()).isSelectableForBot()) {
                throw new BusinessException("聊天助手绑定的工作流未发布，无法发布聊天助手");
            }
            Map<String, Object> item = new LinkedHashMap<>();
            item.put("workflowId", relation.getWorkflowId());
            item.put("workflowName", workflow.getTitle());
            result.add(item);
        }
        return result;
    }

    private List<Map<String, Object>> buildKnowledgeBindings(BigInteger botId) {
        QueryWrapper queryWrapper = QueryWrapper.create().eq(BotDocumentCollection::getBotId, botId);
        List<BotDocumentCollection> relations = botDocumentCollectionService.getMapper().selectListWithRelationsByQuery(queryWrapper);
        relations.sort(Comparator.comparing(BotDocumentCollection::getDocumentCollectionId, Comparator.nullsLast(BigInteger::compareTo)));
        List<Map<String, Object>> result = new ArrayList<>();
        for (BotDocumentCollection relation : relations) {
            DocumentCollection knowledge = relation.getKnowledge();
            if (knowledge == null || !PublishStatus.from(knowledge.getPublishStatus()).isSelectableForBot()) {
                throw new BusinessException("聊天助手绑定的知识库未发布，无法发布聊天助手");
            }
            Map<String, Object> item = new LinkedHashMap<>();
            item.put("knowledgeId", relation.getDocumentCollectionId());
            item.put("knowledgeName", knowledge.getTitle());
            item.put("retrievalMode", relation.getRetrievalMode() == null ? null : relation.getRetrievalMode().name());
            result.add(item);
        }
        return result;
    }

    private List<Map<String, Object>> buildPluginBindings(BigInteger botId) {
        QueryWrapper queryWrapper = QueryWrapper.create().eq(BotPlugin::getBotId, botId);
        List<BotPlugin> relations = botPluginService.list(queryWrapper);
        relations.sort(Comparator.comparing(BotPlugin::getPluginItemId, Comparator.nullsLast(BigInteger::compareTo)));
        List<Map<String, Object>> result = new ArrayList<>();
        for (BotPlugin relation : relations) {
            PluginItem pluginItem = pluginItemService.getById(relation.getPluginItemId());
            if (pluginItem == null) {
                throw new BusinessException("聊天助手绑定的插件工具不存在，无法发布聊天助手");
            }
            Map<String, Object> item = new LinkedHashMap<>();
            item.put("pluginItemId", relation.getPluginItemId());
            item.put("pluginItemName", pluginItem.getName());
            result.add(item);
        }
        return result;
    }

    private List<Map<String, Object>> buildMcpBindings(BigInteger botId) {
        QueryWrapper queryWrapper = QueryWrapper.create().eq(BotMcp::getBotId, botId);
        List<BotMcp> relations = botMcpService.list(queryWrapper);
        relations.sort(Comparator.comparing(BotMcp::getMcpId, Comparator.nullsLast(BigInteger::compareTo)));
        List<Map<String, Object>> result = new ArrayList<>();
        for (BotMcp relation : relations) {
            Mcp mcp = mcpService.getById(relation.getMcpId());
            if (mcp == null) {
                throw new BusinessException("聊天助手绑定的 MCP 不存在，无法发布聊天助手");
            }
            Map<String, Object> item = new LinkedHashMap<>();
            item.put("mcpId", relation.getMcpId());
            item.put("mcpName", mcp.getTitle());
            item.put("mcpToolName", relation.getMcpToolName());
            result.add(item);
        }
        return result;
    }

    private void removeBotRelations(BigInteger botId) {
        botDocumentCollectionService.remove(QueryWrapper.create().eq(BotDocumentCollection::getBotId, botId));
        botWorkflowService.remove(QueryWrapper.create().eq(BotWorkflow::getBotId, botId));
        botPluginService.remove(QueryWrapper.create().eq(BotPlugin::getBotId, botId));
        botMcpService.remove(QueryWrapper.create().eq(BotMcp::getBotId, botId));
    }

    private BotCategory resolveCategory(BigInteger categoryId) {
        if (categoryId == null) {
            return null;
        }
        return botCategoryService.getById(categoryId);
    }

    private SysDept resolveDept(BigInteger deptId) {
        if (deptId == null) {
            return null;
        }
        return sysDeptService.getById(deptId);
    }

    private Model resolveModel(BigInteger modelId) {
        if (modelId == null) {
            throw new BusinessException("聊天助手未配置模型，无法提交审批");
        }
        Model model = modelService.getById(modelId);
        if (model == null) {
            throw new BusinessException("聊天助手关联模型不存在，无法提交审批");
        }
        return model;
    }

    private String resolveModelName(Model model) {
        String providerName = model.getModelProvider() == null ? null : model.getModelProvider().getProviderName();
        if (providerName == null || providerName.isBlank()) {
            return model.getModelName();
        }
        return providerName + " / " + model.getModelName();
    }

    private String resolveSystemPrompt(Bot bot) {
        if (bot.getModelOptions() == null) {
            return null;
        }
        Object value = bot.getModelOptions().get("systemPrompt");
        return value == null ? null : String.valueOf(value);
    }

    private Number readNumberOption(Map<String, Object> options, String key) {
        if (options == null || !options.containsKey(key)) {
            return null;
        }
        Object value = options.get(key);
        if (value instanceof Number number) {
            return number;
        }
        if (value instanceof String string && !string.isBlank()) {
            try {
                return Double.valueOf(string);
            } catch (NumberFormatException ignore) {
                return null;
            }
        }
        return null;
    }
}
