package tech.easyflow.agent.service.impl;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tech.easyflow.agent.config.AgentInteractionConfigSupport;
import tech.easyflow.agent.entity.Agent;
import tech.easyflow.agent.entity.AgentKnowledgeBinding;
import tech.easyflow.agent.entity.AgentToolBinding;
import tech.easyflow.agent.mapper.AgentMapper;
import tech.easyflow.agent.service.AgentKnowledgeBindingService;
import tech.easyflow.agent.service.AgentService;
import tech.easyflow.agent.service.AgentToolBindingService;
import tech.easyflow.ai.entity.*;
import tech.easyflow.ai.enums.PublishStatus;
import tech.easyflow.ai.service.*;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.satoken.util.SaTokenUtil;
import tech.easyflow.common.web.exceptions.BusinessException;
import tech.easyflow.system.enums.CategoryResourceType;
import tech.easyflow.system.enums.ResourceAction;
import tech.easyflow.system.enums.VisibilityScope;
import tech.easyflow.system.service.ResourceAccessService;

import javax.annotation.Resource;
import java.math.BigInteger;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * Agent 配置服务实现。
 */
@Service
public class AgentServiceImpl extends ServiceImpl<AgentMapper, Agent> implements AgentService {

    private static final TypeReference<List<AgentToolBinding>> TOOL_BINDING_LIST_TYPE = new TypeReference<>() {};
    private static final TypeReference<List<AgentKnowledgeBinding>> KNOWLEDGE_BINDING_LIST_TYPE = new TypeReference<>() {};

    @Resource
    private AgentToolBindingService agentToolBindingService;
    @Resource
    private AgentKnowledgeBindingService agentKnowledgeBindingService;
    @Resource
    private ModelService modelService;
    @Resource
    private WorkflowService workflowService;
    @Resource
    private PluginItemService pluginItemService;
    @Resource
    private McpService mcpService;
    @Resource
    private DocumentCollectionService documentCollectionService;
    @Resource
    private ResourceAccessService resourceAccessService;
    @Resource
    private ObjectMapper objectMapper;

