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

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.stylefeng.roses.kernel.db.api.pojo.entity.BaseEntity;
import cn.stylefeng.roses.kernel.rule.enums.StatusEnum;
import cn.stylefeng.roses.kernel.rule.enums.YesOrNotEnum;
import cn.stylefeng.roses.kernel.rule.exception.base.ServiceException;
import cn.stylefeng.team.project.api.BusinessCategoryApi;
import cn.stylefeng.team.project.api.entity.BusinessCategory;
import cn.stylefeng.workflow.api.entity.design.Definition;
import cn.stylefeng.workflow.api.entity.design.Model;
import cn.stylefeng.workflow.design.api.callback.ModelRemoveCallback;
import cn.stylefeng.workflow.design.api.pojo.BusinessCategoryVo;
import cn.stylefeng.workflow.design.modular.enums.ModelExceptionEnum;
import cn.stylefeng.workflow.design.modular.factory.DefinitionFactory;
import cn.stylefeng.workflow.design.modular.factory.ModelFactory;
import cn.stylefeng.workflow.design.modular.mapper.ModelMapper;
import cn.stylefeng.workflow.design.modular.pojo.request.ModelRequest;
import cn.stylefeng.workflow.design.modular.service.DefinitionService;
import cn.stylefeng.workflow.design.modular.service.ModelService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

@Service
public class ModelServiceImpl extends ServiceImpl<ModelMapper, Model> implements ModelService {

    @Resource
    private BusinessCategoryApi businessCategoryApi;

    @Resource
    private DefinitionService definitionService;

    @Override
    public List<BusinessCategoryVo> findList(ModelRequest modelRequest) {

        if (modelRequest.isOpenAuthValidate() && ObjectUtil.isEmpty(modelRequest.getUserHaveAuthFlowModelIdList())) {
            return new ArrayList<>();
        }

        List<BusinessCategory> categoryList = businessCategoryApi.getCategoryList(modelRequest.getBusinessCategoryId());

        LambdaQueryWrapper<Model> wrapper = this.createWrapper(modelRequest);

        wrapper.select(Model::getModelName, Model::getCurrentVersion, Model::getModelId, BaseEntity::getCreateTime,
                Model::getBusinessCategoryId, Model::getModelIcon, Model::getLatestDefinitionId, Model::getModelIconType, Model::getModelIconFileId);
        List<Model> list = this.list(wrapper);

        return ModelFactory.combineCategoryAndModel(categoryList, list);
    }

