package com.fjhx.mes.service.report.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fjhx.common.utils.Assert;
import com.fjhx.file.utils.ObsFileUtil;
import com.fjhx.item.entity.product.po.ProductInfo;
import com.fjhx.item.service.product.ProductInfoService;
import com.fjhx.mes.entity.production.po.ProductionOrder;
import com.fjhx.mes.entity.production.po.ProductionOrderDetail;
import com.fjhx.mes.entity.production.po.ProductionProcesses;
import com.fjhx.mes.entity.report.dto.ReportLossesDetailsDto;
import com.fjhx.mes.entity.report.dto.ReportLossesDetailsSelectDto;
import com.fjhx.mes.entity.report.po.ReportLossesDetails;
import com.fjhx.mes.entity.report.vo.ReportLossesDetailsVo;
import com.fjhx.mes.entity.technology.po.TechnologyProcessLine;
import com.fjhx.mes.mapper.report.ReportLossesDetailsMapper;
import com.fjhx.mes.service.production.ProduceOrderDetailService;
import com.fjhx.mes.service.production.ProduceOrderService;
import com.fjhx.mes.service.production.ProductionProcessesService;
import com.fjhx.mes.service.report.ReportLossesDetailsService;
import com.fjhx.mes.service.technology.TechnologyProcessLineService;
import com.fjhx.tenant.utils.DeptUstil;
import com.fjhx.wms.entity.stock.emums.JournalType;
import com.fjhx.wms.entity.stock.po.StockWait;
import com.fjhx.wms.entity.stock.po.StockWaitDetails;
import com.fjhx.wms.service.stock.StockWaitDetailsService;
import com.fjhx.wms.service.stock.StockWaitService;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.wrapper.IWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;


/**
 * <p>
 * 报损管理 服务实现类
 * </p>
 *
 * @since 2024-01-26
 */
@Service
public class ReportLossesDetailsServiceImpl extends ServiceImpl<ReportLossesDetailsMapper, ReportLossesDetails> implements ReportLossesDetailsService {

    private final ProductInfoService productInfoService;
    private final StockWaitService stockWaitService;
    private final StockWaitDetailsService stockWaitDetailsService;
    private final ProduceOrderService produceOrderService;
    private final ProductionProcessesService productionProcessesService;
    private final TechnologyProcessLineService technologyProcessLineService;
    private final ProduceOrderDetailService produceOrderDetailService;

    @Autowired
    public ReportLossesDetailsServiceImpl(ProductInfoService productInfoService, StockWaitService stockWaitService, StockWaitDetailsService stockWaitDetailsService, ProduceOrderService produceOrderService, ProductionProcessesService productionProcessesService, TechnologyProcessLineService technologyProcessLineService, ProduceOrderDetailService produceOrderDetailService) {
        this.productInfoService = productInfoService;
        this.stockWaitService = stockWaitService;
        this.stockWaitDetailsService = stockWaitDetailsService;
        this.produceOrderService = produceOrderService;
        this.productionProcessesService = productionProcessesService;
        this.technologyProcessLineService = technologyProcessLineService;
        this.produceOrderDetailService = produceOrderDetailService;
    }

