package com.yunfeiyun.agmp.fms.service.impl;

import com.yunfeiyun.agmp.common.constant.ErrorCode;
import com.yunfeiyun.agmp.common.exception.BizException;
import com.yunfeiyun.agmp.common.utils.DateUtils;
import com.yunfeiyun.agmp.common.utils.ObjectUtil;
import com.yunfeiyun.agmp.common.utils.SecurityUtils;
import com.yunfeiyun.agmp.common.utils.StringUtils;
import com.yunfeiyun.agmp.common.utils.img.GraphicsCanvas;
import com.yunfeiyun.agmp.common.utils.img.entity.GraphicsCanvasItem;
import com.yunfeiyun.agmp.common.utils.img.enums.BusType;
import com.yunfeiyun.agmp.common.utils.poi.ExcelUtil;
import com.yunfeiyun.agmp.common.utils.serialno.NoGenerator;
import com.yunfeiyun.agmp.common.utils.uuid.IdUtils;
import com.yunfeiyun.agmp.common.web.system.service.ISysConfigService;
import com.yunfeiyun.agmp.fms.domain.FmsPeriod;
import com.yunfeiyun.agmp.fms.domain.TssTracecodeapply;
import com.yunfeiyun.agmp.fms.domain.TssTracecodetpl;
import com.yunfeiyun.agmp.fms.domain.TssTracecodetplparam;
import com.yunfeiyun.agmp.fms.domain.resvo.TssTracecodeapplyExportResVo;
import com.yunfeiyun.agmp.fms.mapper.TssTracecodeapplyMapper;
import com.yunfeiyun.agmp.fms.mapper.TssTracecodetplMapper;
import com.yunfeiyun.agmp.fms.mapper.TssTracecodetplparamMapper;
import com.yunfeiyun.agmp.fms.mapper.WmsFinishgoodsMapper;
import com.yunfeiyun.agmp.fms.domain.reqvo.TssTraceCodeGenerateReqVo;
import com.yunfeiyun.agmp.fms.domain.reqvo.TssTracecodetplEditReqVo;
import com.yunfeiyun.agmp.fms.domain.resvo.WmsFinishgoodsSourceInfo;
import com.yunfeiyun.agmp.fms.service.ITssTracecodetplService;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;


/**
 * 溯源码模板Service业务层处理
 *
 * @author 杨晓辉
 * @date 2023-07-04
 */
@Service
@Slf4j
public class TssTracecodetplServiceImpl implements ITssTracecodetplService {
    @Autowired
    private TssTracecodetplMapper tssTracecodetplMapper;

    @Resource
    private TssTracecodetplparamMapper tssTracecodetplparamMapper;

    @Resource
    private ISysConfigService sysConfigService;
    @Resource
    private WmsFinishgoodsMapper wmsFinishgoodsMapper;

    @Resource
    private TssTracecodeapplyMapper tssTracecodeapplyMapper;
    @Resource
    private NoGenerator noGenerator;



    /**
     * 查询溯源码模板
     *
     * @param tracecodetplId 溯源码模板主键
     * @return 溯源码模板
     */
    @Override
    public TssTracecodetpl selectTssTracecodetplByTracecodetplId(String tracecodetplId) {
        return tssTracecodetplMapper.selectTssTracecodetplByTracecodetplId(tracecodetplId);
    }

    /**
     * 查询溯源码模板列表
     *
     * @param tssTracecodetpl 溯源码模板
     * @return 溯源码模板
     */
    @Override
    public List<TssTracecodetpl> selectTssTracecodetplList(TssTracecodetpl tssTracecodetpl) {
        return tssTracecodetplMapper.selectTssTracecodetplList(tssTracecodetpl);
    }

    /**
     * 新增溯源码模板
     *
     * @param tssTracecodetpl 溯源码模板
     * @return 结果
     */
    @Override
    public int insertTssTracecodetpl(TssTracecodetpl tssTracecodetpl) {
        return tssTracecodetplMapper.insertTssTracecodetpl(tssTracecodetpl);
    }

    /**
     * 修改溯源码模板
     *
     * @param tssTracecodetpl 溯源码模板
     * @return 结果
     */
    @Override
    public int updateTssTracecodetpl(TssTracecodetpl tssTracecodetpl) {
        return tssTracecodetplMapper.updateTssTracecodetpl(tssTracecodetpl);
    }

    /**
     * 批量删除溯源码模板
     *
     * @param tracecodetplIds 需要删除的溯源码模板主键
     * @return 结果
     */
    @Override
    public int deleteTssTracecodetplByTracecodetplIds(String[] tracecodetplIds) {
        return tssTracecodetplMapper.deleteTssTracecodetplByTracecodetplIds(tracecodetplIds);
    }

