package com.yeejoin.amos.boot.module.jg.biz.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.text.CharSequenceUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.yeejoin.amos.boot.biz.common.annotation.FieldDisplayDefine;
import com.yeejoin.amos.boot.biz.common.entity.BaseEntity;
import com.yeejoin.amos.boot.module.jg.api.dto.*;
import com.yeejoin.amos.boot.module.jg.api.entity.JgUseRegistration;
import com.yeejoin.amos.boot.module.jg.api.entity.JgUseRegistrationEq;
import com.yeejoin.amos.boot.module.jg.api.entity.JgUseRegistrationManage;
import com.yeejoin.amos.boot.module.jg.api.mapper.JgUseRegistrationEqMapper;
import com.yeejoin.amos.boot.module.jg.api.mapper.JgUseRegistrationManageMapper;
import com.yeejoin.amos.boot.module.jg.api.service.IBizDataChangeHandleStrategy;
import com.yeejoin.amos.boot.module.jg.biz.service.*;
import com.yeejoin.amos.boot.module.ymt.api.entity.EquipmentCategory;
import com.yeejoin.amos.boot.module.ymt.api.entity.IdxBizJgDesignInfo;
import com.yeejoin.amos.boot.module.ymt.api.entity.IdxBizJgFactoryInfo;
import com.yeejoin.amos.boot.module.ymt.api.entity.IdxBizJgRegisterInfo;
import com.yeejoin.amos.boot.module.ymt.api.enums.FlowStatusEnum;
import com.yeejoin.amos.boot.module.ymt.api.mapper.EquipmentCategoryMapper;
import com.yeejoin.amos.boot.module.ymt.api.mapper.IdxBizJgDesignInfoMapper;
import com.yeejoin.amos.boot.module.ymt.api.mapper.IdxBizJgFactoryInfoMapper;
import com.yeejoin.amos.boot.module.ymt.api.mapper.IdxBizJgRegisterInfoMapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.typroject.tyboot.core.restful.exception.instance.BadRequest;

import javax.annotation.PostConstruct;
import java.lang.reflect.Field;
import java.util.*;

/**
 * 单个维护使用登记策略实现类
 */
@Component
public class SingleDataChangeProcessStrategy implements IChangeDataProcessStrategy {

    private final IdxBizJgDesignInfoMapper idxBizJgDesignInfoMapper;

    private final IdxBizJgFactoryInfoMapper jgFactoryInfoMapper;

    private final IdxBizJgRegisterInfoMapper idxBizJgRegisterInfoMapper;

    private final JgUseRegistrationServiceImpl useRegistrationService;

    private final JgUseRegistrationEqMapper jgRelationEquipMapper;

    private final CommonEquipDataProcessService commonEquipDataProcessService;

    private final JgUseRegistrationManageMapper registrationManageMapper;

    private final EquipmentCategoryMapper equipmentCategoryMapper;

    private final List<String> jsonFields = new ArrayList<>();


    public SingleDataChangeProcessStrategy(IdxBizJgDesignInfoMapper idxBizJgDesignInfoMapper, IdxBizJgFactoryInfoMapper jgFactoryInfoMapper, IdxBizJgRegisterInfoMapper idxBizJgRegisterInfoMapper, JgUseRegistrationServiceImpl useRegistrationService, JgUseRegistrationEqMapper jgRelationEquipMapper, IIdxBizJgTechParamsElevatorService iIdxBizJgTechParamsElevatorService, IIdxBizJgTechParamsVehicleService iIdxBizJgTechParamsVehicleService, IIdxBizJgTechParamsRopewayService iIdxBizJgTechParamsRopewayService, IIdxBizJgTechParamsRidesService iIdxBizJgTechParamsRidesService, IIdxBizJgTechParamsBoilerService iIdxBizJgTechParamsBoilerService, IIdxBizJgTechParamsVesselService iIdxBizJgTechParamsVesselService, IIdxBizJgTechParamsPipelineService iIdxBizJgTechParamsPipelineService, IIdxBizJgTechParamsLiftingService iIdxBizJgTechParamsLiftingService, EquipChangeDataUpdateService equipChangeDataUpdateService, CommonEquipDataProcessService commonEquipDataProcessService, JgUseRegistrationManageMapper registrationManageMapper, EquipmentCategoryMapper equipmentCategoryMapper) {
        this.idxBizJgDesignInfoMapper = idxBizJgDesignInfoMapper;
        this.jgFactoryInfoMapper = jgFactoryInfoMapper;
        this.idxBizJgRegisterInfoMapper = idxBizJgRegisterInfoMapper;
        this.useRegistrationService = useRegistrationService;
        this.jgRelationEquipMapper = jgRelationEquipMapper;
        this.commonEquipDataProcessService = commonEquipDataProcessService;
        this.registrationManageMapper = registrationManageMapper;
        this.equipmentCategoryMapper = equipmentCategoryMapper;
    }

