package tech.easyflow.admin.controller.job;

import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.date.DateUtil;
import com.easyagents.flow.core.chain.Parameter;
import com.mybatisflex.core.query.QueryWrapper;
import org.quartz.CronExpression;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import tech.easyflow.ai.easyagentsflow.service.WorkflowRunningParameterResolver;
import tech.easyflow.ai.entity.Workflow;
import tech.easyflow.ai.service.WorkflowService;
import tech.easyflow.ai.service.WorkflowUsageAuthorizationService;
import tech.easyflow.admin.model.SysJobWorkflowOptionView;
import tech.easyflow.common.constant.enums.EnumDataStatus;
import tech.easyflow.common.constant.enums.EnumJobType;
import tech.easyflow.common.domain.Result;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.satoken.util.SaTokenUtil;
import tech.easyflow.common.web.controller.BaseCurdController;
import tech.easyflow.common.web.exceptions.BusinessException;
import tech.easyflow.job.entity.SysJob;
import tech.easyflow.job.job.JobConstant;
import tech.easyflow.job.service.SysJobService;
import tech.easyflow.job.support.SysJobWorkflowReferenceSupport;
import tech.easyflow.log.annotation.LogRecord;
import tech.easyflow.system.enums.CategoryResourceType;
import tech.easyflow.system.enums.ResourceAction;
import tech.easyflow.system.service.ResourceAccessService;

import java.io.Serializable;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 系统任务表 控制层。
 *
 * @author xiaoma
 * @since 2025-05-20
 */
@RestController
@RequestMapping("/api/v1/sysJob")
public class SysJobController extends BaseCurdController<SysJobService, SysJob> {

    /** 工作流服务。 */
    private final WorkflowService workflowService;

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

    /** 资源访问控制服务。 */
    private final ResourceAccessService resourceAccessService;

    /** 工作流运行参数解析器。 */
    private final WorkflowRunningParameterResolver workflowRunningParameterResolver;

    /**
     * 创建定时任务控制器。
     *
     * @param service 定时任务服务
     * @param workflowService 工作流服务
     * @param workflowUsageAuthorizationService 工作流使用权限校验服务
     * @param resourceAccessService 资源访问控制服务
     * @param workflowRunningParameterResolver 工作流运行参数解析器
     */
    public SysJobController(SysJobService service,
                            WorkflowService workflowService,
                            WorkflowUsageAuthorizationService workflowUsageAuthorizationService,
                            ResourceAccessService resourceAccessService,
                            WorkflowRunningParameterResolver workflowRunningParameterResolver) {
        super(service);
        this.workflowService = workflowService;
        this.workflowUsageAuthorizationService = workflowUsageAuthorizationService;
        this.resourceAccessService = resourceAccessService;
        this.workflowRunningParameterResolver = workflowRunningParameterResolver;
    }

    /**
     * 获取定时任务关键字搜索字段。
     *
     * @return 任务名称和备注属性
     */
    @Override
    protected String[] getKeywordSearchProperties() {
        return new String[]{"jobName", "remark"};
    }

    @GetMapping("/start")
    @SaCheckPermission("/api/v1/sysJob/save")
    @LogRecord("启动定时任务")
    public Result<Void> start(BigInteger id) {
        LoginAccount account = SaTokenUtil.getLoginAccount();
        SysJob job = requireExistingJob(id);
        validateWorkflowReference(job, account);
        service.startJob(id);
        return Result.ok();
    }

    @GetMapping("/stop")
    @SaCheckPermission("/api/v1/sysJob/save")
    @LogRecord("停止定时任务")
    public Result<Void> stop(BigInteger id) {
        service.stopJob(id);
        return Result.ok();
    }

    @GetMapping("/getNextTimes")
    @SaCheckPermission("/api/v1/sysJob/save")
    public Result<List<String>> getNextTimes(String cronExpression) throws  Exception{
        CronExpression ex = new CronExpression(cronExpression);
        List<String> times = new ArrayList<>();
        Date date = new Date();
        for (int i = 0; i < 5; i++) {
            Date next = ex.getNextValidTimeAfter(date);
            times.add(DateUtil.formatDateTime(next));
            date = next;
        }
        return Result.ok(times);
    }

    /**
     * 查询当前账号可用于定时任务的工作流。
     *
     * @return 工作流安全选项
     */
    @GetMapping("/workflowOptions")
    @SaCheckPermission("/api/v1/sysJob/save")
    public Result<List<SysJobWorkflowOptionView>> workflowOptions() {
        LoginAccount account = SaTokenUtil.getLoginAccount();
        List<SysJobWorkflowOptionView> options = workflowService.list(QueryWrapper.create()
                .eq(Workflow::getTenantId, account.getTenantId())
                        .eq(Workflow::getStatus, EnumDataStatus.AVAILABLE.getCode())
                        .orderBy(Workflow::getModified, false))
                .stream()
                .filter(workflow -> Objects.equals(workflow.getTenantId(), account.getTenantId()))
                .filter(workflow -> resourceAccessService.canAccess(
                        account,
                        CategoryResourceType.WORKFLOW,
                        workflow,
                        ResourceAction.USE))
                .map(workflow -> new SysJobWorkflowOptionView(
                        workflow.getId(),
                        workflow.getTitle(),
                        workflow.getDescription()))
                .toList();
        return Result.ok(options);
    }

