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.BotWorkflow;
import tech.easyflow.ai.entity.Workflow;
import tech.easyflow.ai.enums.PublishStatus;
import tech.easyflow.ai.plugin.workflow.binding.WorkflowPluginBindingService;
import tech.easyflow.ai.plugin.workflow.snapshot.WorkflowPluginSnapshotResolver;
import tech.easyflow.ai.service.BotWorkflowService;
import tech.easyflow.ai.service.ResourceOfflineImpactService;
import tech.easyflow.ai.service.WorkflowService;
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.enums.CategoryResourceType;
import tech.easyflow.system.enums.ResourceAction;
import tech.easyflow.system.service.ResourceAccessService;

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

/**
 * 工作流生命周期处理器。
 */
@Component
public class WorkflowApprovalSubjectHandler extends AbstractAiResourceLifecycleHandler<Workflow> {

    private final WorkflowService workflowService;
    private final ResourceAccessService resourceAccessService;
    private final BotWorkflowService botWorkflowService;
    private final ResourceOfflineImpactService resourceOfflineImpactService;
    private final WorkflowPluginBindingService workflowPluginBindingService;
    private final WorkflowPluginSnapshotResolver workflowPluginSnapshotResolver;

    public WorkflowApprovalSubjectHandler(WorkflowService workflowService,
                                          ResourceAccessService resourceAccessService,
                                          ApprovalInstanceService approvalInstanceService,
                                          BotWorkflowService botWorkflowService,
                                          ResourceOfflineImpactService resourceOfflineImpactService,
                                          WorkflowPluginBindingService workflowPluginBindingService,
                                          WorkflowPluginSnapshotResolver workflowPluginSnapshotResolver,
                                          ObjectMapper objectMapper) {
        super(approvalInstanceService, objectMapper);
        this.workflowService = workflowService;
        this.resourceAccessService = resourceAccessService;
        this.botWorkflowService = botWorkflowService;
        this.resourceOfflineImpactService = resourceOfflineImpactService;
        this.workflowPluginBindingService = workflowPluginBindingService;
        this.workflowPluginSnapshotResolver = workflowPluginSnapshotResolver;
    }

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

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

    @Override
    protected Workflow requireResource(BigInteger resourceId) {
        Workflow workflow = workflowService.getById(resourceId);
        if (workflow == null) {
            throw new BusinessException("工作流不存在");
        }
        return workflow;
    }

    @Override
    protected void assertManagePermission(Workflow resource) {
        resourceAccessService.assertAccess(CategoryResourceType.WORKFLOW, resource, ResourceAction.MANAGE, "无权限管理工作流");
    }

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

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

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

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

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

    @Override
    protected Map<String, Object> buildResourceSnapshot(Workflow resource) {
        Map<String, Object> snapshot = new LinkedHashMap<>();
        snapshot.put("id", resource.getId());
        snapshot.put("alias", resource.getAlias());
        snapshot.put("deptId", resource.getDeptId());
        snapshot.put("tenantId", resource.getTenantId());
        snapshot.put("title", resource.getTitle());
        snapshot.put("description", resource.getDescription());
        snapshot.put("icon", resource.getIcon());
        snapshot.put("content", resource.getContent());
        snapshot.put("englishName", resource.getEnglishName());
        snapshot.put("status", resource.getStatus());
        snapshot.put("categoryId", resource.getCategoryId());
        snapshot.put("visibilityScope", resource.getVisibilityScope());
        return snapshot;
    }

    @Override
    protected Map<String, Object> buildPublishSnapshot(Workflow resource, PublishStatus currentStatus) {
        Map<String, Object> snapshot = super.buildPublishSnapshot(resource, currentStatus);
        OfflineImpactCheckVo impact = resourceOfflineImpactService.checkWorkflowImpact(resource.getId());
        if (impact.isHasPluginBindings()) {
            workflowPluginSnapshotResolver.assertSupportedForWorkflowPlugin(resource);
        }
        return snapshot;
    }

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

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

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

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

    @Override
    protected String resourceLabel() {
        return "工作流";
    }

    @Override
    protected void enrichOfflineSnapshot(Workflow resource, Map<String, Object> snapshot) {
        OfflineImpactCheckVo impact = resourceOfflineImpactService.checkWorkflowImpact(resource.getId());
        if (impact.isHasBotBindings()) {
            snapshot.put("botBindings", impact.getBotBindings());
        }
        if (impact.isHasPluginBindings()) {
            snapshot.put("pluginBindings", impact.getPluginBindings());
        }
    }

    @Override
    protected void validateDelete(Workflow resource, PublishStatus currentStatus) {
        if (hasBotBinding(resource.getId())) {
            throw new BusinessException("此工作流还关联有bot，请先取消关联后再删除！");
        }
    }

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

    private boolean hasBotBinding(BigInteger workflowId) {
        QueryWrapper queryWrapper = QueryWrapper.create().eq(BotWorkflow::getWorkflowId, workflowId);
        return botWorkflowService.exists(queryWrapper);
    }
}