    @PostConstruct
    public void init() {
        Field[] fields1 = EquipRegisterChangeDataDto.class.getDeclaredFields();
        for (Field field : fields1) {
            field.setAccessible(true);
            FieldDisplayDefine fieldDisplayDefine = field.getAnnotation(FieldDisplayDefine.class);
            if (fieldDisplayDefine != null && fieldDisplayDefine.type() == JSON.class) {
                jsonFields.add(field.getName());
            }
        }
        Field[] fields2 = EquipFactoryChangeDataDto.class.getDeclaredFields();
        for (Field field : fields2) {
            field.setAccessible(true);
            FieldDisplayDefine fieldDisplayDefine = field.getAnnotation(FieldDisplayDefine.class);
            if (fieldDisplayDefine != null && fieldDisplayDefine.type() == JSON.class) {
                jsonFields.add(field.getName());
            }
        }
        // 设计信息构建
        Field[] fields3 = EquipDesignChangeDataDto.class.getDeclaredFields();
        for (Field field : fields3) {
            field.setAccessible(true);
            FieldDisplayDefine fieldDisplayDefine = field.getAnnotation(FieldDisplayDefine.class);
            if (fieldDisplayDefine != null && fieldDisplayDefine.type() == JSON.class) {
                jsonFields.add(field.getName());
            }
        }

        // 技术参数附件
        Field[] fields4 = TechParamsLiftingChangeFieldDto.class.getDeclaredFields();
        for (Field field : fields4) {
            field.setAccessible(true);
            FieldDisplayDefine fieldDisplayDefine = field.getAnnotation(FieldDisplayDefine.class);
            if (fieldDisplayDefine != null && fieldDisplayDefine.type() == JSON.class) {
                jsonFields.add(field.getName());
            }
        }
    }


    @Override
    public List<ChangeDataDto> handle(Map<String, Object> changeData, String defaultChangeId) {
        String record = this.getRecord(defaultChangeId);
        List<ChangeDataDto> allChangeColumns = new ArrayList<>();
        // 新数据解析
        EquipRegisterChangeDataDto registerChangeDataDto = commonEquipDataProcessService.castMap2Bean(changeData, EquipRegisterChangeDataDto.class);
        EquipFactoryChangeDataDto factoryChangeDataDto = commonEquipDataProcessService.castMap2Bean(changeData, EquipFactoryChangeDataDto.class);
        EquipDesignChangeDataDto designChangeDataDto = commonEquipDataProcessService.castMap2Bean(changeData, EquipDesignChangeDataDto.class);
        commonEquipDataProcessService.buildChangeFields(record, designChangeDataDto, factoryChangeDataDto, registerChangeDataDto, allChangeColumns);
        // 前置校验
        commonEquipDataProcessService.checkValidField(record, registerChangeDataDto, null, null, null);
        commonEquipDataProcessService.checkValidField(record, factoryChangeDataDto, registerChangeDataDto.getEquList(), registerChangeDataDto.getEquCategory(), registerChangeDataDto.getEquDefine());
        commonEquipDataProcessService.checkValidField(record, designChangeDataDto, registerChangeDataDto.getEquList(), registerChangeDataDto.getEquCategory(), registerChangeDataDto.getEquDefine());
        // 设备制造、设计、注册信息业务处理落库
        commonEquipDataProcessService.dealBizDataForEquip(record, registerChangeDataDto);
        commonEquipDataProcessService.dealBizDataForEquip(record, factoryChangeDataDto);
        commonEquipDataProcessService.dealBizDataForEquip(record, designChangeDataDto);
        // 设备技术参数入库处理
        commonEquipDataProcessService.updateTechParamInfo(registerChangeDataDto, record, changeData, allChangeColumns);
        // 更新单据对应的证信息
        this.updateManagerCertInfo(defaultChangeId, record, registerChangeDataDto);
        return allChangeColumns;
    }