    @Override
    public Page<ReportLossesDetailsVo> getPage(ReportLossesDetailsSelectDto dto) {
        IWrapper<ReportLossesDetails> wrapper = getWrapper();

        //类型过滤
        wrapper.eq("rld", ReportLossesDetailsVo::getType, dto.getType());

        //时间范围过滤
        wrapper.ge("rld", ReportLossesDetails::getRepoTime, dto.getBeginTime());
        wrapper.le("rld", ReportLossesDetails::getRepoTime, dto.getEndTime());

        //近n天过滤
        Integer pastDay = dto.getPastDay();
        if (ObjectUtil.isNotEmpty(pastDay)) {
            wrapper.ge("DATEDIFF(NOW(),rld.repo_time)", 0);
            wrapper.le("DATEDIFF(NOW(),rld.repo_time)", pastDay);
        }


        //关键字检索
        if (ObjectUtil.isNotEmpty(dto.getKeyword())) {
            List<Long> pIds = productInfoService.listObject(ProductInfo::getId, q -> q
                    .like(ProductInfo::getCustomCode, dto.getKeyword())
                    .or().like(ProductInfo::getName, dto.getKeyword())
            );
            wrapper.and(q -> q
                    .in("rld.material_id", pIds)
                    .or().like("rld.repo_time", dto.getKeyword())
                    .or().like("po.code", dto.getKeyword())
            );
        }

        //权限过滤：报损统计
        wrapper.in("rld", ReportLossesDetails::getCompanyId, SecurityUtils.getCompanyIds());
        wrapper.eq("rld", ReportLossesDetails::getCompanyId, dto.getCompanyId());

        //工序id过滤
        wrapper.eq("rld", ReportLossesDetails::getProcessId, dto.getProcessId());


        wrapper.groupBy("rld.id");

        wrapper.orderByDesc("rld", ReportLossesDetails::getId);
        Page<ReportLossesDetailsVo> page = this.baseMapper.getPage(dto.getPage(), wrapper);

        List<ReportLossesDetailsVo> records = page.getRecords();
        if (ObjectUtil.isEmpty(records)) {
            return page;
        }

        //获取生产公司信息
        List<Long> companyIds = records.stream().map(ReportLossesDetailsVo::getCompanyId).collect(Collectors.toList());
        Map<Long, String> companyNameMap = DeptUstil.getDeptNameMap(companyIds);

        //获取工序信息
        List<Long> ppIds = records.stream().map(ReportLossesDetails::getProcessId).collect(Collectors.toList());
        Map<Long, String> ppMap = new HashMap<>();
        if (ObjectUtil.isNotEmpty(ppIds)) {
            ppMap = productionProcessesService.mapKV(ProductionProcesses::getId, ProductionProcesses::getName, q -> q.in(ProductionProcesses::getId, ppIds));
        }
        for (ReportLossesDetailsVo record : records) {
            //赋值生产公司名称
            record.setCompanyName(companyNameMap.get(record.getCompanyId()));

            //赋值工序名称
            record.setProcessName(ppMap.get(record.getProcessId()));
        }

        //赋值产品信息
        productInfoService.attributeAssign(records, ReportLossesDetailsVo::getMaterialId, (item, product) -> {
            item.setMaterialCode(product.getCustomCode());
            item.setMaterialName(product.getName());
            item.setMaterialLength(product.getLength());
            item.setMaterialWidth(product.getWidth());
            item.setMaterialHeight(product.getHeight());
        });

        return page;
    }

    @Override
    public ReportLossesDetailsVo detail(Long id) {
        ReportLossesDetails ReportLossesDetails = this.getById(id);
        ReportLossesDetailsVo result = BeanUtil.toBean(ReportLossesDetails, ReportLossesDetailsVo.class);
        return result;
    }

