package tech.easyflow.job.service;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson2.JSONObject;
import com.easyagents.flow.core.chain.runtime.ChainExecutor;
import com.mybatisflex.core.tenant.TenantManager;
import org.springframework.stereotype.Service;
import tech.easyflow.ai.service.WorkflowUsageAuthorizationService;
import tech.easyflow.common.constant.Constants;
import tech.easyflow.common.constant.enums.EnumDataStatus;
import tech.easyflow.common.constant.enums.EnumJobStatus;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.job.entity.SysJob;
import tech.easyflow.job.job.JobConstant;
import tech.easyflow.job.support.SysJobWorkflowReferenceSupport;
import tech.easyflow.system.entity.SysAccount;
import tech.easyflow.system.service.SysAccountService;

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

/**
 * 工作流定时任务执行服务。
 *
 * <p>每次触发都重新加载任务、账号和工作流，并按服务端记录恢复执行主体及重新授权。</p>
 */
@Service
public class WorkflowJobExecutionService {

    /** 定时任务服务。 */
    private final SysJobService sysJobService;

    /** 系统账号服务。 */
    private final SysAccountService sysAccountService;

    /** 工作流使用权限校验服务。 */
    private final WorkflowUsageAuthorizationService workflowUsageAuthorizationService;

    /** 工作流执行器。 */
    private final ChainExecutor chainExecutor;

    /**
     * 创建工作流定时任务执行服务。
     *
     * @param sysJobService 定时任务服务
     * @param sysAccountService 系统账号服务
     * @param workflowUsageAuthorizationService 工作流使用权限校验服务
     * @param chainExecutor 工作流执行器
     */
    public WorkflowJobExecutionService(
            SysJobService sysJobService,
            SysAccountService sysAccountService,
            WorkflowUsageAuthorizationService workflowUsageAuthorizationService,
            ChainExecutor chainExecutor) {
        this.sysJobService = sysJobService;
        this.sysAccountService = sysAccountService;
        this.workflowUsageAuthorizationService = workflowUsageAuthorizationService;
        this.chainExecutor = chainExecutor;
    }

    /**
     * 使用当前数据库状态执行工作流定时任务。
     *
     * @param scheduledJob Quartz 中保存的任务快照
     * @return 工作流执行结果
     * @throws IllegalStateException 任务、账号或租户状态非法时抛出
     */
    public Object execute(SysJob scheduledJob) {
        if (scheduledJob == null || scheduledJob.getId() == null) {
            throw new IllegalStateException("定时任务不存在或缺少ID");
        }
        return TenantManager.withoutTenantCondition(
                () -> executeWithoutTenantCondition(
                        scheduledJob.getId(),
                        scheduledJob.getTenantId()));
    }

    /**
     * 在已关闭 ORM 租户条件的作用域中执行任务，并显式完成租户边界校验。
     *
     * @param jobId 定时任务 ID
     * @param scheduledTenantId Quartz 任务快照中的租户 ID
     * @return 工作流执行结果
     * @throws IllegalStateException 任务、账号或租户状态非法时抛出
     */
    private Object executeWithoutTenantCondition(
            BigInteger jobId,
            BigInteger scheduledTenantId) {
        SysJob job = sysJobService.getById(jobId);
        if (job == null) {
            throw new IllegalStateException("定时任务不存在或已删除，id=" + jobId);
        }
        if (scheduledTenantId == null
                || job.getTenantId() == null
                || !Objects.equals(scheduledTenantId, job.getTenantId())) {
            throw new IllegalStateException("定时任务租户信息不一致，id=" + jobId);
        }
        if (!Integer.valueOf(EnumJobStatus.RUNNING.getCode()).equals(job.getStatus())) {
            throw new IllegalStateException("定时任务未处于运行状态，id=" + jobId);
        }
        if (!SysJobWorkflowReferenceSupport.isWorkflowJob(job)) {
            throw new IllegalStateException("定时任务类型已变更，id=" + jobId);
        }

        SysAccount account = requireAvailableOwner(job);
        LoginAccount loginAccount = new LoginAccount();
        BeanUtil.copyProperties(account, loginAccount);
        BigInteger workflowId = SysJobWorkflowReferenceSupport.requireWorkflowId(job);
        workflowUsageAuthorizationService.requireUsableWorkflow(
                workflowId,
                loginAccount,
                "定时任务关联的工作流不存在、已禁用或无权运行");

        JSONObject workflowParams = resolveWorkflowParams(job.getJobParams());
        workflowParams.put(Constants.LOGIN_USER_KEY, loginAccount);
        return chainExecutor.execute(workflowId.toString(), workflowParams);
    }

    /**
     * 获取任务创建账号并校验账号仍可用于执行任务。
     *
     * @param job 当前数据库中的定时任务
     * @return 可用的任务创建账号
     * @throws IllegalStateException 创建账号缺失、禁用或跨租户时抛出
     */
    private SysAccount requireAvailableOwner(SysJob job) {
        BigInteger accountId = job.getCreatedBy();
        if (accountId == null) {
            throw new IllegalStateException("定时任务缺少服务端归属账号，id=" + job.getId());
        }
        SysAccount account = sysAccountService.getById(accountId);
        if (account == null) {
            throw new IllegalStateException("定时任务归属账号不存在，id=" + accountId);
        }
        if (!EnumDataStatus.AVAILABLE.getCode().equals(account.getStatus())) {
            throw new IllegalStateException("定时任务归属账号未启用，id=" + accountId);
        }
        if (!Objects.equals(job.getTenantId(), account.getTenantId())) {
            throw new IllegalStateException("定时任务与归属账号租户不一致，id=" + job.getId());
        }
        return account;
    }

    /**
     * 解析工作流运行参数并返回可写对象。
     *
     * @param jobParams 定时任务参数
     * @return 工作流运行参数
     */
    private JSONObject resolveWorkflowParams(Map<String, Object> jobParams) {
        if (jobParams == null) {
            return new JSONObject();
        }
        JSONObject params = new JSONObject(jobParams)
                .getJSONObject(JobConstant.WORKFLOW_PARAMS_KEY);
        return params == null ? new JSONObject() : new JSONObject(params);
    }
}