    /**
     * 查询定时任务所选工作流的运行参数。
     *
     * @param id 工作流 ID
     * @return 工作流运行参数
     * @throws BusinessException 工作流不存在或无运行权限时抛出
     */
    @GetMapping("/workflowRunningParameters")
    @SaCheckPermission("/api/v1/sysJob/save")
    public Result<Map<String, Object>> workflowRunningParameters(BigInteger id) {
        Workflow workflow = workflowUsageAuthorizationService.requireUsableWorkflow(
                id,
                SaTokenUtil.getLoginAccount(),
                "工作流不存在、已禁用或无权运行");
        Map<String, Object> result = workflowRunningParameterResolver.buildRunningParametersView(workflow);
        if (result == null) {
            throw new BusinessException("工作流参数配置无效，请检查工作流后重试");
        }
        return Result.ok(result);
    }

    @Override
    protected Result onSaveOrUpdateBefore(SysJob entity, boolean isSave) {
        if (entity == null) {
            throw new BusinessException("定时任务不能为空");
        }
        LoginAccount loginUser = SaTokenUtil.getLoginAccount();
        SysJob effectiveEntity = entity;
        if (isSave) {
            commonFiled(entity,loginUser.getId(),loginUser.getTenantId(), loginUser.getDeptId());
        } else {
            SysJob existing = requireExistingJob(entity.getId());
            preserveServerControlledFields(entity, existing);
            effectiveEntity = mergeForValidation(entity, existing);
            entity.setModified(new Date());
            entity.setModifiedBy(loginUser.getId());
        }
        validateWorkflowReference(effectiveEntity, loginUser);
        return super.onSaveOrUpdateBefore(entity, isSave);
    }

    /**
     * 校验工作流类型任务引用的工作流可被当前用户运行。
     *
     * @param entity 待保存的定时任务
     * @param account 当前账号
     * @throws BusinessException 工作流不存在、参数非法或无运行权限时抛出
     */
    private void validateWorkflowReference(SysJob entity, LoginAccount account) {
        if (entity == null
                || !Integer.valueOf(EnumJobType.TINY_FLOW.getCode()).equals(entity.getJobType())) {
            return;
        }
        BigInteger workflowId = SysJobWorkflowReferenceSupport.requireWorkflowId(entity);
        Workflow workflow = workflowUsageAuthorizationService.requireUsableWorkflow(
                workflowId,
                account,
                "工作流不存在、已禁用或无权运行");
        validateRequiredWorkflowParams(entity, workflow);
    }

    /**
     * 获取当前租户内存在的定时任务。
     *
     * @param id 定时任务 ID
     * @return 已存在的定时任务
     * @throws BusinessException ID 缺失或任务不存在时抛出
     */
    private SysJob requireExistingJob(BigInteger id) {
        if (id == null) {
            throw new BusinessException("定时任务ID不能为空");
        }
        SysJob existing = service.getById(id);
        if (existing == null) {
            throw new BusinessException("定时任务不存在");
        }
        return existing;
    }

    /**
     * 保留更新请求不能修改的服务端控制字段。
     *
     * @param entity 更新请求
     * @param existing 数据库中的定时任务
     */
    private void preserveServerControlledFields(SysJob entity, SysJob existing) {
        entity.setTenantId(existing.getTenantId());
        entity.setDeptId(existing.getDeptId());
        entity.setCreated(existing.getCreated());
        entity.setCreatedBy(existing.getCreatedBy());
    }

    /**
     * 合并部分更新请求与原记录，生成用于权限和参数校验的有效任务状态。
     *
     * @param entity 更新请求
     * @param existing 数据库中的定时任务
     * @return 合并后的校验对象
     */
    private SysJob mergeForValidation(SysJob entity, SysJob existing) {
        SysJob effective = new SysJob();
        effective.setJobType(entity.getJobType() == null
                ? existing.getJobType()
                : entity.getJobType());
        effective.setJobParams(entity.getJobParams() == null
                ? existing.getJobParams()
                : entity.getJobParams());
        return effective;
    }

    /**
     * 校验定时任务已填写工作流的全部必填运行参数。
     *
     * @param entity 待保存定时任务
     * @param workflow 关联工作流
     * @throws BusinessException 工作流参数配置无效或必填值缺失时抛出
     */
    private void validateRequiredWorkflowParams(SysJob entity, Workflow workflow) {
        List<Parameter> parameters =
                workflowRunningParameterResolver.resolveStartParameters(workflow.getContent());
        if (parameters == null) {
            throw new BusinessException("工作流参数配置无效，请检查工作流后重试");
        }
        Map<String, Object> jobParams = entity.getJobParams();
        Object rawWorkflowParams = jobParams == null
                ? null
                : jobParams.get(JobConstant.WORKFLOW_PARAMS_KEY);
        Map<?, ?> workflowParams = rawWorkflowParams instanceof Map<?, ?> map
                ? map
                : Map.of();
        for (Parameter parameter : parameters) {
            if (parameter == null || !parameter.isRequired()) {
                continue;
            }
            String name = parameter.getName();
            if (!StringUtils.hasText(name)) {
                throw new BusinessException("工作流存在无效必填参数配置，请检查工作流后重试");
            }
            if (!hasRequiredValue(workflowParams.get(name))) {
                String label = StringUtils.hasText(parameter.getFormLabel())
                        ? parameter.getFormLabel()
                        : name;
                throw new BusinessException("工作流必填参数“" + label + "”不能为空");
            }
        }
    }

    /**
     * 判断必填参数值是否有效。
     *
     * @param value 参数值
     * @return 非空时为 true
     */
    private boolean hasRequiredValue(Object value) {
        if (value == null) {
            return false;
        }
        if (value instanceof CharSequence sequence) {
            return StringUtils.hasText(sequence);
        }
        if (value instanceof Collection<?> collection) {
            return !collection.isEmpty();
        }
        if (value instanceof Map<?, ?> map) {
            return !map.isEmpty();
        }
        return true;
    }

    @Override
    protected Result onRemoveBefore(Collection<Serializable> ids) {
        service.deleteJob(ids);
        return super.onRemoveBefore(ids);
    }
}