    private void updateManagerCertInfo(String applyNo, String record, EquipRegisterChangeDataDto registerChangeDataDto) {
        JgUseRegistration jgUseRegistration = useRegistrationService.getOne(new LambdaQueryWrapper<JgUseRegistration>()
                .eq(JgUseRegistration::getApplyNo, applyNo).select(BaseEntity::getSequenceNbr, JgUseRegistration::getStatus, JgUseRegistration::getUseRegistrationCode));
        if (FlowStatusEnum.TO_BE_FINISHED.getName().equals(jgUseRegistration.getStatus())) {
            LambdaQueryWrapper<JgUseRegistrationManage> queryWrapper = new LambdaQueryWrapper<JgUseRegistrationManage>()
                    .eq(JgUseRegistrationManage::getUseRegistrationCode, jgUseRegistration.getUseRegistrationCode())
                    .eq(JgUseRegistrationManage::getIsDelete, 0).select(BaseEntity::getSequenceNbr,JgUseRegistrationManage::getVersion);
            JgUseRegistrationManage jgUseRegistrationManage = registrationManageMapper.selectOne(queryWrapper);
            if (jgUseRegistrationManage != null && !StringUtils.equals(jgUseRegistrationManage.getEquDefineCode(), registerChangeDataDto.getEquDefine())) {
                LambdaUpdateWrapper<JgUseRegistrationManage> updateWrapper = new LambdaUpdateWrapper<>();
                updateWrapper.eq(BaseEntity::getSequenceNbr, jgUseRegistrationManage.getSequenceNbr());
                if (StringUtils.isNotEmpty(registerChangeDataDto.getEquDefine())) {
                    EquipmentCategory equipmentCategory = getEquipmentCategory(registerChangeDataDto);
                    updateWrapper.set(JgUseRegistrationManage::getEquDefineCode, registerChangeDataDto.getEquDefine());
                    updateWrapper.set(JgUseRegistrationManage::getEquDefine, equipmentCategory.getName());
                } else {
                    updateWrapper.set(JgUseRegistrationManage::getEquDefineCode, null);
                    updateWrapper.set(JgUseRegistrationManage::getEquDefine, null);
                }
                updateWrapper.set(JgUseRegistrationManage::getVersion, jgUseRegistrationManage.getVersion() + 1);
                updateWrapper.set(JgUseRegistrationManage::getRecDate, new Date());
                updateWrapper.set(JgUseRegistrationManage::getChangeReason, "设备信息变更");
                registrationManageMapper.update(null, updateWrapper);
            }
        }

    }

    private EquipmentCategory getEquipmentCategory(EquipRegisterChangeDataDto registerChangeDataDto) {
        return equipmentCategoryMapper.selectOne(new LambdaQueryWrapper<EquipmentCategory>().eq(EquipmentCategory::getCode, registerChangeDataDto.getEquDefine()));
    }

    @Override
    public Map<String, Object> getDetail(String applyNo, String bizId) {
        if (StringUtils.isEmpty(applyNo) && StringUtils.isEmpty(bizId)) {
            throw new BadRequest("applyNo and bizId can't be empty");
        }
        if (StringUtils.isNotEmpty(applyNo)) {
            return this.getDetailByApplyNo(applyNo);
        } else {
            return this.getDetailByBizId(bizId);
        }
    }

