package tech.easyflow.ai.publish;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Component;
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.ResourceOfflineImpactService;
import tech.easyflow.ai.service.WorkflowService;
import tech.easyflow.ai.service.WorkflowScheduleReferenceProvider;
import tech.easyflow.ai.vo.OfflineImpactCheckVo;
import tech.easyflow.ai.vo.OfflineImpactBindingVo;
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.List;
import java.util.Map;

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

    private final WorkflowService workflowService;
    private final ResourceAccessService resourceAccessService;
    private final ResourceOfflineImpactService resourceOfflineImpactService;
    private final WorkflowPluginBindingService workflowPluginBindingService;
    private final WorkflowPluginSnapshotResolver workflowPluginSnapshotResolver;
    private final List<WorkflowScheduleReferenceProvider> workflowScheduleReferenceProviders;

    public WorkflowApprovalSubjectHandler(WorkflowService workflowService,
                                          ResourceAccessService resourceAccessService,
                                          ApprovalInstanceService approvalInstanceService,
                                          ResourceOfflineImpactService resourceOfflineImpactService,
                                          WorkflowPluginBindingService workflowPluginBindingService,
                                          WorkflowPluginSnapshotResolver workflowPluginSnapshotResolver,
                                          ObjectMapper objectMapper,
                                          List<WorkflowScheduleReferenceProvider> workflowScheduleReferenceProviders) {
        super(approvalInstanceService, objectMapper);
        this.workflowService = workflowService;
        this.resourceAccessService = resourceAccessService;
        this.resourceOfflineImpactService = resourceOfflineImpactService;
        this.workflowPluginBindingService = workflowPluginBindingService;
        this.workflowPluginSnapshotResolver = workflowPluginSnapshotResolver;
        this.workflowScheduleReferenceProviders = workflowScheduleReferenceProviders == null
                ? List.of()
                : List.copyOf(workflowScheduleReferenceProviders);
    }

    @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.isHasAgentBindings()) {
            snapshot.put("agentBindings", impact.getAgentBindings());
        }
        if (impact.isHasPluginBindings()) {
            snapshot.put("pluginBindings", impact.getPluginBindings());
        }
    }

    @Override
    protected void validateDelete(Workflow resource, PublishStatus currentStatus) {
        OfflineImpactCheckVo impact = resourceOfflineImpactService.checkWorkflowImpact(resource.getId());
        if (impact.isHasAgentBindings()) {
            throw new BusinessException("此工作流仍被智能体使用，请先取消绑定后再删除");
        }
        OfflineImpactBindingVo scheduledJob = findFirstScheduledJobReference(resource.getId());
        if (scheduledJob != null) {
            String jobName = scheduledJob.getTitle() == null ? "未命名任务" : scheduledJob.getTitle();
            throw new BusinessException("此工作流仍被定时任务“" + jobName + "”引用，请先删除或重新选择定时任务中的工作流后再删除");
        }
    }

    /**
     * 审批通过后执行真实删除前，重新校验工作流引用。
     *
     * @param resourceId 工作流 ID
     * @throws BusinessException 工作流不存在或仍被引用时抛出
     */
    @Override
    protected void beforeRemove(BigInteger resourceId) {
        Workflow workflow = requireResource(resourceId);
        validateDelete(workflow, getCurrentStatus(workflow));
    }

    /**
     * 查询第一个引用指定工作流的定时任务。
     *
     * @param workflowId 工作流 ID
     * @return 定时任务摘要；未被引用时为 null
     */
    private OfflineImpactBindingVo findFirstScheduledJobReference(BigInteger workflowId) {
        for (WorkflowScheduleReferenceProvider provider : workflowScheduleReferenceProviders) {
            List<OfflineImpactBindingVo> jobs = provider.listScheduledJobsByWorkflowId(workflowId);
            if (jobs == null || jobs.isEmpty()) {
                continue;
            }
            for (OfflineImpactBindingVo job : jobs) {
                if (job != null) {
                    return job;
                }
            }
        }
        return null;
    }

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