    /**
     * {@inheritDoc}
     */
    @Override
    public Agent getDetail(BigInteger id) {
        Agent agent = requireAgent(id);
        resourceAccessService.assertAccess(CategoryResourceType.AGENT, agent, ResourceAction.READ, "无权限查看该 Agent");
        agent.setToolBindings(agentToolBindingService.listEnabled(id));
        agent.setKnowledgeBindings(agentKnowledgeBindingService.listEnabled(id));
        return agent;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Agent saveDraft(Agent agent) {
        validateDraft(agent);
        applyDraftDefaults(agent);
        save(agent);
        return getDetail(agent.getId());
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Agent updateDraft(Agent agent) {
        if (agent == null || agent.getId() == null) {
            throw new BusinessException("Agent ID 不能为空");
        }
        Agent existing = requireAgent(agent.getId());
        resourceAccessService.assertAccess(CategoryResourceType.AGENT, existing, ResourceAction.MANAGE, "无权限管理该 Agent");
        validateDraft(agent);
        applyDraftUpdate(existing, agent);
        updateById(existing);
        return getDetail(existing.getId());
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Agent getPublishedView(BigInteger id) {
        Agent agent = requireAgent(id);
        PublishStatus status = PublishStatus.from(agent.getPublishStatus());
        if (!status.isExternallyVisible() || agent.getPublishedSnapshotJson() == null || agent.getPublishedSnapshotJson().isEmpty()) {
            throw new BusinessException("Agent 未发布");
        }
        return fromSnapshot(agent.getPublishedSnapshotJson());
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Map<String, Object> buildPublishSnapshot(Agent agent) {
        Agent detail = getDetail(agent.getId());
        Map<String, Object> snapshot = new LinkedHashMap<>();
        snapshot.put("id", detail.getId());
        snapshot.put("tenantId", detail.getTenantId());
        snapshot.put("deptId", detail.getDeptId());
        snapshot.put("createdBy", detail.getCreatedBy());
        snapshot.put("name", detail.getName());
        snapshot.put("description", detail.getDescription());
        snapshot.put("avatar", detail.getAvatar());
        snapshot.put("categoryId", detail.getCategoryId());
        snapshot.put("modelId", detail.getModelId());
        snapshot.put("modelConfigJson", detail.getModelConfigJson());
        snapshot.put("generationConfigJson", detail.getGenerationConfigJson());
        snapshot.put("promptConfigJson", detail.getPromptConfigJson());
        snapshot.put("memoryConfigJson", detail.getMemoryConfigJson());
        snapshot.put("executionConfigJson", detail.getExecutionConfigJson());
        snapshot.put("interactionConfigJson", detail.getInteractionConfigJson());
        snapshot.put("visibilityScope", detail.getVisibilityScope());
        snapshot.put("toolBindings", snapshotToolBindings(detail.getToolBindings()));
        snapshot.put("knowledgeBindings", snapshotKnowledgeBindings(detail.getKnowledgeBindings()));
        snapshot.put("basicSummary", basicSummary(detail));
        snapshot.put("modelSummary", modelSummary(detail.getModelId()));
        snapshot.put("parameterSummary", parameterSummary(detail));
        snapshot.put("promptSummary", promptSummary(detail));
        snapshot.put("toolSummaries", toolSummaries(detail.getToolBindings()));
        snapshot.put("knowledgeSummaries", knowledgeSummaries(detail.getKnowledgeBindings()));
        snapshot.put("snapshotAt", new Date());
        return snapshot;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Agent fromSnapshot(Map<String, Object> snapshot) {
        if (snapshot == null || snapshot.isEmpty()) {
            throw new BusinessException("Agent 发布快照为空");
        }
        Agent agent = objectMapper.convertValue(snapshot, Agent.class);
        agent.setId(toBigInteger(snapshot.get("id")));
        agent.setTenantId(toBigInteger(snapshot.get("tenantId")));
        agent.setDeptId(toBigInteger(snapshot.get("deptId")));
        agent.setCreatedBy(toBigInteger(snapshot.get("createdBy")));
        agent.setModelId(toBigInteger(snapshot.get("modelId")));
        agent.setCategoryId(toBigInteger(snapshot.get("categoryId")));
        agent.setPublishStatus(PublishStatus.PUBLISHED.getCode());
        agent.setInteractionConfigJson(AgentInteractionConfigSupport.normalize(agent.getInteractionConfigJson()));
        agent.setPublishedSnapshotJson(snapshot);
        agent.setToolBindings(objectMapper.convertValue(snapshot.get("toolBindings"), TOOL_BINDING_LIST_TYPE));
        agent.setKnowledgeBindings(objectMapper.convertValue(snapshot.get("knowledgeBindings"), KNOWLEDGE_BINDING_LIST_TYPE));
        return agent;
    }

    private Agent requireAgent(BigInteger id) {
        Agent agent = getById(id);
        if (agent == null) {
            throw new BusinessException("Agent 不存在");
        }
        return agent;
    }

    private void validateDraft(Agent agent) {
        if (agent == null) {
            throw new BusinessException("Agent 不能为空");
        }
        if (agent.getName() == null || agent.getName().isBlank()) {
            throw new BusinessException("Agent 名称不能为空");
        }
        if (agent.getModelId() == null) {
            throw new BusinessException("Agent 模型不能为空");
        }
        Model model = modelService.getModelInstance(agent.getModelId());
        if (model == null) {
            throw new BusinessException("Agent 模型不存在");
        }
        agent.setVisibilityScope(VisibilityScope.fromOrDefault(agent.getVisibilityScope(), VisibilityScope.PRIVATE).name());
        agent.setInteractionConfigJson(AgentInteractionConfigSupport.normalize(agent.getInteractionConfigJson()));
    }

    private void applyDraftDefaults(Agent agent) {
        LoginAccount account = requireCurrentLoginAccount();
        Date now = new Date();
        agent.setTenantId(account.getTenantId());
        agent.setDeptId(account.getDeptId());
        agent.setCreated(now);
        agent.setCreatedBy(account.getId());
        agent.setModified(now);
        agent.setModifiedBy(account.getId());
        agent.setStatus(agent.getStatus() == null ? 1 : agent.getStatus());
        agent.setPublishStatus(PublishStatus.DRAFT.getCode());
    }

    private Map<String, Object> basicSummary(Agent agent) {
        Map<String, Object> summary = new LinkedHashMap<>();
        summary.put("id", agent.getId());
        summary.put("name", agent.getName());
        summary.put("description", agent.getDescription());
        summary.put("avatar", agent.getAvatar());
        summary.put("categoryId", agent.getCategoryId());
        summary.put("status", agent.getStatus());
        summary.put("publishStatus", PublishStatus.PUBLISHED.getCode());
        summary.put("visibilityScope", agent.getVisibilityScope());
        return summary;
    }

    private void applyDraftUpdate(Agent existing, Agent incoming) {
        LoginAccount account = requireCurrentLoginAccount();
        existing.setName(incoming.getName());
        existing.setDescription(incoming.getDescription());
        existing.setAvatar(incoming.getAvatar());
        existing.setCategoryId(incoming.getCategoryId());
        existing.setModelId(incoming.getModelId());
        existing.setModelConfigJson(incoming.getModelConfigJson());
        existing.setGenerationConfigJson(incoming.getGenerationConfigJson());
        existing.setPromptConfigJson(incoming.getPromptConfigJson());
        existing.setMemoryConfigJson(incoming.getMemoryConfigJson());
        existing.setExecutionConfigJson(incoming.getExecutionConfigJson());
        existing.setInteractionConfigJson(incoming.getInteractionConfigJson());
        existing.setStatus(incoming.getStatus() == null ? 1 : incoming.getStatus());
        existing.setVisibilityScope(incoming.getVisibilityScope());
        existing.setModified(new Date());
        existing.setModifiedBy(account.getId());
    }

    private Map<String, Object> modelSummary(BigInteger modelId) {
        Model model = modelService.getModelInstance(modelId);
        Map<String, Object> summary = new LinkedHashMap<>();
        summary.put("id", model.getId());
        summary.put("title", model.getTitle());
        summary.put("modelName", model.getModelName());
        summary.put("providerType", model.getModelProvider() == null ? null : model.getModelProvider().getProviderType());
        summary.put("supportImage", Boolean.TRUE.equals(model.getSupportImage()));
        summary.put("supportImageB64Only", Boolean.TRUE.equals(model.getSupportImageB64Only()));
        return summary;
    }

    private Map<String, Object> parameterSummary(Agent agent) {
        Map<String, Object> summary = new LinkedHashMap<>();
        summary.put("generationConfigJson", agent.getGenerationConfigJson());
        summary.put("modelConfigJson", agent.getModelConfigJson());
        summary.put("memoryConfigJson", agent.getMemoryConfigJson());
        summary.put("executionConfigJson", agent.getExecutionConfigJson());
        return summary;
    }

    private Map<String, Object> promptSummary(Agent agent) {
        Map<String, Object> summary = new LinkedHashMap<>();
        summary.put("promptConfigJson", agent.getPromptConfigJson());
        summary.put("systemPrompt", agent.getPromptConfigJson() == null ? null : agent.getPromptConfigJson().get("systemPrompt"));
        summary.put("prompt", agent.getPromptConfigJson() == null ? null : agent.getPromptConfigJson().get("prompt"));
        return summary;
    }

    private List<Map<String, Object>> toolSummaries(List<AgentToolBinding> bindings) {
        if (bindings == null) {
            return List.of();
        }
        return bindings.stream().map(this::toolSummary).toList();
    }

    private Map<String, Object> toolSummary(AgentToolBinding binding) {
        Map<String, Object> summary = new LinkedHashMap<>();
        summary.put("bindingId", binding.getId());
        summary.put("toolType", binding.getToolType());
        summary.put("targetId", binding.getTargetId());
        summary.put("enabled", Boolean.TRUE.equals(binding.getEnabled()));
        summary.put("hitlEnabled", Boolean.TRUE.equals(binding.getHitlEnabled()));
        summary.put("hitlConfigJson", binding.getHitlConfigJson());
        summary.put("sortNo", binding.getSortNo());
        if ("WORKFLOW".equalsIgnoreCase(binding.getToolType())) {
            summary.put("toolName", binding.getToolName());
            Workflow workflow = workflowService.getById(binding.getTargetId());
            summary.put("title", workflow == null ? null : workflow.getTitle());
        } else if ("PLUGIN".equalsIgnoreCase(binding.getToolType())) {
            summary.put("toolName", binding.getToolName());
            PluginItem pluginItem = pluginItemService.getById(binding.getTargetId());
            summary.put("title", pluginItem == null ? null : pluginItem.getName());
        } else {
            Mcp mcp = mcpService.getById(binding.getTargetId());
            summary.put("title", mcp == null ? null : mcp.getTitle());
            summary.put("tools", mcp == null || mcp.getTools() == null ? List.of() : mcp.getTools());
        }
        return summary;
    }

    private List<AgentToolBinding> snapshotToolBindings(List<AgentToolBinding> bindings) {
        if (bindings == null) {
            return List.of();
        }
        return bindings.stream().map(binding -> {
            AgentToolBinding snapshot = objectMapper.convertValue(binding, AgentToolBinding.class);
            snapshot.setResourceSummary(toolSummary(binding));
            snapshot.setResourceSnapshot(toolResourceSnapshot(binding));
            return snapshot;
        }).toList();
    }

    private Map<String, Object> toolResourceSnapshot(AgentToolBinding binding) {
        if ("WORKFLOW".equalsIgnoreCase(binding.getToolType())) {
            Workflow workflow = workflowService.getPublishedById(binding.getTargetId());
            if (workflow == null || !PublishStatus.from(workflow.getPublishStatus()).isExternallyVisible()) {
                throw new BusinessException("绑定工作流不存在或未发布");
            }
            return objectMapper.convertValue(workflow, new TypeReference<Map<String, Object>>() {});
        }
        if ("PLUGIN".equalsIgnoreCase(binding.getToolType())) {
            PluginItem pluginItem = pluginItemService.getById(binding.getTargetId());
            if (pluginItem == null) {
                throw new BusinessException("绑定插件不存在");
            }
            return objectMapper.convertValue(pluginItem, new TypeReference<Map<String, Object>>() {});
        }
        Mcp mcp = mcpService.getById(binding.getTargetId());
        if (mcp == null) {
            throw new BusinessException("绑定 MCP 不存在");
        }
        return objectMapper.convertValue(mcp, new TypeReference<Map<String, Object>>() {});
    }

    private List<AgentKnowledgeBinding> snapshotKnowledgeBindings(List<AgentKnowledgeBinding> bindings) {
        if (bindings == null) {
            return List.of();
        }
        return bindings.stream().map(binding -> {
            AgentKnowledgeBinding snapshot = objectMapper.convertValue(binding, AgentKnowledgeBinding.class);
            snapshot.setResourceSummary(knowledgeSummary(binding));
            snapshot.setResourceSnapshot(knowledgeResourceSnapshot(binding));
            return snapshot;
        }).toList();
    }

    private Map<String, Object> knowledgeResourceSnapshot(AgentKnowledgeBinding binding) {
        DocumentCollection knowledge = documentCollectionService.getPublishedById(binding.getKnowledgeId());
        if (knowledge == null || !PublishStatus.from(knowledge.getPublishStatus()).isExternallyVisible()) {
            throw new BusinessException("绑定知识库不存在或未发布");
        }
        return objectMapper.convertValue(knowledge, new TypeReference<Map<String, Object>>() {});
    }

    private List<Map<String, Object>> knowledgeSummaries(List<AgentKnowledgeBinding> bindings) {
        if (bindings == null) {
            return List.of();
        }
        return bindings.stream().map(this::knowledgeSummary).toList();
    }

    private Map<String, Object> knowledgeSummary(AgentKnowledgeBinding binding) {
        DocumentCollection knowledge = documentCollectionService.getById(binding.getKnowledgeId());
        Map<String, Object> summary = new LinkedHashMap<>();
        summary.put("bindingId", binding.getId());
        summary.put("knowledgeId", binding.getKnowledgeId());
        summary.put("retrievalMode", binding.getRetrievalMode());
        summary.put("enabled", Boolean.TRUE.equals(binding.getEnabled()));
        summary.put("optionsJson", binding.getOptionsJson());
        summary.put("sortNo", binding.getSortNo());
        summary.put("title", knowledge == null ? null : knowledge.getTitle());
        return summary;
    }

    private LoginAccount requireCurrentLoginAccount() {
        try {
            return SaTokenUtil.getLoginAccount();
        } catch (Exception e) {
            throw new BusinessException("当前登录状态失效，请重新登录后再试");
        }
    }

    private BigInteger toBigInteger(Object value) {
        if (value == null) {
            return null;
        }
        if (value instanceof BigInteger bigInteger) {
            return bigInteger;
        }
        if (value instanceof Number number) {
            return BigInteger.valueOf(number.longValue());
        }
        return new BigInteger(String.valueOf(value));
    }
}
