package com.cf.imes.module.system.service.machine;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
import com.cf.imes.framework.common.enums.MachineTypeEnum;
import com.cf.imes.framework.common.exception.ServiceException;
import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.common.util.json.JsonUtils;
import com.cf.imes.framework.common.util.object.BeanUtils;
import com.cf.imes.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.cf.imes.framework.organ.core.aop.OrganIgnore;
import com.cf.imes.framework.security.core.LoginUser;
import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils;
import com.cf.imes.module.executor.api.plan.OrderPlanApi;
import com.cf.imes.module.system.controller.admin.machine.vo.*;
import com.cf.imes.module.system.controller.admin.systemconfig.vo.ProcessSchemeConfig;
import com.cf.imes.module.system.convert.machine.MachineConvert;
import com.cf.imes.module.system.dal.dataobject.machine.MachineBrandDO;
import com.cf.imes.module.system.dal.dataobject.machine.MachineDO;
import com.cf.imes.module.system.dal.dataobject.machine.MachineLimitDO;
import com.cf.imes.module.system.dal.dataobject.machinetemplate.MachineTemplateDO;
import com.cf.imes.module.system.dal.dataobject.organ.OrganizationDO;
import com.cf.imes.module.system.dal.dataobject.systemconfig.SystemConfigSchemeDO;
import com.cf.imes.module.system.dal.mysql.machine.MachineBrandMapper;
import com.cf.imes.module.system.dal.mysql.machine.MachineLimitMapper;
import com.cf.imes.module.system.dal.mysql.machine.MachineMapper;
import com.cf.imes.module.system.dal.mysql.machinetemplate.MachineTemplateMapper;
import com.cf.imes.module.system.dal.mysql.organ.OrganMapper;
import com.cf.imes.module.system.dal.mysql.systemconfig.SystemConfigSchemeMapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import jakarta.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.cf.imes.framework.common.util.json.JsonUtils.*;
import static com.cf.imes.framework.security.core.util.SecurityFrameworkUtils.getUserOrganId;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.*;

/**
 * 机台 Service 实现类
 *
 * @author 晨丰科技
 */
@Service
@Slf4j
@Validated
public class MachineServiceImpl implements MachineService {


    @Resource
    private MachineMapper machineMapper;

    @Resource
    private MachineLimitMapper machineLimitMapper;

    @Resource
    private MachineTemplateMapper machineTemplateMapper;

    @Resource
    private OrderPlanApi orderPlanApi;

    @Resource
    private MachineBrandMapper machineBrandMapper;

    @Resource
    private OrganMapper organMapper;

    @Resource
    private SystemConfigSchemeMapper systemConfigSchemeMapper;

    private static final String FIELD_ADVANCED_CONFIGS_REF = "advancedConfigsRef";
    private static final String FIELD_AUTO_LABEL_MODEL = "autoLabelModel";
    private static final String FIELD_CUTTING_MODULE_REF = "cuttingModuleRef";
    private static final String FIELD_DRILL_MODEL = "drillModel";

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createCutting(CuttingSaveReqVO createReqVO) throws JsonProcessingException {
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        assert loginUser != null;

        MachineLimitDO machineLimitDO = machineLimitMapper.selectById(loginUser.getOrganId());

        if(machineLimitDO == null){
            throw new ServiceException(MACHINE_DATA_ERROR);
        }

        // 校验机台名称是否重复
        MachineDO machine = machineMapper.selectMachineName(createReqVO.getName(), createReqVO.getMachineType(), getUserOrganId());
        if (machine != null) {
            throw new ServiceException(MACHINE_NAME_ERROR);
        }

        validateMachineNum(createReqVO.getMachineType(), loginUser.getOrganId());

        MachineDO machineDO;
        MachineTemplateDO machineTemplateDO;

        if (createReqVO.getId() != null) {

            MachineDO copyMachineDO = machineMapper.selectById(createReqVO.getId());

            // 插入
            machineDO = MachineConvert.convert(createReqVO);

            String updatedJsonString = handleMachineSpecificConfiguration(machineDO, machineDO.getMachineType(), copyMachineDO.getSetting(), true);

            machineDO.setSetting(updatedJsonString == null ? copyMachineDO.getSetting() : zipString(updatedJsonString));

            machineDO.setId(null);

            machineMapper.insert(machineDO);

            // 返回
            return machineDO.getId();

        }

        machineTemplateDO = machineTemplateMapper.selectMachine(createReqVO.getBrandId(), createReqVO.getMachineId());

        if (machineTemplateDO == null) {
            throw new ServiceException(THE_MODEL_OF_THE_MACHINE_DOES_NOT_EXIST);
        }

        // 创建新的机器对象
        machineDO = MachineConvert.convert(createReqVO);

        // 处理不同机台类型的配置
        String updatedJsonString = handleMachineSpecificConfiguration(machineDO, machineTemplateDO.getMachineType(), machineTemplateDO.getSetting(), false);

        machineDO.setSetting(updatedJsonString == null ? machineTemplateDO.getSetting() : zipString(updatedJsonString));
        machineMapper.insert(machineDO);


        // 返回新机器的ID
        return machineDO.getId();
    }


