package com.sw.common.accessory.server.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.sw.common.accessory.api.feignCilent.StoreFeignClients;
import com.sw.common.accessory.api.feignCilent.YbFeignClients;
import com.sw.common.accessory.api.model.AccessoryMaterialDto;
import com.sw.common.accessory.server.dto.*;
import com.sw.common.accessory.server.entity.Accessory;
import com.sw.common.accessory.server.entity.AccessoryProductionOrder;
import com.sw.common.accessory.server.mapper.AccessoryProductionOrderMapper;
import com.sw.common.accessory.server.service.AccessoryProductionOrderService;
import com.sw.common.accessory.server.service.AccessoryService;
import com.sw.common.accessory.utils.AccessoryProductionConstant;
import com.sw.common.accessory.utils.DateCountTools;
import com.sw.common.component.redis.server.RedissonUtils;
import com.sw.common.core.context.BaseContextHandler;
import com.sw.common.core.exception.BusinessException;
import com.sw.common.core.response.Result;
import com.sw.common.core.response.ResultGenerator;
import com.sw.common.mybatisplus.base.BaseServiceImpl;
import org.redisson.api.RLock;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.*;

/**
 * @author lf
 * @description
 * @date 2024/3/9 20:03
 */
@Service
@Transactional
public class AccessoryProductionOrderServiceImpl extends BaseServiceImpl<AccessoryProductionOrderMapper, AccessoryProductionOrder> implements AccessoryProductionOrderService {
    @Autowired
    private AccessoryProductionOrderMapper accessoryProductionOrderMapper;
    @Autowired
    private AccessoryService accessoryService;

    @Autowired
    private YbFeignClients ybFeignClients;

    @Autowired
    private DateCountTools dateCountTools;

    @Autowired
    private StoreFeignClients storeFeignClients;

    @Override
    public AccessoryProductionOrder findMaterialCodeAndProductionNo(String materialCode, String productionNo, String deviceCode) {
        return accessoryProductionOrderMapper.findMaterialCodeAndProductionNo(materialCode, productionNo, deviceCode);
    }

    @Override
    public AccessoryProductionOrder productionMaterialToSAP(Long orderNoId, String deviceCode) {
        return accessoryProductionOrderMapper.productionMaterialToSAP(orderNoId, deviceCode);
    }

    @Override
    public AccessoryProductionOrder productionOrderToSAP(Long orderNoId, String deviceCode) {
        return accessoryProductionOrderMapper.productionOrderToSAP(orderNoId, deviceCode);
    }

    @Override
    public AccessoryProductionOrder findMaterialCodeAndAccessoryId(String materialCode, Long accessoryId, String deviceCode) {
        return accessoryProductionOrderMapper.findMaterialCodeAndAccessoryId(materialCode, accessoryId, deviceCode);
    }

    @Override
    public AccessoryProductionOrder findOrderNoId(Long orderNoId) {
        return accessoryProductionOrderMapper.findOrderNoId(orderNoId);
    }
    @Override
    public AccessoryProductionOrder findByOrderNoId(Long orderNoId) {
        return accessoryProductionOrderMapper.findByOrderNoId(orderNoId);
    }

    @Override
    public List<AccessoryProductionOrder> findAllProductionOrderNo() {
        return accessoryProductionOrderMapper.findAllProductionOrderNo();
    }

    @Override
    public List<AccessoryProductionOrder> findAllMaterialOrderNo() {
        return accessoryProductionOrderMapper.findAllMaterialOrderNo();
    }

    @Override
    public Result<String> create(AccessoryProductionOrderDto dto) {
        AccessoryProductionOrder accessoryProduction = this.findByOrderNoId(dto.getOrderNoId());
        if (!ObjectUtils.isEmpty(accessoryProduction)) {
            return ResultGenerator.fail("石墨件排程工单关联对象已存在,请检查数据！");
        }
        AccessoryProductionOrder accessoryProductionOrder = new AccessoryProductionOrder();
        BeanUtils.copyProperties(dto, accessoryProductionOrder);
        if(StringUtils.isEmpty(accessoryProductionOrder.getProductionNo())){
            Result<ProductionScheduleDto> planProductionByCode = ybFeignClients.findPlanProductionById(dto.getOrderNoId());
            if (planProductionByCode.getCode().equals(200) && null != planProductionByCode.getData()) {
                ProductionScheduleDto productionSchedule = planProductionByCode.getData();
                accessoryProductionOrder.setProductionNo(productionSchedule.getProductionOrderAufnr());
            } else {
                return ResultGenerator.fail("未获取到相关排程工单数据【" + dto.getProductionNo() + "】,请检查数据!");
            }
        }
        accessoryProductionOrder.setDel(false);
        accessoryProductionOrder.setMaterialDel(false);
        accessoryProductionOrder.setProductionDel(false);
        accessoryProductionOrder.setCreateTime(new Date());
        this.saveOrUpdate(accessoryProductionOrder);
        return ResultGenerator.success(accessoryProductionOrder.getId().toString());
    }

