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.BotDocumentCollection;
import tech.easyflow.ai.entity.DocumentCollection;
import tech.easyflow.ai.entity.DocumentCollectionCategory;
import tech.easyflow.ai.entity.Model;
import tech.easyflow.ai.enums.PublishStatus;
import tech.easyflow.ai.service.BotDocumentCollectionService;
import tech.easyflow.ai.service.DocumentCollectionCategoryService;
import tech.easyflow.ai.service.DocumentCollectionService;
import tech.easyflow.ai.service.ModelService;
import tech.easyflow.ai.service.ResourceOfflineImpactService;
import tech.easyflow.ai.vo.OfflineImpactCheckVo;
import tech.easyflow.approval.service.ApprovalInstanceService;
import tech.easyflow.approval.enums.ApprovalResourceType;
import tech.easyflow.common.web.exceptions.BusinessException;
import tech.easyflow.system.entity.SysDept;
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 tech.easyflow.system.service.SysDeptService;

import java.math.BigInteger;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 知识库生命周期处理器。
 */
@Component
public class KnowledgeApprovalSubjectHandler extends AbstractAiResourceLifecycleHandler<DocumentCollection> {

    private final DocumentCollectionService documentCollectionService;
    private final ResourceAccessService resourceAccessService;
    private final BotDocumentCollectionService botDocumentCollectionService;
    private final ModelService modelService;
    private final DocumentCollectionCategoryService documentCollectionCategoryService;
    private final SysDeptService sysDeptService;
    private final ResourceOfflineImpactService resourceOfflineImpactService;

    public KnowledgeApprovalSubjectHandler(DocumentCollectionService documentCollectionService,
                                           ResourceAccessService resourceAccessService,
                                           ApprovalInstanceService approvalInstanceService,
                                           BotDocumentCollectionService botDocumentCollectionService,
                                           ModelService modelService,
                                           DocumentCollectionCategoryService documentCollectionCategoryService,
                                           SysDeptService sysDeptService,
                                           ResourceOfflineImpactService resourceOfflineImpactService,
                                           ObjectMapper objectMapper) {
        super(approvalInstanceService, objectMapper);
        this.documentCollectionService = documentCollectionService;
        this.resourceAccessService = resourceAccessService;
        this.botDocumentCollectionService = botDocumentCollectionService;
        this.modelService = modelService;
        this.documentCollectionCategoryService = documentCollectionCategoryService;
        this.sysDeptService = sysDeptService;
        this.resourceOfflineImpactService = resourceOfflineImpactService;
    }

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

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

    @Override
    protected DocumentCollection requireResource(BigInteger resourceId) {
        DocumentCollection knowledge = documentCollectionService.getById(resourceId);
        if (knowledge == null) {
            throw new BusinessException("知识库不存在");
        }
        return knowledge;
    }

    @Override
    protected void assertManagePermission(DocumentCollection resource) {
        resourceAccessService.assertAccess(CategoryResourceType.KNOWLEDGE, resource, ResourceAction.MANAGE, "无权限管理知识库");
    }

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

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

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

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

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