    /**
     * 删除溯源码模板信息
     *
     * @param tracecodetplId 溯源码模板主键
     * @return 结果
     */
    @Override
    public int deleteTssTracecodetplByTracecodetplId(String tracecodetplId) {
        return tssTracecodetplMapper.deleteTssTracecodetplByTracecodetplId(tracecodetplId);
    }

    @Override
    public String drawTraceCode(TssTracecodetpl tssTracecodetpl) {
        TssTracecodetplparam queryParam = new TssTracecodetplparam();
        queryParam.setTracecodetplId(tssTracecodetpl.getTracecodetplId());
        // 查询溯源码模板参数
        List<TssTracecodetplparam> params = tssTracecodetplparamMapper.selectTssTracecodetplparamList(queryParam);
        // 转化为画板参数
        List<GraphicsCanvasItem> graphicsCanvasItemList = params.stream().map(TssTracecodetplparam::transToGraphicsCanvasItem).collect(Collectors.toList());
        try {
            // 转为base64
            return draw(tssTracecodetpl,graphicsCanvasItemList).toBase64();
        } catch (Exception e) {
            throw new BizException(ErrorCode.TSS_TRACE_CODE_TO_BASE64_ERROR);
        }
    }
    private GraphicsCanvas draw(TssTracecodetpl tssTracecodetpl,List<GraphicsCanvasItem> graphicsCanvasItemList){
        // 声明画板
        GraphicsCanvas graphicsCanvas = new GraphicsCanvas(tssTracecodetpl.getTracecodetplWidth(), tssTracecodetpl.getTracecodetplHeight(), tssTracecodetpl.getTracecodetplBitcolor(), graphicsCanvasItemList);
        try {
            // 绘制
            graphicsCanvas.appendByGraphicsCanvasItems();
        } catch (Exception e) {
            throw new BizException(ErrorCode.TSS_TRACE_CODE_DRAW_ERROR);
        }
        return graphicsCanvas;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateTssTraceCodeTplBackgroundAndLogo(TssTracecodetplEditReqVo tssTracecodetplEditReqVo) {
        if(StringUtils.isEmpty(tssTracecodetplEditReqVo.getTracecodetplId())){
            throw new BizException(ErrorCode.TSS_TRACE_CODE_TPL_ID_IS_BLOCK);
        }
        // 更新最大扫码次数
        TssTracecodetpl tssTracecodetpl = new TssTracecodetpl();
        tssTracecodetpl.setTracecodetplId(tssTracecodetplEditReqVo.getTracecodetplId());
        tssTracecodetpl.setTracecodetplMaxscan(tssTracecodetplEditReqVo.getTracecodetplMaxscan());
        tssTracecodetplMapper.updateTssTracecodetpl(tssTracecodetpl);
        if(StringUtils.isNotEmpty(tssTracecodetplEditReqVo.getBackground())){
            // 更新背景图
            tssTracecodetplparamMapper.updateTssTracecodetplparamBusInfo(BusType.BACKGROUND.toString(),tssTracecodetpl.getTracecodetplId(),tssTracecodetplEditReqVo.getBackground());
        }
        if(StringUtils.isNotEmpty(tssTracecodetplEditReqVo.getLogo())){
            // 更新Logo
            tssTracecodetplparamMapper.updateTssTracecodetplparamBusInfo(BusType.LOGO.toString(),tssTracecodetpl.getTracecodetplId(),tssTracecodetplEditReqVo.getLogo());
        }
        return true;
    }

    @Override
    public boolean generate(TssTraceCodeGenerateReqVo tssTraceCodeGenerateReqVo) {
        String batchNo = IdUtils.fastSimpleUUID();
        List<TssTracecodeapply> list = new ArrayList<>();
        String date = DateUtils.dateTimeNow();
        String userId = SecurityUtils.getUserId();
        while(list.size()<tssTraceCodeGenerateReqVo.getNum()){
            TssTracecodeapply tssTracecodeapply = new TssTracecodeapply();
            tssTracecodeapply.setTarcecodeapplyId(tssTracecodeapply.getId());
            tssTracecodeapply.setTarcecodeapplyBatchno(batchNo);
            tssTracecodeapply.setTarcecodeapplyno(noGenerator.getSourceNo());
            tssTracecodeapply.setTracecodetplId(tssTraceCodeGenerateReqVo.getTracecodetplId());
            tssTracecodeapply.setStockId(tssTraceCodeGenerateReqVo.getStockId());
            tssTracecodeapply.setTarcecodeapplyCreator(userId);
            tssTracecodeapply.setTarcecodeapplyCreateddate(date);
            list.add(tssTracecodeapply);
        }
        return tssTracecodeapplyMapper.insertTssTracecodeapplyByBatch(list) == list.size();
    }

    @Override
    public ResponseEntity<InputStreamResource> downLand(String tarcecodeapplyBatchno)throws Exception {
        long nowTime = System.currentTimeMillis();
        log.info("当前时间{}",nowTime);
        // 查询溯源码申请单-》通过申请批次
        TssTracecodeapply tssTracecodeapplyQuery = new TssTracecodeapply();
        tssTracecodeapplyQuery.setTarcecodeapplyBatchno(tarcecodeapplyBatchno);
        List<TssTracecodeapply> tssTracecodeapplies = tssTracecodeapplyMapper.selectTssTracecodeapplyList(tssTracecodeapplyQuery);
        return drawGraphAndDowmLand(tssTracecodeapplies);
    }


    @Override
    public ResponseEntity<InputStreamResource> downLand(String[] tarcecodeapplynos)throws Exception {
        // 查询溯源码申请单-》通过指定生成单号
        List<TssTracecodeapply> tssTracecodeapplies = tssTracecodeapplyMapper.selectTssTracecodeapplyListByTarcecodeapplynos(tarcecodeapplynos);
        if(tssTracecodeapplies.isEmpty()){
            throw new BizException(ErrorCode.TSS_TRACE_APPLY_NOT_EXIST);
        }
        return drawGraphAndDowmLand(tssTracecodeapplies);
    }

    @Override
    public void export(HttpServletResponse response, String tarcecodeapplyBatchno) {
        // 查询溯源码申请单-》通过指定生成单号
        TssTracecodeapply tssTracecodeapplyQuery = new TssTracecodeapply();
        tssTracecodeapplyQuery.setTarcecodeapplyBatchno(tarcecodeapplyBatchno);
        List<TssTracecodeapply> tssTracecodeapplies = tssTracecodeapplyMapper.selectTssTracecodeapplyList(tssTracecodeapplyQuery);
        if(tssTracecodeapplies.isEmpty()){
            throw new BizException(ErrorCode.TSS_TRACE_APPLY_NOT_EXIST);
        }
        // 查询溯源码模板-》每一批的溯源码模板相同，通过申请单中的模板标识
        TssTracecodetpl tssTracecodetpl = tssTracecodetplMapper.selectTssTracecodetplByTracecodetplId(tssTracecodeapplies.get(0).getTracecodetplId());
        // 查询溯源码模板参数
        TssTracecodetplparam queryParam = new TssTracecodetplparam();
        queryParam.setTracecodetplId(tssTracecodetpl.getTracecodetplId());
        List<TssTracecodetplparam> params = tssTracecodetplparamMapper.selectTssTracecodetplparamList(queryParam);
        LinkedHashMap<String,TssTracecodetplparam> paramsMap = params.stream().collect(Collectors.toMap(TssTracecodetplparam::getTplparamBustype, ObjectUtil::clone, (a, b) -> b,LinkedHashMap::new));
        String url = paramsMap.get(BusType.TRACING_QR_CODE.name()).getTplparamData() + "?tarcecodeapplyno=" ;
        LinkedList<TssTracecodeapplyExportResVo> data = new LinkedList<>();
        for(TssTracecodeapply tssTracecodeapply : tssTracecodeapplies){
            TssTracecodeapplyExportResVo tssTracecodeapplyExportResVo = new TssTracecodeapplyExportResVo();
            tssTracecodeapplyExportResVo.setCodeUrl(url + tssTracecodeapply.getTarcecodeapplyno());
            tssTracecodeapplyExportResVo.setTarcecodeapplyno(tssTracecodeapply.getTarcecodeapplyno());
            data.add(tssTracecodeapplyExportResVo);
        }
        ExcelUtil<TssTracecodeapplyExportResVo> util = new ExcelUtil<TssTracecodeapplyExportResVo>(TssTracecodeapplyExportResVo.class);
        util.exportExcel(response, data, "溯源码信息");

    }

    public ResponseEntity<InputStreamResource> drawGraphAndDowmLand(List<TssTracecodeapply> tssTracecodeapplies) throws Exception{
        // 查询溯源码模板-》每一批的溯源码模板相同，通过申请单中的模板标识
        TssTracecodetpl tssTracecodetpl = tssTracecodetplMapper.selectTssTracecodetplByTracecodetplId(tssTracecodeapplies.get(0).getTracecodetplId());
        // 查询溯源码模板参数
        TssTracecodetplparam queryParam = new TssTracecodetplparam();
        queryParam.setTracecodetplId(tssTracecodetpl.getTracecodetplId());
        List<TssTracecodetplparam> params = tssTracecodetplparamMapper.selectTssTracecodetplparamList(queryParam);
        WmsFinishgoodsSourceInfo wmsFinishgoodsSourceInfo = wmsFinishgoodsMapper.selectWmsFinishgoodsSourceInfo(tssTracecodeapplies.get(0).getStockId());
        // 转为map,方便替换参数，提升效率
        // 准备要绘制的多个图片数据
        List<CompletableFuture<byte[]>> completableFutureList = new LinkedList<>();
        for(TssTracecodeapply tssTracecodeapply : tssTracecodeapplies){
            TssTracecodeapply paramApply = ObjectUtil.clone(tssTracecodeapply);
            TssTracecodetpl paramTpl = ObjectUtil.clone(tssTracecodetpl);
            // 生成线程进行图像绘制
            CompletableFuture<byte[]> generateGraphics = CompletableFuture.supplyAsync(() -> drawGraph(paramTpl,params,wmsFinishgoodsSourceInfo,paramApply));
            completableFutureList.add(generateGraphics);
        }
        CompletableFuture<Void> allFutures = CompletableFuture.allOf(completableFutureList.toArray(new CompletableFuture[0]));
        allFutures.get();
        List<byte[]> imageList = new LinkedList<>();
        for(CompletableFuture<byte[]> completableFuture: completableFutureList){
            imageList.add(completableFuture.get());
        }
        // 打包多张图片为 Zip 格式压缩文件
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ZipOutputStream zos = new ZipOutputStream(baos);
        for (int i = 0; i < imageList.size(); i++) {
            byte[] bytes = imageList.get(i);
            ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
            ZipEntry entry = new ZipEntry(tssTracecodeapplies.get(i).getTarcecodeapplyno()+ ".png");
            zos.putNextEntry(entry);
            byte[] buffer = new byte[1024];
            int len;
            while ((len = bais.read(buffer)) > 0) {
                zos.write(buffer, 0, len);
            }
            zos.closeEntry();
            bais.close();
        }
        zos.close();
        byte[] zipBytes = baos.toByteArray();
        // 设置下载相关的 HTTP 响应头信息
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        headers.setContentDispositionFormData("attachment", "溯源码.zip");
        headers.setContentLength(zipBytes.length);
        // 返回压缩文件字节流给用户进行下载
        InputStream inputStream = new ByteArrayInputStream(zipBytes);
        InputStreamResource resource = new InputStreamResource(inputStream);
        return ResponseEntity.ok()
                .headers(headers)
                .contentLength(zipBytes.length)
                .contentType(MediaType.APPLICATION_OCTET_STREAM)
                .body(resource);
    }

    public byte[] drawGraph(TssTracecodetpl tssTracecodetpl,List<TssTracecodetplparam> params, WmsFinishgoodsSourceInfo wmsFinishgoodsSourceInfo,TssTracecodeapply tssTracecodeapply){
        try {
            // 将生成的图片数据保存到字节数组中
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            LinkedHashMap<String,TssTracecodetplparam> paramsMap = params.stream().collect(Collectors.toMap(TssTracecodetplparam::getTplparamBustype, ObjectUtil::clone, (a, b) -> b,LinkedHashMap::new));
            // 替换产品名称和采收时间
            paramsMap.get(BusType.GOODS_NAME.name()).setTplparamData("商品名称:"+wmsFinishgoodsSourceInfo.getFinishgoodscodeContext());
            if(StringUtils.isBlank(wmsFinishgoodsSourceInfo.getPickDate())){
                paramsMap.get(BusType.PICK_TIME.name()).setTplparamData("采收时间:"+DateUtils.turnDateFormat(wmsFinishgoodsSourceInfo.getFinishgoodCreateddate(),DateUtils.YYYY_MM_DD_HH_MM_SS,"yyyy.MM.dd"));
            }else {
                paramsMap.get(BusType.PICK_TIME.name()).setTplparamData("采收时间:"+DateUtils.turnDateFormat(wmsFinishgoodsSourceInfo.getPickDate(),DateUtils.YYYY_MM_DD_HH_MM_SS,"yyyy.MM.dd"));
            }
            paramsMap.get(BusType.TRACING_NO.name()).setTplparamData(tssTracecodeapply.getTarcecodeapplyno());
            paramsMap.get(BusType.TRACING_QR_CODE.name()).setTplparamData(paramsMap.get(BusType.TRACING_QR_CODE.name()).getTplparamData() + "?tarcecodeapplyno=" +  paramsMap.get(BusType.TRACING_NO.name()).getTplparamData());
            paramsMap.get(BusType.TRACING_BARCODE.name()).setTplparamData(tssTracecodeapply.getTarcecodeapplyno());
            // 转化为画板参数
            List<GraphicsCanvasItem> graphicsCanvasItemList = paramsMap.values().stream().map(TssTracecodetplparam::transToGraphicsCanvasItem).collect(Collectors.toList());
            draw(tssTracecodetpl,graphicsCanvasItemList).write("png",baos);
            return baos.toByteArray();
        } catch (Exception e) {
            throw new BizException(ErrorCode.TSS_TRACE_CODE_OUT_TO_STREAM_ERROR);
        }
    }

}
