package cn.stylefeng.workflow.log.modular.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.stylefeng.roses.kernel.db.api.factory.PageFactory;
import cn.stylefeng.roses.kernel.db.api.factory.PageResultFactory;
import cn.stylefeng.roses.kernel.db.api.pojo.page.PageResult;
import cn.stylefeng.roses.kernel.rule.enums.YesOrNotEnum;
import cn.stylefeng.roses.kernel.rule.exception.base.ServiceException;
import cn.stylefeng.workflow.api.entity.instance.RuntimeInstance;
import cn.stylefeng.workflow.api.entity.log.FlowEngineLog;
import cn.stylefeng.workflow.api.enums.FlowLogLevelEnum;
import cn.stylefeng.workflow.api.pojo.InstanceContext;
import cn.stylefeng.workflow.log.modular.enums.FlowEngineLogExceptionEnum;
import cn.stylefeng.workflow.log.modular.mapper.FlowEngineLogMapper;
import cn.stylefeng.workflow.log.modular.pojo.request.FlowEngineLogRequest;
import cn.stylefeng.workflow.log.modular.service.FlowEngineLogService;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;

@Service
public class FlowEngineLogServiceImpl extends ServiceImpl<FlowEngineLogMapper, FlowEngineLog> implements FlowEngineLogService {

    @Override
    public void add(FlowEngineLogRequest flowEngineLogRequest) {
        FlowEngineLog flowEngineLog = new FlowEngineLog();
        BeanUtil.copyProperties(flowEngineLogRequest, flowEngineLog);
        this.save(flowEngineLog);
    }

    @Override
    public void del(FlowEngineLogRequest flowEngineLogRequest) {
        FlowEngineLog flowEngineLog = this.queryFlowEngineLog(flowEngineLogRequest);
        this.removeById(flowEngineLog.getLogId());
    }

    @Override
    public void edit(FlowEngineLogRequest flowEngineLogRequest) {
        FlowEngineLog flowEngineLog = this.queryFlowEngineLog(flowEngineLogRequest);
        BeanUtil.copyProperties(flowEngineLogRequest, flowEngineLog);
        this.updateById(flowEngineLog);
    }

    @Override
    public FlowEngineLog detail(FlowEngineLogRequest flowEngineLogRequest) {
        return this.queryFlowEngineLog(flowEngineLogRequest);
    }

    @Override
    public PageResult<FlowEngineLog> findPage(FlowEngineLogRequest flowEngineLogRequest) {
        LambdaQueryWrapper<FlowEngineLog> wrapper = createWrapper(flowEngineLogRequest);
        Page<FlowEngineLog> sysRolePage = this.page(PageFactory.defaultPage(), wrapper);
        return PageResultFactory.createPageResult(sysRolePage);
    }

    @Override
    public List<FlowEngineLog> nodeLogList(FlowEngineLogRequest flowEngineLogRequest) {
        LambdaQueryWrapper<FlowEngineLog> wrapper = this.createWrapper(flowEngineLogRequest);

        wrapper.isNotNull(FlowEngineLog::getBeginTime);

        return this.list(wrapper);
    }

    @Override
    public List<FlowEngineLog> findList(FlowEngineLogRequest flowEngineLogRequest) {
        LambdaQueryWrapper<FlowEngineLog> wrapper = this.createWrapper(flowEngineLogRequest);
        return this.list(wrapper);
    }

    @Override
    public void logRecord(InstanceContext instanceContext, Long nodeId, String message, Boolean successFlag) {
        FlowEngineLog flowEngineLog = createBaseFlowLog(instanceContext, nodeId, message, successFlag);
        this.save(flowEngineLog);
    }

    @Override
    public void logSuccess(InstanceContext instanceContext, Long nodeId, String message) {
        logRecord(instanceContext, nodeId, message, true);
    }

    @Override
    public void logFail(InstanceContext instanceContext, Long nodeId, String message) {
        logRecord(instanceContext, nodeId, message, false);
    }

    @Override
    public void createNodeLog(InstanceContext instanceContext, Long nodeId, FlowLogLevelEnum flowLogLevelEnum, Long startMillSeconds,
                              Long endMilSeconds, String message, Boolean successFlag) {

        FlowEngineLog baseFlowLog = this.createBaseFlowLog(instanceContext, nodeId, message, successFlag);

        baseFlowLog.setLogLevel(flowLogLevelEnum.getCode());

        baseFlowLog.setBeginTime(new Date(startMillSeconds));

        baseFlowLog.setEndTime(new Date(endMilSeconds));

        baseFlowLog.setSuccessFlag(successFlag ? YesOrNotEnum.Y.getCode() : YesOrNotEnum.N.getCode());

        this.save(baseFlowLog);
    }

    private FlowEngineLog queryFlowEngineLog(FlowEngineLogRequest flowEngineLogRequest) {
        FlowEngineLog flowEngineLog = this.getById(flowEngineLogRequest.getLogId());
        if (ObjectUtil.isEmpty(flowEngineLog)) {
            throw new ServiceException(FlowEngineLogExceptionEnum.FLOW_ENGINE_LOG_NOT_EXISTED);
        }
        return flowEngineLog;
    }

    private LambdaQueryWrapper<FlowEngineLog> createWrapper(FlowEngineLogRequest flowEngineLogRequest) {
        LambdaQueryWrapper<FlowEngineLog> queryWrapper = new LambdaQueryWrapper<>();

        Long instanceId = flowEngineLogRequest.getInstanceId();
        queryWrapper.eq(ObjectUtil.isNotNull(instanceId), FlowEngineLog::getInstanceId, instanceId);

        String message = flowEngineLogRequest.getMessage();
        queryWrapper.like(ObjectUtil.isNotEmpty(message), FlowEngineLog::getMessage, message);

        queryWrapper.orderByAsc(FlowEngineLog::getLogId);

        return queryWrapper;
    }

    private FlowEngineLog createBaseFlowLog(InstanceContext instanceContext, Long nodeId, String message, Boolean successFlag) {
        FlowEngineLog flowEngineLog = new FlowEngineLog();

        flowEngineLog.setLogId(IdWorker.getId());
        flowEngineLog.setModelId(instanceContext.getModel().getModelId());
        flowEngineLog.setDefinitionId(instanceContext.getDefinition().getDefinitionId());

        RuntimeInstance runtimeInstance = instanceContext.getRuntimeInstance();
        if (runtimeInstance != null) {
            flowEngineLog.setInstanceId(runtimeInstance.getInstanceId());
        }

        flowEngineLog.setReNodeId(nodeId);
        flowEngineLog.setMessage(message);

        flowEngineLog.setSuccessFlag(successFlag ? YesOrNotEnum.Y.getCode() : YesOrNotEnum.N.getCode());

        if (instanceContext.getInstanceVariable() != null) {
            flowEngineLog.setVariableJsonValue(JSON.toJSONString(instanceContext.getInstanceVariable()));
        }

        flowEngineLog.setLogLevel(FlowLogLevelEnum.INFO.getCode());

        return flowEngineLog;
    }

}