    @DSTransactional
    @Override
    public void add(ReportLossesDetailsDto dto) {

        Long companyId = SecurityUtils.getCompanyId();
        dto.setCompanyId(companyId);

        ProductInfo materialInfo = productInfoService.getById(dto.getMaterialId());
        Assert.notEmpty(materialInfo, "查询不到报损物品信息！");

        //赋值报损时物料价格
        dto.setMaterialPrice(materialInfo.getPrice());

        //报损原材料 有工序 计算返工成本，返工道数
        Long processId = dto.getProcessId();
        if (ObjectUtil.isNotEmpty(processId) && ObjectUtil.notEqual(processId, -1)) {
            //获取任务产品 工艺线路
            ProductionOrderDetail productionOrderDetail = produceOrderDetailService.getById(dto.getProdTaskId());
            Assert.notEmpty(productionOrderDetail, "查询不到生产任务信息！！！");
            ProductInfo productInfo = productInfoService.getById(productionOrderDetail.getProductId());
            Assert.notEmpty(productInfo, "查询不到产品信息");

            //冗余产品id
            dto.setProductId(productionOrderDetail.getProductId());

            Map<Long, TechnologyProcessLine> technologyLineMap = technologyProcessLineService
                    .mapKEntity(TechnologyProcessLine::getTargetProcessesId, q -> q
                            .eq(TechnologyProcessLine::getTechnologyId, productInfo.getTechnologyId()));

            Long tempProcessId = processId;
            int reCount = 0;
            BigDecimal rePrice = BigDecimal.ZERO;
            while (true) {
                ProductionProcesses productionProcesses = productionProcessesService.getById(tempProcessId);
                TechnologyProcessLine technologyProcessLine = technologyLineMap.get(tempProcessId);
                if (ObjectUtil.isEmpty(technologyProcessLine)) {
                    break;
                }
                tempProcessId = technologyProcessLine.getSourceProcessesId();

                reCount++;

                //赋值报损工序价格
                BigDecimal costPrice = productionProcesses.getCostPrice();
                costPrice = ObjectUtil.isEmpty(costPrice) ? BigDecimal.ZERO : costPrice;
                rePrice = rePrice.add(costPrice);
            }
            //赋值成本价
            dto.setReCount(reCount);
            dto.setRePrice(rePrice);
        }

        this.save(dto);

        //保存附件
        ObsFileUtil.saveFile(dto.getFileList(), dto.getId());

        JournalType journalType = Objects.equals(dto.getType(), 1) ? JournalType.REPAIR_EXCEED_OUT : JournalType.LOSE_EXCEED_OUT;

        Long prodOrderId = dto.getProdOrderId();
        ProductionOrder productionOrder = produceOrderService.getById(prodOrderId);

        //生成待出库数据
        StockWait stockWait = new StockWait();
        stockWait.setCompanyId(SecurityUtils.getCompanyId());
        stockWait.setType(2);
        stockWait.setBusinessType(journalType.getDetailType());
        stockWait.setBusinessId(dto.getId());
        stockWait.setStatus(0);

        if (ObjectUtil.isNotEmpty(productionOrder)) {
            stockWait.setBusinessCode(productionOrder.getCode());
        }

        stockWaitService.save(stockWait);
        StockWaitDetails stockWaitDetails = new StockWaitDetails();
        stockWaitDetails.setStockWaitId(stockWait.getId());
        stockWaitDetails.setProductId(dto.getMaterialId());
        stockWaitDetails.setQuantity(dto.getQuantity());
        stockWaitDetails.setReceiptQuantity(BigDecimal.ZERO);
        stockWaitDetailsService.save(stockWaitDetails);
    }

    @Override
    public void edit(ReportLossesDetailsDto dto) {

        //保存附件
        ObsFileUtil.editFile(dto.getFileList(), dto.getId());

        this.updateById(dto);
    }

    @Override
    @DSTransactional
    public void delete(Long id) {
        this.removeById(id);

        StockWait stockWait = stockWaitService.getOne(q -> q.eq(StockWait::getBusinessId, id));
        if (ObjectUtil.equals(stockWait.getStatus(), 0)) {
            throw new ServiceException("报损已出库，禁止删除！");
        }

        //删除待出库
        stockWaitService.removeById(stockWait.getId());
        stockWaitDetailsService.remove(q -> q.eq(StockWaitDetails::getStockWaitId, stockWait.getId()));
    }

    @Override
    public JSONObject lossesStatistics(ReportLossesDetailsSelectDto dto) {
        IWrapper<Object> wrapper = IWrapper.getWrapper();

        //权限过滤：报损统计
        wrapper.in("rld", ReportLossesDetails::getCompanyId, SecurityUtils.getCompanyIds());
        wrapper.eq("rld", ReportLossesDetails::getCompanyId, dto.getCompanyId());


        //近n天过滤
        Integer pastDay = dto.getPastDay();
        if (ObjectUtil.isNotEmpty(pastDay)) {
            wrapper.ge("DATEDIFF(NOW(),rld.repo_time)", 0);
            wrapper.le("DATEDIFF(NOW(),rld.repo_time)", pastDay);
        }
        return baseMapper.lossesStatistics(wrapper);
    }

}
