package cn.stylefeng.team.form.modular.manage.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.extra.spring.SpringUtil;
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.entity.BaseEntity;
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.roses.kernel.rule.util.SortUtils;
import cn.stylefeng.team.form.api.FormModelServiceApi;
import cn.stylefeng.team.form.api.callback.RemoveFormModelCallbackApi;
import cn.stylefeng.team.form.api.entity.FormDefinition;
import cn.stylefeng.team.form.api.entity.FormModel;
import cn.stylefeng.team.form.api.pojo.FormDesignVo;
import cn.stylefeng.team.form.modular.manage.enums.FormModelExceptionEnum;
import cn.stylefeng.team.form.modular.manage.factory.FormDefinitionFactory;
import cn.stylefeng.team.form.modular.manage.mapper.FormModelMapper;
import cn.stylefeng.team.form.modular.manage.request.FormModelRequest;
import cn.stylefeng.team.form.modular.manage.service.FormDefinitionService;
import cn.stylefeng.team.form.modular.manage.service.FormModelService;
import cn.stylefeng.team.project.api.SystemCodeApi;
import cn.stylefeng.team.project.api.enums.SystemCodeTypeEnum;
import cn.stylefeng.team.project.api.pojo.SystemCodeInfo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
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.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class FormModelServiceImpl extends ServiceImpl<FormModelMapper, FormModel> implements FormModelService {

    @Resource
    private FormDefinitionService formDefinitionService;

    @Resource
    private SystemCodeApi systemCodeApi;

    @Resource
    private FormModelServiceApi formModelServiceApi;

    @Override
    public void add(FormModelRequest formModelRequest) {

        this.validateRepeat(formModelRequest, false);

        FormModel formModel = new FormModel();
        BeanUtil.copyProperties(formModelRequest, formModel);

        formModel.setFormModelId(IdWorker.getId());

        FormDefinition metaDefinition = FormDefinitionFactory.initDefinition(formModel);

        formModel.setCurrentVersion(metaDefinition.getFldVersion());
        formModel.setCurrentDefinitionId(metaDefinition.getFormDefinitionId());

        this.save(formModel);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void del(FormModelRequest formModelRequest) {

        FormModel formModel = this.queryFormModel(formModelRequest);
        this.removeById(formModel.getFormModelId());

        Map<String, RemoveFormModelCallbackApi> beansOfType = SpringUtil.getBeansOfType(RemoveFormModelCallbackApi.class);
        for (RemoveFormModelCallbackApi removeFormModelCallbackApi : beansOfType.values()) {
            removeFormModelCallbackApi.modelRemoveAction(formModel.getFormModelId());
        }

    }

    @Override
    public void edit(FormModelRequest formModelRequest) {

        formModelRequest.setFormModelCode(null);

        formModelRequest.setSystemCode(null);
        formModelRequest.setSystemCodeShow(null);

        formModelRequest.setBusinessCategoryId(null);
        formModelRequest.setBusinessId(null);

        this.validateRepeat(formModelRequest, true);

        FormModel formModel = this.queryFormModel(formModelRequest);
        BeanUtil.copyProperties(formModelRequest, formModel);
        this.updateById(formModel);
    }

    @Override
    public FormModel detail(FormModelRequest formModelRequest) {
        return this.queryFormModel(formModelRequest);
    }

    @Override
    public PageResult<FormModel> findPage(FormModelRequest formModelRequest) {
        LambdaQueryWrapper<FormModel> wrapper = createWrapper(formModelRequest);

        wrapper.select(FormModel::getFormModelId, FormModel::getFormModelName, FormModel::getFormModelCode, FormModel::getCurrentVersion, FormModel::getCurrentDefinitionId,
                FormModel::getSystemCodeShow, FormModel::getFldSort, BaseEntity::getCreateTime);

        Page<FormModel> sysRolePage = this.page(PageFactory.defaultPage(), wrapper);
        return PageResultFactory.createPageResult(sysRolePage);
    }

    @Override
    public void updateModelVersion(FormDefinition newFormDefinition) {
        Long formModelId = newFormDefinition.getFormModelId();

        LambdaUpdateWrapper<FormModel> metaModelLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        metaModelLambdaUpdateWrapper.set(FormModel::getCurrentVersion, newFormDefinition.getFldVersion());
        metaModelLambdaUpdateWrapper.set(FormModel::getCurrentDefinitionId, newFormDefinition.getFormDefinitionId());
        metaModelLambdaUpdateWrapper.eq(FormModel::getFormModelId, formModelId);
        this.update(metaModelLambdaUpdateWrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public FormDefinition upgradeModel(FormModelRequest formModelRequest) {

        FormModel formModel = this.queryFormModel(formModelRequest);

        FormDefinition oldDefinition = this.formDefinitionService.findModelNewestDefinition(formModel.getFormModelId());

        FormDefinition newFormDefinition = FormDefinitionFactory.upgradeDefinition(oldDefinition, oldDefinition.getFormConfig());

        formModel.setCurrentVersion(newFormDefinition.getFldVersion());
        formModel.setCurrentDefinitionId(newFormDefinition.getFormDefinitionId());
        this.updateById(formModel);

        this.formDefinitionService.save(newFormDefinition);

        return newFormDefinition;
    }

    @Override
    public List<FormModel> getSelectList(FormModelRequest formModelRequest) {

        LambdaQueryWrapper<FormModel> wrapper = createWrapper(formModelRequest);

        wrapper.select(FormModel::getCurrentDefinitionId, FormModel::getFormModelName);

        return this.list(wrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void copyModel(FormModelRequest formModelRequest) {


        FormModel newFormModel = this.queryFormModel(formModelRequest);

        newFormModel.setFormModelId(IdWorker.getId());
        newFormModel.setFormModelName(newFormModel.getFormModelName() + "_copy");

        String removeSuffix = newFormModel.getFormModelCode().replaceAll("_\\d+", "");
        newFormModel.setFormModelCode(removeSuffix + "_" + newFormModel.getFormModelId());

        SystemCodeInfo newSystemCode = systemCodeApi.createNewSystemCode(SystemCodeTypeEnum.FORM);
        newFormModel.setSystemCode(newSystemCode.getSystemCode());
        newFormModel.setSystemCodeShow(newSystemCode.getSystemCodeShow());

        FormDefinition newFormDefinition = this.formDefinitionService.findModelNewestDefinition(formModelRequest.getFormModelId());

        newFormDefinition.setFormDefinitionId(IdWorker.getId());
        newFormDefinition.setFormModelId(newFormModel.getFormModelId());
        newFormModel.setCurrentDefinitionId(newFormDefinition.getFormDefinitionId());

        newFormDefinition.setFldVersion("v1.0");
        newFormModel.setCurrentVersion(newFormDefinition.getFldVersion());

        this.save(newFormModel);
        this.formDefinitionService.save(newFormDefinition);
    }

    @Override
    public List<FormModel> findList(FormModelRequest formModelRequest) {
        LambdaQueryWrapper<FormModel> wrapper = this.createWrapper(formModelRequest);
        return this.list(wrapper);
    }

    @Override
    public String getFormModelName(Long formModelId) {

        LambdaQueryWrapper<FormModel> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(FormModel::getFormModelId, formModelId);
        queryWrapper.select(FormModel::getFormModelName);
        FormModel one = this.getOne(queryWrapper, false);

        if (ObjectUtil.isNotEmpty(one)) {
            return one.getFormModelName();
        }

        return "";
    }

    @Override
    public List<FormDesignVo> getFromDesignList(List<Long> formDefinitionIdList) {
        return this.getFromDesignListWithMobileCondition(formDefinitionIdList, false);
    }

    @Override
    public List<FormDesignVo> getFromDesignListWithMobileCondition(List<Long> formDefinitionIdList, Boolean mobileFlag) {
        ArrayList<FormDesignVo> formDesignVos = new ArrayList<>();

        if (ObjectUtil.isEmpty(formDefinitionIdList)) {
            return formDesignVos;
        }

        List<FormDefinition> formDefinitionList = this.formDefinitionService.listByIds(formDefinitionIdList);
        formDefinitionList = SortUtils.sortListByObjectKey(formDefinitionList, formDefinitionIdList);

        if (ObjectUtil.isEmpty(formDefinitionList)) {
            return formDesignVos;
        }

        for (FormDefinition formDefinition : formDefinitionList) {

            FormDesignVo formDesignVo = new FormDesignVo();

            String formModelName = formModelServiceApi.getFormModelName(formDefinition.getFormModelId());
            formDesignVo.setFormModelName(formModelName);

            Long formDefinitionId = formDefinition.getFormDefinitionId();
            formDesignVo.setFormDefinitionId(formDefinitionId);

            if (mobileFlag != null && mobileFlag) {
                FormDesignVo mobileFormConfig = formDefinition.getMobileFormConfig();
                formDesignVo.setFormConfig(mobileFormConfig.getFormConfig());
                formDesignVo.setFormItemList(mobileFormConfig.getFormItemList());
            } else {
                FormDesignVo pcFormConfig = formDefinition.getFormConfig();
                formDesignVo.setFormConfig(pcFormConfig.getFormConfig());
                formDesignVo.setFormItemList(pcFormConfig.getFormItemList());
            }

            formDesignVos.add(formDesignVo);
        }

        return formDesignVos;
    }

    @Override
    public Long getFormBusinessId(Long formDefinitionId) {
        LambdaQueryWrapper<FormDefinition> formDefinitionLambdaQueryWrapper = new LambdaQueryWrapper<>();
        formDefinitionLambdaQueryWrapper.eq(FormDefinition::getFormDefinitionId, formDefinitionId);
        formDefinitionLambdaQueryWrapper.select(FormDefinition::getBusinessId);
        FormDefinition formDefinition = this.formDefinitionService.getOne(formDefinitionLambdaQueryWrapper, false);
        if (formDefinition != null) {
            return formDefinition.getBusinessId();
        }
        return null;
    }

    @Override
    public List<FormModel> getBusinessFormModelList(Long businessId) {
        LambdaQueryWrapper<FormModel> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.select(FormModel::getFormModelId, FormModel::getFormModelName, FormModel::getCurrentDefinitionId);
        queryWrapper.eq(FormModel::getBusinessId, businessId);
        return this.list(queryWrapper);
    }

    @Override
    public FormDesignVo getFromDesignByFormModelId(Long formModelId) {

        FormDefinition formDefinition = this.formDefinitionService.findModelNewestDefinition(formModelId);
        if (formDefinition == null) {
            return null;
        }

        return formDefinition.getFormConfig();
    }

    @Override
    public Map<Long, Integer> statFormNumber(Set<Long> businessIds) {
        HashMap<Long, Integer> resultMap = new HashMap<>();

        if (ObjectUtil.isEmpty(businessIds)) {
            return resultMap;
        }

        LambdaQueryWrapper<FormModel> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(FormModel::getBusinessId, businessIds);
        wrapper.select(FormModel::getBusinessId, FormModel::getFormModelId);
        List<FormModel> formModelList = this.list(wrapper);
        if (ObjectUtil.isEmpty(formModelList)) {
            return resultMap;
        }

        return formModelList.stream().collect(Collectors.groupingBy(FormModel::getBusinessId, Collectors.summingInt(i -> 1)));
    }

    @Override
    public FormDefinition createFormModelAndDefinition(Long businessCategoryId, Long businessId, String formName, String formCode) {

        FormModel formModel = new FormModel();
        formModel.setFormModelId(IdWorker.getId());
        formModel.setBusinessCategoryId(businessCategoryId);
        formModel.setBusinessId(businessId);
        formModel.setFormModelName(formName);
        formModel.setFormModelCode(formCode);

        SystemCodeInfo formModelCode = systemCodeApi.createNewSystemCode(SystemCodeTypeEnum.FORM);
        formModel.setSystemCode(formModelCode.getSystemCode());
        formModel.setSystemCodeShow(formModelCode.getSystemCodeShow());

        formModel.setCurrentVersion("1.0");
        formModel.setFlowFlag(YesOrNotEnum.N.getCode());
        formModel.setFldSort(new BigDecimal(1000));

        FormDefinition metaDefinition = FormDefinitionFactory.initDefinition(formModel);
        formModel.setCurrentDefinitionId(metaDefinition.getFormDefinitionId());

        this.save(formModel);

        return metaDefinition;
    }

    @Override
    public void updateFormDefinition(FormDefinition formDefinition) {
        this.formDefinitionService.updateById(formDefinition);
    }

    private FormModel queryFormModel(FormModelRequest formModelRequest) {
        FormModel formModel = this.getById(formModelRequest.getFormModelId());
        if (ObjectUtil.isEmpty(formModel)) {
            throw new ServiceException(FormModelExceptionEnum.FORM_MODEL_NOT_EXISTED);
        }
        return formModel;
    }

    private LambdaQueryWrapper<FormModel> createWrapper(FormModelRequest formModelRequest) {
        LambdaQueryWrapper<FormModel> queryWrapper = new LambdaQueryWrapper<>();

        String searchText = formModelRequest.getSearchText();
        if (ObjectUtil.isNotEmpty(searchText)) {
            queryWrapper.nested(i -> i.like(FormModel::getFormModelName, searchText).or().like(FormModel::getFormModelCode, searchText).or().like(FormModel::getSystemCodeShow, searchText));
        }

        if (ObjectUtil.isNotEmpty(formModelRequest.getBusinessId())) {
            queryWrapper.eq(FormModel::getBusinessId, formModelRequest.getBusinessId());
        }

        if (ObjectUtil.isNotEmpty(formModelRequest.getBusinessCategoryId())) {
            queryWrapper.eq(FormModel::getBusinessCategoryId, formModelRequest.getBusinessCategoryId());
        }

        return queryWrapper;
    }

    private void validateRepeat(FormModelRequest formModelRequest, boolean editFlag) {

        LambdaQueryWrapper<FormModel> wrapper1 = new LambdaQueryWrapper<>();
        wrapper1.eq(FormModel::getFormModelName, formModelRequest.getFormModelName());
        if (editFlag) {
            wrapper1.ne(FormModel::getFormModelId, formModelRequest.getFormModelId());
        }
        long nameNumber = this.count(wrapper1);
        if (nameNumber > 0) {
            throw new ServiceException(FormModelExceptionEnum.NAME_REPEAT);
        }

        LambdaQueryWrapper<FormModel> wrapper2 = new LambdaQueryWrapper<>();
        wrapper2.eq(FormModel::getFormModelCode, formModelRequest.getFormModelCode());
        if (editFlag) {
            wrapper2.ne(FormModel::getFormModelId, formModelRequest.getFormModelId());
        }
        long codeNumber = this.count(wrapper2);
        if (codeNumber > 0) {
            throw new ServiceException(FormModelExceptionEnum.CODE_REPEAT);
        }

        LambdaQueryWrapper<FormModel> wrapper3 = new LambdaQueryWrapper<>();
        wrapper3.eq(FormModel::getSystemCode, formModelRequest.getSystemCode());
        if (editFlag) {
            wrapper3.ne(FormModel::getFormModelId, formModelRequest.getFormModelId());
        }
        long sysCodeNumber = this.count(wrapper3);
        if (sysCodeNumber > 0) {
            throw new ServiceException(FormModelExceptionEnum.SYS_CODE_REPEAT);
        }

    }
}