    @Override
    public Model detail(ModelRequest modelRequest) {
        LambdaQueryWrapper<Model> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Model::getModelId, modelRequest.getModelId());
        queryWrapper.select(Model::getModelId, Model::getModelName, Model::getModelIcon, Model::getModelIconType, Model::getModelIconFileId, Model::getModelCode, Model::getBusinessCategoryId,
                Model::getFldSort, Model::getRemark, Model::getLatestDefinitionId, Model::getManagerUserId);
        return this.getOne(queryWrapper, false);
    }

    @Override
    public Model add(ModelRequest modelRequest) {

        Model model = new Model();
        BeanUtil.copyProperties(modelRequest, model);

        model.setModelId(IdWorker.getId());
        model.setStatusFlag(StatusEnum.ENABLE.getCode());

        Definition newDefinition = DefinitionFactory.createNewDefinition(model);
        this.definitionService.save(newDefinition);

        Long definitionId = newDefinition.getDefinitionId();
        model.setLatestDefinitionId(definitionId);
        model.setCurrentVersion(newDefinition.getDefinitionVersion());

        model.setHiddenFlag(YesOrNotEnum.N.getCode());

        this.save(model);

        return model;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void del(ModelRequest modelRequest) {

        Model model = this.queryModel(modelRequest);
        this.removeById(model.getModelId());

        Map<String, ModelRemoveCallback> beansOfType = SpringUtil.getBeansOfType(ModelRemoveCallback.class);
        for (ModelRemoveCallback value : beansOfType.values()) {
            value.modelRemoveAction(model.getModelId());
        }
    }

    @Override
    public void edit(ModelRequest modelRequest) {

        Model model = this.queryModel(modelRequest);

        model.setModelName(modelRequest.getModelName());

        model.setBusinessCategoryId(modelRequest.getBusinessCategoryId());

        model.setModelIconType(modelRequest.getModelIconType());
        model.setModelIcon(modelRequest.getModelIcon());
        model.setModelIconFileId(modelRequest.getModelIconFileId());

        model.setModelCode(modelRequest.getModelCode());

        model.setRemark(modelRequest.getRemark());

        model.setFldSort(modelRequest.getFldSort());

        model.setManagerUserId(modelRequest.getManagerUserId());

        this.updateById(model);
    }

    @Override
    public Model copy(ModelRequest modelRequest) {

        Model model = this.detail(modelRequest);

        Definition definition = definitionService.getById(model.getLatestDefinitionId());

        Model copyNewModel = DefinitionFactory.createCopyNewModel(model, modelRequest);
        Definition copyNewDefinition = DefinitionFactory.createCopyNewDefinition(definition, model.getModelId());

        copyNewModel.setLatestDefinitionId(copyNewDefinition.getDefinitionId());
        copyNewModel.setCurrentVersion(copyNewDefinition.getDefinitionVersion());

        this.save(model);
        this.definitionService.save(copyNewDefinition);

        definitionService.updateDefinition(copyNewDefinition, copyNewDefinition.getMyFlowDesign(), copyNewDefinition.getXSixGraphJson());

        return model;
    }

    @Override
    public Long getModelLatestDefinitionId(Long modelId) {
        LambdaQueryWrapper<Model> modelLambdaQueryWrapper = new LambdaQueryWrapper<>();
        modelLambdaQueryWrapper.eq(Model::getModelId, modelId);
        modelLambdaQueryWrapper.select(Model::getLatestDefinitionId);
        Model model = this.getOne(modelLambdaQueryWrapper, false);
        if (model != null) {
            return model.getLatestDefinitionId();
        }

        return null;
    }

    @Override
    public Map<Long, Long> batchGetDefinitionId(List<Long> modelIdList) {
        LambdaQueryWrapper<Model> modelLambdaQueryWrapper = new LambdaQueryWrapper<>();
        modelLambdaQueryWrapper.in(Model::getModelId, modelIdList);
        modelLambdaQueryWrapper.select(Model::getModelId, Model::getLatestDefinitionId);
        List<Model> modelList = this.list(modelLambdaQueryWrapper);
        if (ObjectUtil.isEmpty(modelList)) {
            return new HashMap<>();
        }

        HashMap<Long, Long> modelDefinitionIdMap = new HashMap<>();
        for (Model model : modelList) {
            modelDefinitionIdMap.put(model.getModelId(), model.getLatestDefinitionId());
        }
        return modelDefinitionIdMap;
    }

    @Override
    public Model getModelInfo(Long modelId) {
        ModelRequest modelRequest = new ModelRequest();
        modelRequest.setModelId(modelId);
        return this.detail(modelRequest);
    }

    @Override
    public String getModelName(Long modelId) {
        LambdaQueryWrapper<Model> modelLambdaQueryWrapper = new LambdaQueryWrapper<>();
        modelLambdaQueryWrapper.eq(Model::getModelId, modelId);
        modelLambdaQueryWrapper.select(Model::getModelName);
        Model model = this.getOne(modelLambdaQueryWrapper, false);
        if (model != null) {
            return model.getModelName();
        }
        return "";
    }

    @Override
    public List<Model> getModelList(Collection<Long> modelIdList) {
        LambdaQueryWrapper<Model> modelLambdaQueryWrapper = new LambdaQueryWrapper<>();
        modelLambdaQueryWrapper.in(Model::getModelId, modelIdList);
        modelLambdaQueryWrapper.select(Model::getModelId, Model::getModelName);

        modelLambdaQueryWrapper.eq(Model::getStatusFlag, StatusEnum.ENABLE.getCode());

        modelLambdaQueryWrapper.orderByAsc(Model::getFldSort);

        return this.list(modelLambdaQueryWrapper);
    }

    @Override
    public List<Model> getCategoryModelList(Long categoryId) {

        ModelRequest modelRequest = new ModelRequest();
        modelRequest.setBusinessCategoryId(categoryId);
        LambdaQueryWrapper<Model> wrapper = this.createWrapper(modelRequest);

        wrapper.select(Model::getModelName, Model::getCurrentVersion, Model::getLatestDefinitionId);
        return this.list(wrapper);
    }

    @Override
    public void updateModelVersion(Long modelId, String newVersion, Long newDefinitionId) {

        Model model = this.getById(modelId);

        model.setCurrentVersion(newVersion);
        model.setLatestDefinitionId(newDefinitionId);

        this.updateById(model);
    }

    @Override
    public List<Model> getDefinitionSelectList(ModelRequest modelRequest) {

        LambdaQueryWrapper<Model> modelLambdaQueryWrapper = new LambdaQueryWrapper<>();

        modelLambdaQueryWrapper.select(Model::getBusinessCategoryId, Model::getModelId, Model::getLatestDefinitionId, Model::getModelName);

        modelLambdaQueryWrapper.orderByDesc(BaseEntity::getCreateTime);

        List<Model> list = this.list(modelLambdaQueryWrapper);

        for (Model model : list) {
            Long categoryId = model.getBusinessCategoryId();
            String categoryName = businessCategoryApi.getCategoryName(categoryId);
            model.setCategoryName(categoryName);
        }

        return list;
    }

    private Model queryModel(ModelRequest modelRequest) {
        Model model = this.getById(modelRequest.getModelId());
        if (ObjectUtil.isEmpty(model)) {
            throw new ServiceException(ModelExceptionEnum.MODEL_NOT_EXISTED);
        }
        return model;
    }

    private LambdaQueryWrapper<Model> createWrapper(ModelRequest modelRequest) {
        LambdaQueryWrapper<Model> queryWrapper = new LambdaQueryWrapper<>();

        Long businessCategoryId = modelRequest.getBusinessCategoryId();
        queryWrapper.eq(ObjectUtil.isNotNull(businessCategoryId), Model::getBusinessCategoryId, businessCategoryId);

        String searchText = modelRequest.getSearchText();
        if (StrUtil.isNotBlank(searchText)) {
            queryWrapper.nested(i -> i.like(Model::getModelName, searchText).or().like(Model::getModelCode, searchText));
        }

        queryWrapper.eq(Model::getHiddenFlag, YesOrNotEnum.N.getCode());

        queryWrapper.eq(Model::getStatusFlag, StatusEnum.ENABLE.getCode());

        queryWrapper.orderByAsc(Model::getFldSort);

        if (modelRequest.isOpenAuthValidate()) {
            queryWrapper.in(Model::getModelId, modelRequest.getUserHaveAuthFlowModelIdList());
        }

        return queryWrapper;
    }

}