    @Override
    protected Map<String, Object> buildResourceSnapshot(DocumentCollection collection) {
        Model vectorModel = resolveModel(collection.getVectorEmbedModelId(), "知识库向量模型不存在，无法提交审批");
        Model rerankModel = resolveOptionalModel(collection.getRerankModelId(), "知识库重排模型不存在，无法提交审批");
        DocumentCollectionCategory category = resolveCategory(collection.getCategoryId());
        SysDept dept = resolveDept(collection.getDeptId());
        Map<String, Object> snapshot = new LinkedHashMap<>();
        snapshot.put("id", collection.getId());
        snapshot.put("collectionType", collection.getCollectionType());
        snapshot.put("collectionTypeLabel", resolveCollectionTypeLabel(collection.getCollectionType()));
        snapshot.put("alias", collection.getAlias());
        snapshot.put("deptId", collection.getDeptId());
        snapshot.put("deptName", dept == null ? null : dept.getDeptName());
        snapshot.put("icon", collection.getIcon());
        snapshot.put("title", collection.getTitle());
        snapshot.put("description", collection.getDescription());
        snapshot.put("slug", collection.getSlug());
        snapshot.put("vectorStoreEnable", collection.getVectorStoreEnable());
        snapshot.put("vectorStoreType", collection.getVectorStoreType());
        snapshot.put("vectorEmbedModelId", collection.getVectorEmbedModelId());
        snapshot.put("vectorEmbedModelName", resolveModelName(vectorModel));
        snapshot.put("dimensionOfVectorModel", collection.getDimensionOfVectorModel());
        Map<String, Object> options = collection.getOptions() == null
                ? Collections.emptyMap()
                : new LinkedHashMap<>(collection.getOptions());
        snapshot.put("options", options);
        snapshot.put("canUpdateEmbeddingModel", collection.getOptionsByKey(DocumentCollection.KEY_CAN_UPDATE_EMBEDDING_MODEL));
        snapshot.put("rerankEnable", collection.getOptionsByKey(DocumentCollection.KEY_RERANK_ENABLE));
        snapshot.put("rerankModelId", collection.getRerankModelId());
        snapshot.put("rerankModelName", rerankModel == null ? null : resolveModelName(rerankModel));
        snapshot.put("searchEngineEnable", collection.getSearchEngineEnable());
        snapshot.put("englishName", collection.getEnglishName());
        snapshot.put("categoryId", collection.getCategoryId());
        snapshot.put("categoryName", category == null ? null : category.getCategoryName());
        snapshot.put("visibilityScope", collection.getVisibilityScope());
        snapshot.put("visibilityScopeLabel", resolveVisibilityScopeLabel(collection.getVisibilityScope()));
        return snapshot;
    }

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

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

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

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

    @Override
    protected String resourceLabel() {
        return "知识库";
    }

    @Override
    protected void enrichOfflineSnapshot(DocumentCollection resource, Map<String, Object> snapshot) {
        OfflineImpactCheckVo impact = resourceOfflineImpactService.checkKnowledgeImpact(resource.getId());
        if (!impact.isCanProceed()) {
            throw new BusinessException(buildWorkflowUsageBlockMessage(impact));
        }
        if (impact.isHasBotBindings()) {
            snapshot.put("botBindings", impact.getBotBindings());
        }
    }

    @Override
    protected void validateDelete(DocumentCollection resource, PublishStatus currentStatus) {
        if (hasBotBinding(resource.getId())) {
            throw new BusinessException("此知识库还关联着bot，请先取消关联！");
        }
    }

    @Override
    protected void afterOffline(BigInteger resourceId) {
        resourceOfflineImpactService.unbindKnowledgeFromBots(resourceId);
    }

    private boolean hasBotBinding(BigInteger knowledgeId) {
        QueryWrapper queryWrapper = QueryWrapper.create().eq(BotDocumentCollection::getDocumentCollectionId, knowledgeId);
        return botDocumentCollectionService.exists(queryWrapper);
    }

    private String buildWorkflowUsageBlockMessage(OfflineImpactCheckVo impact) {
        String names = impact.getWorkflowUsages().stream()
                .map(item -> item.getTitle() == null ? String.valueOf(item.getId()) : item.getTitle())
                .reduce((left, right) -> left + "、" + right)
                .orElse("未知工作流");
        return "当前知识库被以下工作流使用：" + names + "，请先在工作流中调整解绑后再下线";
    }

    private DocumentCollectionCategory resolveCategory(BigInteger categoryId) {
        if (categoryId == null) {
            return null;
        }
        return documentCollectionCategoryService.getById(categoryId);
    }

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

    private Model resolveModel(BigInteger modelId, String errorMessage) {
        if (modelId == null) {
            throw new BusinessException(errorMessage);
        }
        Model model = modelService.getById(modelId);
        if (model == null) {
            throw new BusinessException(errorMessage);
        }
        return model;
    }

    private Model resolveOptionalModel(BigInteger modelId, String errorMessage) {
        if (modelId == null) {
            return null;
        }
        Model model = modelService.getById(modelId);
        if (model == null) {
            throw new BusinessException(errorMessage);
        }
        return model;
    }

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

    private String resolveCollectionTypeLabel(String collectionType) {
        if ("FAQ".equalsIgnoreCase(collectionType)) {
            return "FAQ";
        }
        return "文档";
    }

    private String resolveVisibilityScopeLabel(String visibilityScope) {
        VisibilityScope scope = VisibilityScope.from(visibilityScope);
        return switch (scope) {
            case PRIVATE -> "仅自己";
            case DEPT -> "本部门";
            case PUBLIC -> "公开";
        };
    }
}