    // 判断机台的模板是否是钻孔机或封边机
    private String handleMachineSpecificConfiguration(MachineDO machineDO, Integer machineType, String machineSetting, Boolean isMachine) throws JsonProcessingException {

        String updatedJsonString = null;

        if (MachineTypeEnum.CUTTING.getType().equals(machineType)) {
            updatedJsonString = handleCuttingMachineConfiguration(machineDO, machineSetting, isMachine);
        } else if (MachineTypeEnum.BANDING.getType().equals(machineType)) {
            updatedJsonString = handleBandingMachineConfiguration(machineDO, machineSetting, isMachine);
        }

        return updatedJsonString;
    }


    // 创建贴标机
    private String handleCuttingMachineConfiguration(MachineDO machineDO, String machineSetting, Boolean isMachine) throws JsonProcessingException {

        String unzipMachineSetting = unzipString(machineSetting);

        JsonNode cuttingSetting = JsonUtils.parseTree(unzipMachineSetting);

        JsonNode advancedConfigsRef = cuttingSetting.get(FIELD_ADVANCED_CONFIGS_REF);


        boolean isAutoLabelEnabled = advancedConfigsRef.get("isAutoLabelEnabled") != null && advancedConfigsRef.get("isAutoLabelEnabled").asBoolean();


        if (isAutoLabelEnabled && advancedConfigsRef.has(FIELD_AUTO_LABEL_MODEL)) {
            Integer autoLabelModelId = advancedConfigsRef.get(FIELD_AUTO_LABEL_MODEL).asInt();

            MachineDO newMachine;

            if (Boolean.TRUE.equals(isMachine)) {

                MachineDO machine = machineMapper.selectById(autoLabelModelId);
                if (machine != null) {
                    newMachine = MachineDO.builder()
                            .labelId(machine.getLabelId())
                            .machineType(machine.getMachineType())
                            .brandId(machine.getBrandId())
                            .setting(machine.getSetting())
                            .name("开料" + machineDO.getName() + "-贴标机")
                            .build();

                    machineMapper.insert(newMachine);

                    // 创建 ObjectMapper 对象
                    ObjectMapper mapper = new ObjectMapper();

                    // 将 JSON 字符串解析为 Map
                    Map map = mapper.readValue(unzipMachineSetting, Map.class);

                    // 修改值
                    Map<String, Object> advancedConfigs = (Map<String, Object>) map.get(FIELD_ADVANCED_CONFIGS_REF);
                    advancedConfigs.put(FIELD_AUTO_LABEL_MODEL, newMachine.getId());

                    // 将 Map 转换为 JSON 字符串
                    return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(map);


                }
            }

            MachineTemplateDO machineTemplate = machineTemplateMapper.selectById(autoLabelModelId);
            if (machineTemplate != null) {
                newMachine = MachineDO.builder()
                        .labelId(machineDO.getLabelId())
                        .machineType(machineTemplate.getMachineType())
                        .brandId(machineTemplate.getBrandId())
                        .setting(machineTemplate.getSetting())
                        .name("开料" + machineDO.getName() + "-贴标机")
                        .build();

                machineMapper.insert(newMachine);

                // 创建 ObjectMapper 对象
                ObjectMapper mapper = new ObjectMapper();

                // 将 JSON 字符串解析为 Map
                Map map = mapper.readValue(unzipMachineSetting, Map.class);

                // 修改值
                Map<String, Object> advancedConfigs = (Map<String, Object>) map.get(FIELD_ADVANCED_CONFIGS_REF);
                advancedConfigs.put(FIELD_AUTO_LABEL_MODEL, newMachine.getId());

                // 将 Map 转换为 JSON 字符串
                return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(map);

            }
        }

        return null;
    }