    @Override
    public Result<String> update(AccessoryProductionOrderDto productionScheduleDto) {
        AccessoryProductionOrder accessoryProductionOrder = this.findByOrderNoId(productionScheduleDto.getOrderNoId());
        if (ObjectUtils.isEmpty(accessoryProductionOrder)) {
            return ResultGenerator.fail("石墨件排程工单关联对象不存在,请检查数据！");
        }
        //如果结束时间已存在,则触发石墨件上报
        if(null != productionScheduleDto.getEndTime()){
            accessoryProductionOrder.setEndTime(productionScheduleDto.getEndTime());
            this.saveOrUpdate(accessoryProductionOrder);
            ProductionScheduleDto productionSchedule = new ProductionScheduleDto();
            productionSchedule.setId(productionScheduleDto.getOrderNoId());
            //石墨件物料维度上报SAP
            this.productionMaterialToSAPs(productionSchedule);
            //石墨件工单维度上报SAP
            this.productionOrderToSAPs(productionSchedule);
        }
        return ResultGenerator.success();
    }

    @Override
    public Result productionMaterialToSAPs(ProductionScheduleDto productionScheduleDto) {
        RLock lock = RedissonUtils.getRLock(AccessoryProductionConstant.MATAEIAL_UP_SAP_LOCK_KEY);
        boolean isLock = RedissonUtils.getRLockExpireTime(lock, 4L, 6L);
        if (isLock) {
            try {
                lock.lock();
                if (null != productionScheduleDto.getId()) {
                    Result<ProductionScheduleDto> planProductionById = ybFeignClients.findPlanProductionById(productionScheduleDto.getId());
                    if (planProductionById.getCode().equals(200)) {
                        productionScheduleDto = planProductionById.getData();
                    }
                }
                String productModelCode = "";
                String deviceNo = "";
                if (null != productionScheduleDto.getDeviceNo()) {
                    deviceNo = productionScheduleDto.getDeviceNo();
                }
                AccessoryProductionOrder materialCodeAndProductionNo = this.productionMaterialToSAP(productionScheduleDto.getId(), deviceNo);
                if(!ObjectUtils.isEmpty(materialCodeAndProductionNo)){
                    materialCodeAndProductionNo.setLastEndTime(new Date());
                    materialCodeAndProductionNo.setReportTime(new Date());
                    materialCodeAndProductionNo.setMaterialDel(true);
                    materialCodeAndProductionNo.setDel(materialCodeAndProductionNo.isMaterialDel(), materialCodeAndProductionNo.isProductionDel());
                    this.saveOrUpdate(materialCodeAndProductionNo);
                }
                //物料状态正常的字典数据
                String normal = "";
                Result<DictionaryItemDto> normalDictionaryItem = ybFeignClients.findByThemeCodeAndItemName("accessory_graphite_parts_scrap", "normal");
                if(normalDictionaryItem.getCode().equals(200)){
                    normal = normalDictionaryItem.getData().getValue();
                }
                ComputingTimeDto computingTimeDto = new ComputingTimeDto();
                Double appearTime = 0D;
                Double noAppearTime = 0D;
                Result result = new Result();
                if (!ObjectUtils.isEmpty(materialCodeAndProductionNo)) {
                    Date startTime = materialCodeAndProductionNo.getStartTime();
                    //每次上报的结束时间
                    Date endTime = new Date();
                    if(null != materialCodeAndProductionNo.getEndTime()){
                        endTime = materialCodeAndProductionNo.getEndTime();
                    }
                    Calendar calendar = Calendar.getInstance();
                    calendar.setTime(endTime);
                    int year = calendar.get(Calendar.YEAR);
                    int startMonth = calendar.get(Calendar.MONTH) + 1;
                    String dateCount = "dateCount" + "-" + year + "-" + startMonth;
                    Accessory accessory = accessoryService.get(materialCodeAndProductionNo.getAccessoryId());
                    //查询当前设备下面所有的辅材信息拿到对应的物料号
                    List<AccessoryDetails> accessoryDetails = accessoryService.findByEquipmentAccessoryCode(deviceNo);
                    for (int i = 0; i < accessoryDetails.size(); i++) {
                        productModelCode = accessoryDetails.get(i).getMaterialCode();
                        computingTimeDto = dateCountTools.computingTime(accessoryDetails.get(i).getMaterialCode(), startTime, endTime);
                        if (null == computingTimeDto) {
                            return ResultGenerator.fail("未获取到相关数据,请确认数据!");
                        }
                        if (null != computingTimeDto) {
                            List<Map<String, Integer>> monthCounts = computingTimeDto.getMonthCounts();
                            for (Map<String, Integer> map : monthCounts) {
                                Optional<Map.Entry<String, Integer>> first = map.entrySet().stream().filter(entity -> entity.getKey().equals(dateCount)).findFirst();
                                if (first.isPresent()) {
                                    //获取到当前上报的天数
                                    Integer day = first.get().getValue();
                                    //额定寿命
                                    Double useLife = computingTimeDto.getUseLife();
                                    //停炉时间 - 上次截断时间 / 额定寿命
                                    appearTime = (day / useLife);
                                    //判断是否报废
                                    if (!normal.contains(String.valueOf(accessory.getAccessoryStatus()))) {
                                        //额定寿命 - 已使用寿命 / 额定寿命
                                        noAppearTime = (useLife - day) / useLife;
                                    }
                                }
                            }
                        }
                        if(noAppearTime.compareTo(0D)==1){
                            // 工单报消耗
                            SendUpMaterialSap(accessory, productionScheduleDto, productModelCode, noAppearTime);
                        }
                        result = SendUpMaterialSap(accessory, productionScheduleDto, productModelCode, appearTime);
                    }
                    materialCodeAndProductionNo.setLastEndTime(null);
                    materialCodeAndProductionNo.setReportTime(null);
                    materialCodeAndProductionNo.setMaterialDel(true);
                    materialCodeAndProductionNo.setDel(materialCodeAndProductionNo.isMaterialDel(), materialCodeAndProductionNo.isProductionDel());
                    this.saveOrUpdate(materialCodeAndProductionNo);
                    if (result.getCode().equals(200)) {
                        return ResultGenerator.success(computingTimeDto);
                    }else {
                        log.error("石墨件成本中心消耗上报失败!"+JSONObject.toJSON(productionScheduleDto));
                    }
                }
                return ResultGenerator.fail("石墨件成本中心消耗上报失败!", JSONObject.toJSON(productionScheduleDto));
            } finally {
                if (lock != null && lock.isLocked() && lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        } else {
            throw new BusinessException("有其他人正在操作，请稍后再试！");
        }
    }

    /**
     * 物料消耗上报SAP
     * @return
     */
    public Result SendUpMaterialSap(Accessory accessory,ProductionScheduleDto productionScheduleDto,String productModelCode,Double appearTime){
        // 工单报消耗
        ReportSapGoodsmvtDto reportSapGoodsmvtDto = new ReportSapGoodsmvtDto();
        List<ReportSapChargItemDto> reportSapChargItemDtos = new ArrayList<>();
        ReportSapChargItemDto reportSapChargItemDto = new ReportSapChargItemDto();
        reportSapChargItemDto.setAtnam("P01");
        reportSapChargItemDto.setAtwrt("JK1");
        reportSapChargItemDtos.add(reportSapChargItemDto);
        reportSapGoodsmvtDto.setChargItems(reportSapChargItemDtos);
        reportSapGoodsmvtDto.setBwart("201");
        reportSapGoodsmvtDto.setWerks("5041");
        reportSapGoodsmvtDto.setMenge(String.valueOf(appearTime));
        reportSapGoodsmvtDto.setMatnr(productModelCode);
        reportSapGoodsmvtDto.setMeins("PC");
        reportSapGoodsmvtDto.setBudat(new Date());
        reportSapGoodsmvtDto.setBldat(new Date());
        reportSapGoodsmvtDto.setErnam(BaseContextHandler.getUsername());
        Result<ProductionOrderDto> order = ybFeignClients.findProductionOrderById(Long.valueOf(String.valueOf(productionScheduleDto.getProductionOrderId())));
        if (order.getCode().equals(200) && null != order.getData()) {
            String kostl = order.getData().getField4();//成本中心
            reportSapGoodsmvtDto.setKostl(kostl);
        }
        String uniqueCode = "";
        if (!ObjectUtils.isEmpty(accessory)) {
            uniqueCode = accessory.getUniqueCode();
        }
        Result<WarehouseOutputRecordBillDetailDto> outputBillByMaterialCode = storeFeignClients.getOutputBillByUniqueCode(uniqueCode);
        if (outputBillByMaterialCode.getCode().equals(200) && null != outputBillByMaterialCode.getData()) {
            WarehouseOutputRecordBillDetailDto data = outputBillByMaterialCode.getData();
            reportSapGoodsmvtDto.setLgort(data.getWarehouseCode());
            reportSapGoodsmvtDto.setCharg(data.getBatchNo());
        }
        reportSapGoodsmvtDto.setProcName("石墨件物料消耗");
        Result result = ybFeignClients.sendSapGoodsmvt(reportSapGoodsmvtDto);
        return result;
    }

    @Override
    public Result productionOrderToSAPs(ProductionScheduleDto productionScheduleDto) {
        RLock lock = RedissonUtils.getRLock(AccessoryProductionConstant.PRODUCTIONORDER_UP_SAP_LOCK_KEY);
        boolean isLock = RedissonUtils.getRLockExpireTime(lock, 4L, 10L);
        if (isLock) {
            try {
                lock.lock();
                if (null != productionScheduleDto.getId()) {
                    Result<ProductionScheduleDto> planProductionById = ybFeignClients.findPlanProductionById(productionScheduleDto.getId());
                    if (planProductionById.getCode().equals(200)) {
                        productionScheduleDto = planProductionById.getData();
                    }
                }
                String productModelCode = "";
                String code = "";
                String deviceNo = "";
                String workcenterId = "";
                if (null != productionScheduleDto.getProductModelCode()) {
                    //物料料号
                    productModelCode = productionScheduleDto.getProductModelCode();
                }
                if (null != productionScheduleDto.getProductionOrderAufnr()) {
                    //工单号
                    code = productionScheduleDto.getProductionOrderAufnr();
                }
                if (null != productionScheduleDto.getDeviceNo()) {
                    //设备号
                    deviceNo = productionScheduleDto.getDeviceNo();
                }
                Result<ProductionOrderDto> listByWorkType = ybFeignClients.getListByWorkType(productionScheduleDto.getProductionOrderAufnr());
                if (listByWorkType.getCode().equals(200)) {
                    //获取上报的工作中心数据
                    ProductionOrderDto productionOrderDto = listByWorkType.getData();
                    workcenterId = productionOrderDto.getField4();
                }
                AccessoryProductionOrder materialCodeAndProductionNo = this.productionOrderToSAP(productionScheduleDto.getId(), deviceNo);
                //物料状态正常的字典数据
                String normal;
                Result<DictionaryItemDto> normalDictionaryItem = ybFeignClients.findByThemeCodeAndItemName("accessory_graphite_parts_scrap", "normal");
                if(normalDictionaryItem.getCode().equals(200)){
                    normal = normalDictionaryItem.getData().getValue();
                } else {
                    normal = "";
                }
                ComputingTimeDto computingTimeDto;
                List<Double> appearTime = new ArrayList<>();
                List<Double> noAppearTime = new ArrayList<>();
                if (!ObjectUtils.isEmpty(materialCodeAndProductionNo)) {
                    String materialCode = materialCodeAndProductionNo.getMaterialCode();
                    Date startTime = materialCodeAndProductionNo.getStartTime();
                    Calendar calendar = Calendar.getInstance();
                    //每次上报的结束时间
                    Date endTime = new Date();
                    //如果结束时间存在记录表中则以记录结束时间为准
                    if (null != materialCodeAndProductionNo.getEndTime()) {
                        endTime = materialCodeAndProductionNo.getEndTime();
                    }
                    calendar.setTime(endTime);
                    //拼接时间上报的key,与工具类中计算返回值进行匹配
                    int year = calendar.get(Calendar.YEAR);
                    int startMonth = calendar.get(Calendar.MONTH) + 1;
                    String dateCount = "dateCount" + "-" + year + "-" + startMonth;
                    //获取上报时间工具类
                    computingTimeDto = dateCountTools.computingTime(materialCode, startTime, endTime);
                    if (null == computingTimeDto) {
                        return ResultGenerator.fail("未获取到相关数据,请确认数据!");
                    }
                    if (null != computingTimeDto) {
                        List<Map<String, Integer>> monthCounts = computingTimeDto.getMonthCounts();
                        for (Map<String, Integer> map : monthCounts) {
                            Optional<Map.Entry<String, Integer>> first = map.entrySet().stream().filter(entity -> entity.getKey().equals(dateCount)).findFirst();
                            if (first.isPresent()) {
                                //获取到当前上报的天数
                                Integer day = first.get().getValue();
                                //查询当前工单下所有关联的物料 (判定正常物料)
                                List<AccessoryDetails> accessoryDtos = accessoryService.findByEquipmentAccessoryCode(productionScheduleDto.getDeviceNo());
                                if (!CollectionUtils.isEmpty(accessoryDtos)) {
                                    accessoryDtos.forEach(accessoryDto -> {
                                        //获取物料对应的辅料寿命类型
                                        Result<AccessoryMaterialDto> accessoryMaterial = ybFeignClients.getByMaterialCode(accessoryDto.getMaterialCode());
                                        if (accessoryMaterial.getCode().equals(200) && null != accessoryMaterial.getData()) {
                                            AccessoryMaterialDto accessoryMaterialDto = accessoryMaterial.getData();
                                            //额定寿命
                                            Double useLife = accessoryMaterialDto.getUseLife();
                                            //权重值
                                            Double weightValue = Double.valueOf(accessoryMaterialDto.getWeightValue());
                                            //停炉时间 - 上次截断时间 / 额定寿命 * 权重值
                                            double time = (weightValue * day) / (useLife * 100);
                                            appearTime.add(time);
                                            if(!normal.contains(String.valueOf(accessoryDto.getAccessoryStatus()))){
                                                //停炉时间 - 上次截断时间 / 额定寿命 * 权重值
                                                double time1 = (useLife - day) / (useLife * 100);
                                                noAppearTime.add(time1);
                                            }
                                        }
                                    });
                                }
                            }
                        }
                    }
                    //异常报工
                    if(!CollectionUtils.isEmpty(noAppearTime)){
                        SendUpOrderSAP(code, workcenterId, noAppearTime,productionScheduleDto.getDeviceNo());
                    }
                    // 正常工单报工时
                    Result result = SendUpOrderSAP(code, workcenterId, appearTime,productionScheduleDto.getDeviceNo());
                    if (result.getCode().equals(200)) {
                        materialCodeAndProductionNo.setLastEndTime(new Date());
                        materialCodeAndProductionNo.setReportTime(new Date());
                        materialCodeAndProductionNo.setProductionDel(true);
                        materialCodeAndProductionNo.setDel(materialCodeAndProductionNo.isMaterialDel(), materialCodeAndProductionNo.isProductionDel());
                        this.saveOrUpdate(materialCodeAndProductionNo);
                    }
                    return ResultGenerator.success(computingTimeDto);
                }
                return ResultGenerator.fail("石墨件工单工时上报失败!", JSONObject.toJSON(productionScheduleDto));
            } finally {
                if (lock != null && lock.isLocked() && lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        } else {
            throw new BusinessException("有其他人正在操作，请稍后再试！");
        }
    }


    /**
     * 工单工时上报SAP
     * @return
     */
    public Result SendUpOrderSAP(String code,String workcenterId,List<Double> appearTime,String deviceNo){
        // 正常工单报工时
        ReportSapWorkinghoursDto reportSapWorkinghoursDto = new ReportSapWorkinghoursDto();
        //计算工单工时(当前工单下物料之和)
        double sum = appearTime.stream().map(BigDecimal::valueOf).reduce(BigDecimal::add).orElse(BigDecimal.ZERO).doubleValue();
        BigDecimal bigDecimal = BigDecimal.valueOf(sum).setScale(3, BigDecimal.ROUND_HALF_UP);
        reportSapWorkinghoursDto.setProductionOrderId(code);
        reportSapWorkinghoursDto.setPlant("5041");
        reportSapWorkinghoursDto.setWorkcenterId(workcenterId);
        reportSapWorkinghoursDto.setCnfTime(String.valueOf(bigDecimal));
        reportSapWorkinghoursDto.setCnfTimeUom("PC");
        Date date = new Date();
        reportSapWorkinghoursDto.setCnfDate(date);
        reportSapWorkinghoursDto.setStatus(false);
        reportSapWorkinghoursDto.setProcName("石墨件工时上报");
        reportSapWorkinghoursDto.setCrystalNo(deviceNo);
        Result result = ybFeignClients.sendSapWorkinghours(reportSapWorkinghoursDto);
        return result;
    }


    public AccessoryProductionOrder fromDto(AccessoryProductionOrderDto dto) {
        AccessoryProductionOrder accessoryProductionOrder = new AccessoryProductionOrder();
        BeanUtils.copyProperties(dto, accessoryProductionOrder);
        return accessoryProductionOrder;
    }
}