    private Map<String, Object> getDetailByBizId(String record) {
        Map<String, Object> re = new HashMap<>();
        // 注册信息(基本信息)构建
        EquipRegisterChangeDataDto equipRegisterChangeDataDto = this.buildRegisterInfo(record);
        // 制造信息构建
        EquipFactoryChangeDataDto factoryChangeDataDto = this.buildFactoryInfo(record);
        // 设计信息构建
        EquipDesignChangeDataDto designChangeDataDto = this.buildDesignInfo(record);
        // 技术参数构建
        BaseTechParamsFieldDto techParamsFieldDto = commonEquipDataProcessService.buildTechParamInfo(equipRegisterChangeDataDto, record);
        re.putAll(BeanUtil.beanToMap(equipRegisterChangeDataDto));
        re.putAll(BeanUtil.beanToMap(factoryChangeDataDto));
        re.putAll(BeanUtil.beanToMap(designChangeDataDto));
        re.putAll(BeanUtil.beanToMap(techParamsFieldDto));
        this.castJsonFields(re);
        return this.cast2UnderCase(re);
    }

    private Map<String, Object> cast2UnderCase(Map<String, Object> re) {
        // 由于历史遗留问题，和前端保存统一，要转成大写下滑线驼峰
        Map<String, Object> result = new HashMap<>();
        re.forEach((k, v) -> {
            result.put(CharSequenceUtil.toUnderlineCase(k).toUpperCase(), v);
        });
        return result;
    }

    private void castJsonFields(Map<String, Object> re) {
        jsonFields.forEach(field -> {
            if (re.containsKey(field) && re.get(field) instanceof String) {
                re.put(field, JSON.parse((String) re.get(field)));
            }
        });
    }


    private EquipDesignChangeDataDto buildDesignInfo(String record) {
        EquipDesignChangeDataDto changeDataDto = new EquipDesignChangeDataDto();
        IdxBizJgDesignInfo designInfo = idxBizJgDesignInfoMapper.selectOne(new LambdaQueryWrapper<IdxBizJgDesignInfo>().eq(IdxBizJgDesignInfo::getRecord, record));
        BeanUtil.copyProperties(designInfo, changeDataDto);
        changeDataDto.setDesignDate(DateUtil.formatDate(designInfo.getDesignDate()));
        return changeDataDto;
    }

    private EquipFactoryChangeDataDto buildFactoryInfo(String record) {
        EquipFactoryChangeDataDto changeDataDto = new EquipFactoryChangeDataDto();
        IdxBizJgFactoryInfo factoryInfo = jgFactoryInfoMapper.selectOne(new LambdaQueryWrapper<IdxBizJgFactoryInfo>().eq(IdxBizJgFactoryInfo::getRecord, record));
        BeanUtil.copyProperties(factoryInfo, changeDataDto);
        changeDataDto.setProduceDate(DateUtil.formatDate(factoryInfo.getProduceDate()));
        return changeDataDto;
    }

    private EquipRegisterChangeDataDto buildRegisterInfo(String record) {
        EquipRegisterChangeDataDto changeDataDto = new EquipRegisterChangeDataDto();
        IdxBizJgRegisterInfo registerInfo = idxBizJgRegisterInfoMapper.selectOne(new LambdaQueryWrapper<IdxBizJgRegisterInfo>().eq(IdxBizJgRegisterInfo::getRecord, record));
        BeanUtil.copyProperties(registerInfo, changeDataDto);
        return changeDataDto;
    }


    public Map<String, Object> getDetailByApplyNo(String bizId) {
        String record = this.getRecord(bizId);
        return getDetailByBizId(record);
    }


    public String getRecord(String applyNo) {
        JgUseRegistration jgUseRegistration = useRegistrationService.getOne(new LambdaQueryWrapper<JgUseRegistration>()
                .eq(JgUseRegistration::getApplyNo, applyNo).select(BaseEntity::getSequenceNbr));
        LambdaQueryWrapper<JgUseRegistrationEq> eq = new QueryWrapper<JgUseRegistrationEq>().lambda()
                .eq(JgUseRegistrationEq::getEquipTransferId, jgUseRegistration.getSequenceNbr())
                .eq(JgUseRegistrationEq::getIsDelete, false).select(JgUseRegistrationEq::getEquId);
        JgUseRegistrationEq jgUseRegistrationEq = jgRelationEquipMapper.selectOne(eq);
        return jgUseRegistrationEq.getEquId();
    }

    @Override
    public IBizDataChangeHandleStrategy.ModelType canHandleMode() {
        return IBizDataChangeHandleStrategy.ModelType.single;
    }

}