    // 创建钻孔机
    private String handleBandingMachineConfiguration(MachineDO machineDO, String machineSetting, Boolean isMachine) throws JsonProcessingException {

        String unzipMachineSetting = unzipString(machineSetting);

        JsonNode drillSetting = JsonUtils.parseTree(unzipMachineSetting);

        JsonNode cuttingModuleRef = drillSetting.get(FIELD_CUTTING_MODULE_REF);


        boolean isDrillEnabled = cuttingModuleRef.get("isDrillEnabled") != null && cuttingModuleRef.get("isDrillEnabled").asBoolean();

        if (isDrillEnabled && cuttingModuleRef.has(FIELD_DRILL_MODEL)) {
            Integer drillModelId = cuttingModuleRef.get(FIELD_DRILL_MODEL).asInt();

            MachineDO newMachine;

            if (Boolean.TRUE.equals(isMachine)) {

                MachineDO machine = machineMapper.selectById(drillModelId);
                if (machine != null) {
                    newMachine = MachineDO.builder()
                            .labelId(machine.getLabelId())
                            .machineType(machine.getMachineType())
                            .brandId(machine.getBrandId())
                            .setting(machine.getSetting())
                            .name("封边" + machineDO.getName() + "-钻孔机")
                            .build();

                    machineMapper.insert(newMachine);

                    // 创建 ObjectMapper 对象
                    ObjectMapper mapper = new ObjectMapper();

                    // 将 JSON 字符串解析为 Map
                    Map map = mapper.readValue(unzipMachineSetting, Map.class);

                    // 修改值
                    Map<String, Object> advancedConfigs = (Map<String, Object>) map.get(FIELD_CUTTING_MODULE_REF);
                    advancedConfigs.put(FIELD_DRILL_MODEL, newMachine.getId());

                    // 将 Map 转换为 JSON 字符串并打印输出
                    return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(map);


                }
            }


            MachineTemplateDO machineTemplate = machineTemplateMapper.selectById(drillModelId);
            if (machineTemplate != null) {
                newMachine = MachineDO.builder()
                        .labelId(machineDO.getLabelId())
                        .machineType(machineTemplate.getMachineType())
                        .brandId(machineTemplate.getBrandId())
                        .setting(machineTemplate.getSetting())
                        .name("封边" + machineDO.getName() + "-钻孔机")
                        .build();

                machineMapper.insert(newMachine);

                // 创建 ObjectMapper 对象
                ObjectMapper mapper = new ObjectMapper();

                // 将 JSON 字符串解析为 Map
                Map map = mapper.readValue(unzipMachineSetting, Map.class);

                // 修改值
                Map<String, Object> advancedConfigs = (Map<String, Object>) map.get(FIELD_CUTTING_MODULE_REF);
                advancedConfigs.put(FIELD_DRILL_MODEL, newMachine.getId());

                // 将 Map 转换为 JSON 字符串并打印输出
                return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(map);
            }
        }

        return null;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateCutting(CuttingSaveReqVO updateReqVO) {
        // 校验存在和判断机台名称是否重复
        validateExists(updateReqVO.getId(), updateReqVO.getName(), updateReqVO.getMachineType(), updateReqVO.getId());
        // 更新
        MachineDO updateObj = MachineConvert.convert(updateReqVO);

        updateObj.setUpdateTime(LocalDateTime.now());

        // 判断传入的机台配置是否存在，如果存在，压缩数据后更新
        if (!updateReqVO.getMachineSettingDO().isEmpty()) {
            updateObj.setSetting(JsonUtils.zipString(updateObj.getSetting()));
        }

        machineMapper.updateById(updateObj);
    }

    @Override
    public void deleteCutting(Long id) {
        // 校验存在
        MachineDO machineDO = machineMapper.selectById(id);
        if (machineDO == null) {
            throw new ServiceException(MACHINE_NOT_EXISTS);
        }
        // 校验当前机台是否使用
        if (Boolean.TRUE.equals(orderPlanApi.getOrderPlan(id).getData())) {
            throw new ServiceException(THE_CURRENT_MACHINE_IS_ALREADY_IN_USE);
        }

        List<SystemConfigSchemeDO> systemConfigSchemeDOS = systemConfigSchemeMapper.selectOrganConfig(machineDO.getOrganId());

        List<Long> machineIds = new ArrayList<>();

        if(CollUtil.isNotEmpty(systemConfigSchemeDOS)){

            try {
                systemConfigSchemeDOS.forEach(f -> {
                    List<ProcessSchemeConfig> processSchemeConfigs = parseArray(f.getSetting(), ProcessSchemeConfig.class);
                    machineIds.addAll(processSchemeConfigs.stream().map(m -> Long.valueOf(m.getAnalyzer())).distinct().toList());
                });

            }catch (Exception e){
                log.error("删除机台解析加工方案组配置报错："+e.getMessage());
            }
        }

        if(machineIds.contains(id)){
            throw new ServiceException(MACHINE_USED_ALREADY);
        }

        machineMapper.deleteById(id);
    }

    @Override
    public void batchDeleteCutting(List<Long> ids) {
        List<MachineDO> machineDOS = machineMapper.selectBatchIds(ids);
        if (CollUtil.isEmpty(machineDOS)) {
            throw new ServiceException(MACHINE_NOT_EXISTS);
        }
        machineMapper.deleteBatchIds(ids);
    }

    @Override
    public CuttingRespVO getCutting(Long id) {
        MachineDO machineDO = machineMapper.selectById(id);

        if (machineDO == null) {
            throw new ServiceException(MACHINE_ERROR);
        }
        // 对机台的配置进行解压
        machineDO.setSetting(JsonUtils.unzipString(machineDO.getSetting()));

        return MachineConvert.convert(machineDO);

    }


    @Override
    public List<MachineDO> list(Collection<Long> ids) {
        return machineMapper.selectBatchIds(ids);
    }

    @Override
    public Map<Integer, List<MachineVO>> getMachineTree(Long type) {

        List<MachineDO> machineDOS = machineMapper.selectList(new LambdaQueryWrapperX<MachineDO>()
                .eqIfPresent(MachineDO::getOrganId, getUserOrganId())
                .eqIfPresent(MachineDO::getMachineType, type));

        List<Long> brandIds = machineDOS.stream().map(MachineDO::getBrandId).toList();
        List<MachineBrandDO> machineBrandDOS = new ArrayList<>();

        if (!brandIds.isEmpty()) {
            machineBrandDOS = machineBrandMapper.selectBatchIds(brandIds);
        }

        List<MachineVO> machineVOS = BeanUtils.toBean(machineDOS, MachineVO.class);

        for (MachineVO machineVO : machineVOS) {

            machineVO.setBrand(machineBrandDOS.stream().filter(m -> m.getId().equals(machineVO.getBrandId())).map(MachineBrandDO::getBrand)
                    .findFirst().orElse(null));
        }

        return machineVOS.stream().collect(Collectors.groupingBy(MachineVO::getMachineType));
    }


    @Override
    public Boolean authInsert(MachineOrgAuthReq vo) {

        OrganizationDO tenant = organMapper.selectById(vo.getOrganId());
        if (tenant == null) {
            throw new ServiceException(ORGAN_NOT_EXISTS);
        }

        MachineLimitDO machineLimit = machineLimitMapper.selectById(vo.getOrganId());

        if (machineLimit != null) {
            throw new ServiceException(ORGAN_ALREADY_EXISTS);
        }

        Integer[] limits = {vo.getCutLimit(), vo.getDrillLimit(), vo.getLabelLimit(), vo.getSawLimit(), vo.getSealedgeLimit()};

        for (Integer limit : limits) {
            if (limit > 127) {
                throw new ServiceException(MACHINE_NUM_DATA_ERROR);
            }
        }


        MachineLimitDO machineLimitDO = MachineLimitDO.builder()
                .organId(vo.getOrganId())
                .cutLimit(vo.getCutLimit())
                .drillLimit(vo.getDrillLimit())
                .labelLimit(vo.getLabelLimit())
                .sawLimit(vo.getSawLimit())
                .sealedgeLimit(vo.getSealedgeLimit())
                .build();

        machineLimitMapper.insert(machineLimitDO);

        return Boolean.TRUE;
    }


    @Override
    public Boolean authUpdate(MachineOrgAuthReq vo) {

        OrganizationDO tenant = organMapper.selectById(vo.getOrganId());
        if (tenant == null) {
            throw new ServiceException(ORGAN_NOT_EXISTS);
        }


        Integer[] limits = {vo.getCutLimit(), vo.getDrillLimit(), vo.getLabelLimit(), vo.getSawLimit(), vo.getSealedgeLimit()};

        for (Integer limit : limits) {
            if (limit > 127) {
                throw new ServiceException(MACHINE_NUM_DATA_ERROR);
            }
        }


        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        assert loginUser != null;

        MachineLimitDO machineLimitDO = MachineLimitDO.builder()
                .organId(vo.getOrganId())
                .cutLimit(vo.getCutLimit())
                .drillLimit(vo.getDrillLimit())
                .labelLimit(vo.getLabelLimit())
                .sawLimit(vo.getSawLimit())
                .sealedgeLimit(vo.getSealedgeLimit())
                .build();

        machineLimitDO.setUpdateTime(LocalDateTime.now());
        machineLimitDO.setUpdater(loginUser.getNickname());

        machineLimitMapper.updateById(machineLimitDO);

        return Boolean.TRUE;
    }

    @Override
    public PageResult<OrganMachineResp> organMachinePage(OrganMachinePage machinePage) {

        PageDTO<OrganMachineResp> page = new PageDTO<>(machinePage.getPageNo(), machinePage.getPageSize());


        IPage<OrganMachineResp> packRespVOS = machineLimitMapper.selectOrganMachinePage(page, machinePage);


        return new PageResult<>(packRespVOS.getRecords(), packRespVOS.getTotal());

    }

    @Override
    public List<Integer> organMachineByOrganId(Long organId) {

        String setting = machineLimitMapper.selectById(organId).getSetting();

        return JSON.parseArray(setting).toJavaList(Integer.class);

    }

    @Override
    public PageResult<MachineDO> page(MachinePageReqVO pageParam) {
        return machineMapper.selectPage(pageParam, new ArrayList<>());
    }


    @Override
    public List<MachineSimpleRespVO> getMachineModelAll(Integer type) {

        List<MachineDO> machineDOS = machineMapper.selectMachineList(type, getUserOrganId());

        return BeanUtils.toBean(machineDOS, MachineSimpleRespVO.class);

    }


    @Override
    @OrganIgnore
    public List<MachineSimpleRespVO> simpleList( Long type) {
        List<MachineDO> machineDOS = machineMapper.selectList(new LambdaQueryWrapperX<MachineDO>()
                .eqIfPresent(MachineDO::getOrganId, getUserOrganId())
                .eqIfPresent(MachineDO::getMachineType, type)
                .select(MachineDO::getId, MachineDO::getName, MachineDO::getMachineType)
        );
        return BeanUtils.toBean(machineDOS, MachineSimpleRespVO.class);
    }


    @Override
    public List<MachineBrandModelRespVO> getBrandAndModel(Integer type) {


        return machineBrandMapper.selectMachineList(type);


    }


    @Override
    public Map<Integer, List<MachineBrandModelRespVO>> getTemplateAuth(MachinePageReqVO pageParam) {


        List<MachineBrandModelRespVO> machineBrandModelRespVOS = machineBrandMapper.selectMachineNoDefault();

        return machineBrandModelRespVOS.stream().collect(Collectors.groupingBy(MachineBrandModelRespVO::getMachineType));


    }


    // 校验机台是否存在和机台名称是否重复
    private void validateExists(Long id, String name, Integer machineType, Long machineId) {
        MachineDO machineDO = machineMapper.selectById(id);
        if (machineDO == null) {
            throw new ServiceException(MACHINE_NOT_EXISTS);
        }
        MachineDO machine = machineMapper.selectMachine(name, machineType, machineId, getUserOrganId());

        if (machine != null) {
            throw new ServiceException(MACHINE_NAME_ERROR);
        }
    }

    // 校验当前组织当前类型的机台是否达到最大创建数量
    private void validateMachineNum(Integer machineType, Long organId) {

        int machineNum = machineMapper.selectByOrganId(machineType, organId).size() ;

        MachineLimitDO machineLimitDO = machineLimitMapper.selectById(organId);

        Integer authMachineNum = switch (machineType) {
            case 1 -> machineLimitDO.getCutLimit();
            case 2 -> machineLimitDO.getDrillLimit();
            case 3 -> machineLimitDO.getSawLimit();
            case 4 -> machineLimitDO.getLabelLimit();
            case 5 -> machineLimitDO.getSealedgeLimit();
            default -> 0;
        };

        if (machineNum >= authMachineNum) {
            throw new ServiceException(MACHINE_NUM_ERROR);
        }

    }

    @Override
    public PageResult<MachineDO> getPage(MachinePageReqVO pageReqVO) {

        PageResult<MachineDO> machineDOPageResult = machineMapper.selectPage(pageReqVO);
        // 将查询到的机台的数据中的配置信息进行解压
        List<MachineDO> list = machineDOPageResult.getList().stream().map(m -> m.setSetting(JsonUtils.unzipString(m.getSetting()))).toList();
        return machineDOPageResult.setList(list);

    }


}