package com.neusoft.smart.pos.printer.service;

import com.neusoft.smart.pos.constants.DictionaryConstant;
import com.neusoft.smart.pos.dto.printer.redis.PrinterBean;
import com.neusoft.smart.pos.dto.printer.redis.PrinterTaskBean;
import com.neusoft.smart.pos.dto.printer.request.PostPrinterOrderRequest;
import com.neusoft.smart.pos.dto.printer.response.GetPrinterResponse;
import com.neusoft.smart.pos.dto.printer.response.GetPrinterResponse.PrinterInfoGoodsResponse;
import com.neusoft.smart.pos.dto.printer.response.GetPrinterResponse.PrinterInfoRuleAttributeResponse;
import com.neusoft.smart.pos.dto.printer.response.GetPrinterResponse.PrinterInfoRuleResponse;
import com.neusoft.smart.pos.dto.printer.response.GetPrinterResponse.PrinterInfoTemplateResponse;
import com.neusoft.smart.pos.dto.printer.response.PostPrinterOrderResponse;
import com.neusoft.smart.pos.framework.exception.BusinessCommonException;
import com.neusoft.smart.pos.framework.utils.BeanUtil;
import com.neusoft.smart.pos.framework.utils.ListUtils;
import com.neusoft.smart.pos.printer.constants.PrinterErrorCode;
import com.neusoft.smart.pos.printer.persistent.domain.*;
import com.neusoft.smart.pos.printer.persistent.ext.domain.Printer;
import com.neusoft.smart.pos.printer.persistent.ext.domain.PrinterObject;
import com.neusoft.smart.pos.printer.repository.*;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;

@Service("printerService")
public class PrinterService {

    @Autowired
    private PrinterGoodsRefInfoRepository printerGoodsRefInfoRepository;

    @Autowired
    private PrinterInfoRepository printerInfoRepository;

    @Autowired
    private PrinterRuleService printerRuleService;

    @Autowired
    private PrinterTemplateService printerTemplateService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private PrintQueueHandleService printQueueHandleService;

    @Autowired
    private PrinterRedisService printerRedisService;
    
    @Autowired
    private PrinterTemplateRefRepository printerTemplateRefRepository;
    
    @Autowired
    private PrinterTemplateRepository printerTemplateRepository;
    
    @Autowired
    private PrinterRuleRefRepository printerRuleRefRepository;
    
    @Autowired
    private PrinterRuleAttributeRepository printerRuleAttributeRepository;
    
    @Autowired
    private PrinterRuleRepository printerRuleRepository;
    
    @Autowired
    private PrinterTaskInfoRepository printerTaskInfoRepository;
    
    /**
     * 订单支付状态查询方法
     *
     * @return
     */
    public PostPrinterOrderResponse printerOrder(PostPrinterOrderRequest printerOrderRequest) {
        Integer orgId = printerOrderRequest.getOrgId();
        Integer shopId = printerOrderRequest.getShopId();

        //1.处理打印数据
        PrinterObject printerObject = printerDataHandle(printerOrderRequest);

        //2.查询可用打印机信息,并将打印菜品按打印机分组
        groupingGoodsByPrinterHandle(orgId, shopId, printerObject);

        //3.执行打印规则
        executePrinterRuleHandle(orgId, shopId, printerObject);

        //4.执行打印模板
        List<PrinterTaskBean> printerTaskBeanList = executePrinterTemplateHandle(orgId, shopId, printerObject);

        //5.将打印信息推送至打印队列
        //是否支持重复打印，0不支持，1支持
        if (printerOrderRequest.getRepeatPrinter().equals("0") && !StringUtils.isEmpty(printerOrderRequest.getOrderNumber())) {
            //判断当前订单打印任务是否已经存在，如果存在则不再继续推送
            List<PrinterTaskInfo> printerTaskInfoList = printerTaskInfoRepository.getTaskByOrderNumber(printerOrderRequest.getOrderNumber());
            if(null == printerTaskInfoList || printerTaskInfoList.size() == 0){
                printQueueHandleService.pushTasks(printerTaskBeanList);
            }
        }else{
            printQueueHandleService.pushTasks(printerTaskBeanList);
        }

        //6.整理返回数据
        PostPrinterOrderResponse printerOrderResponse = new PostPrinterOrderResponse();
        //请求状态@0:失败@1:成功
        printerOrderResponse.setRequestStatus("1");
        return printerOrderResponse;
    }

    /**
     * 打印数据处理方法
     *
     * @return
     */
    private PrinterObject printerDataHandle(PostPrinterOrderRequest printerOrderRequest) {
        PrinterObject printerObject = new PrinterObject();
        //1.存储原始打印对象
        printerObject.setOriginalTarget(printerOrderRequest);

        //2.声明打印目标对象
        PrinterObject.PrinterTarget printerTarget = new PrinterObject.PrinterTarget();

        //3.声明打印对象
        Printer printer = new Printer();
        BeanUtils.copyProperties(printerOrderRequest, printer);

        //4.声明打印明细对象
        List<Printer.PrinterDetail> printerDetails = ListUtils.convertListToList(printerOrderRequest.orderDetails, Printer.PrinterDetail.class);

        if (null != printer) {
            printer.setPrinterDetails(printerDetails);
        }
        printerTarget.getPrinters().add(printer);
        printerObject.setPrinterTarget(printerTarget);
        return printerObject;
    }

    /**
     * 执行打印规则使用方法
     */
    private void executePrinterRuleHandle(Integer orgId, Integer shopId, PrinterObject printerObject) {
        printerRuleService.executeRule(orgId, shopId, printerObject);
    }

    /**
     * 执行打印模板使用方法
     */
    private List<PrinterTaskBean> executePrinterTemplateHandle(Integer orgId, Integer shopId, PrinterObject printerObject) {
        return printerTemplateService.executeTemplate(orgId, shopId, printerObject);
    }

    /**
     * 查询可用打印机信息
     */
    private void groupingGoodsByPrinterHandle(Integer orgId, Integer shopId, PrinterObject printerObject) {
        //1.查询传入菜品可用打印机列表信息
        List<Integer> goodsIds = new ArrayList<>();
        List<Integer> categoryIds = new ArrayList<>();
        Printer printer = printerObject.getPrinterTarget().getPrinters().get(0);
        printer.getPrinterDetails().forEach(printerDetail -> {
            goodsIds.add(printerDetail.getItemNumber());
            categoryIds.add(Integer.parseInt(printerDetail.getCategoryNumber()));
        });
        
        //2.根据打印模板类型查询可用打印机ID
        List<Long> printerIds = getPrinterIdsByTemplateTypes(orgId,shopId,printer.getTemplateType());
        if(null == printerIds || printerIds.size() == 0){
            printerObject.getPrinterTarget().setPrinters(new ArrayList<Printer>());
            return;
        }
        
        List<PrinterGoodsRefInfo> printerGoodsRefInfos = printerGoodsRefInfoRepository.queryPrinterGoodsRefInfoByRelationshipIds(orgId, shopId, goodsIds, categoryIds,printerIds);

        //3.根据菜品处理打印数据，根据打印机进行分组
        List<Printer> printers = doGroupingGoods(printerGoodsRefInfos, printer);

        //4.存入处理后的打印分组数据
        printerObject.getPrinterTarget().setPrinters(printers);
    }
    
    /**
     * 根据打印模板类型获取可用打印机ID列表
     * @param templateType
     * @return
     */
    private List<Long> getPrinterIdsByTemplateTypes(Integer orgId,Integer shopId,String templateType){
        List<Long> printerIdList = new ArrayList<>();
        //1.根据模板类型查询系统字典模板列表
        List<PrinterTemplate> printerTemplateList = printerTemplateRepository.queryPrinterTemplateList(DictionaryConstant.IS_ENABLE_QY.getCode(),templateType);
        List<Long> templateIds = new ArrayList<>();
        if(null != printerTemplateList && printerTemplateList.size() > 0){
            printerTemplateList.forEach(printerTemplate -> {
                templateIds.add(printerTemplate.getId());
            });
        }
        
        //2.根据集团门店ID查询当前门店可用打印机列表
        List<Long> printerIds = new ArrayList<>();
        List<PrinterInfo> printerInfoList = printerInfoRepository.queryAll(orgId,shopId,DictionaryConstant.PRINTER_STATUS_1.getCode());
        if(null != printerInfoList && printerInfoList.size() > 0){
            printerInfoList.forEach(printerInfo -> {
                printerIds.add(printerInfo.getId());
            });
        }
        
        //3.根据字典模板ID列表打印机ID列表查询集团门店可用模板列表
        List<PrinterTemplateRef> printerTemplateRefList = printerTemplateRefRepository.queryPrinterTemplateListByTemplateId(templateIds,printerIds);
        if(null != printerTemplateRefList && printerTemplateRefList.size() > 0){
            printerTemplateRefList.forEach(printerTemplateRef -> {
                printerIdList.add(printerTemplateRef.getPrinterId());
            });
        }
        return printerIdList;
    }
    
    /**
     * 进行打印机分组操作
     *
     * @param printerGoodsRefInfos
     * @param printer
     * @return
     */
    private List<Printer> doGroupingGoods(List<PrinterGoodsRefInfo> printerGoodsRefInfos, Printer printer) {
        //1.将List根据printerId进行分组
        Map<Long, List<PrinterGoodsRefInfo>> printerGoodsRefInfoMap = new HashMap<>();
        for (PrinterGoodsRefInfo printerGoodsRefInfo : printerGoodsRefInfos) {
            List<PrinterGoodsRefInfo> tempList = printerGoodsRefInfoMap.get(printerGoodsRefInfo.getPrinterId());
            /*如果取不到数据,那么直接new一个空的ArrayList**/
            if (tempList == null) {
                tempList = new ArrayList<>();
                tempList.add(printerGoodsRefInfo);
                printerGoodsRefInfoMap.put(printerGoodsRefInfo.getPrinterId(), tempList);
            } else {
                /*之前已经存放过了,则直接追加数据到原来的List里**/
                tempList.add(printerGoodsRefInfo);
            }
        }

        //2.分组后将打印数据进行组装
        List<Printer> printerList = new ArrayList<>();

        for (Long printerId : printerGoodsRefInfoMap.keySet()) {
            Printer printerTemp = new Printer();
            BeanUtils.copyProperties(printer, printerTemp);
            PrinterInfo printerInfo = printerInfoRepository.queryPrinterByPrinterId(printerId);
            printerTemp.setPrinterId(printerId);
            printerTemp.setPrinterSn(printerInfo.getPrinterSn());
            printerTemp.setDeviceType(printerInfo.getDeviceType());
            List<PrinterGoodsRefInfo> printerGoodsRefInfoTempList = printerGoodsRefInfoMap.get(printerId);
            List<Printer.PrinterDetail> printerDetailsTemp = new ArrayList<>();
            for (PrinterGoodsRefInfo printerGoodsRefInfoTemp : printerGoodsRefInfoTempList) {
                for (Printer.PrinterDetail printerDetailTemp : printer.getPrinterDetails()) {
                    if (printerGoodsRefInfoTemp.getRelationshipType().equals(DictionaryConstant.PRINTER_RELATIONSHIP_TYPE_1.getCode())) {
                        if (printerGoodsRefInfoTemp.getRelationshipId().equals(printerDetailTemp.getItemNumber())) {
                            printerDetailsTemp.add(printerDetailTemp);
                        }
                    } else if (printerGoodsRefInfoTemp.getRelationshipType().equals(DictionaryConstant.PRINTER_RELATIONSHIP_TYPE_2.getCode())) {
                        if (printerGoodsRefInfoTemp.getRelationshipId().toString().equals(printerDetailTemp.getCategoryNumber())) {
                            printerDetailsTemp.add(printerDetailTemp);
                        }
                    }
                }
            }
            removeDuplicate(printerDetailsTemp);
            printerTemp.setPrinterDetails(printerDetailsTemp);
            printerList.add(printerTemp);
        }
        return printerList;
    }

    /**
     * 去除List中重复商品信息
     *
     * @param printerDetails
     * @return
     */
    public static void removeDuplicate(List<Printer.PrinterDetail> printerDetails) {
        for (int i = 0; i < printerDetails.size() - 1; i++) {
            for (int j = printerDetails.size() - 1; j > i; j--) {
                if (printerDetails.get(j).getItemNumber().equals(printerDetails.get(i).getItemNumber())) {
                    printerDetails.remove(j);
                }
            }
        }
    }

    /**
     * 轮询取出打印任务所用
     *
     * @param sn
     * @param peekCount
     * @return
     */
    public List<Map<String, Object>> pollTasks(String sn, Integer peekCount) {
        List<Map<String, Object>> result = new ArrayList<>();
        //正常来说一个sn只能有一个redis数据
        Set<String> keySet = redisTemplate.keys("printer_*_" + sn);
        if (!keySet.isEmpty()) {
            //取出中转设备
            BoundValueOperations<String, PrinterBean> boundValueOperations = redisTemplate.boundValueOps(keySet.iterator().next());
            PrinterBean printerBean = boundValueOperations.get();
            //TODO 打印可能会有一个最大打印设置,以后再说
            printerBean.getLinkedPrinters().forEach(printer -> {
                Map<String, Object> map = new HashMap<>();
                map.put("id", printer.getId());
                map.put("task", printQueueHandleService.peekTasks(printer.getId().intValue(), peekCount));
                result.add(map);
            });

            return result;
        } else {
            throw new BusinessCommonException(PrinterErrorCode.INVALIDATE_SN_ERROR);
        }
    }

    /**
     * 设置打印任务已打印
     *
     * @param printerId
     * @param taskId
     */
    public void setTaskPrinted(Integer printerId, Integer taskId) {
        printQueueHandleService.removeTask(printerId, taskId);

    }

    /**
     * 通过sn获得链接的打印设备
     *
     * @param sn
     * @return
     */
    public List<PrinterBean> getLinkedPrinters(String sn) {
        return printerRedisService.getBySn(sn).getLinkedPrinters();
    }
    
    /**
     * 查询打印机信息使用方法
     * @return
     */
    public GetPrinterResponse queryPrinterDetail(Long printerId) {
        GetPrinterResponse printerResponse = new GetPrinterResponse();
        //1.查询打印机信息
        PrinterInfo printerInfo = printerInfoRepository.queryPrinterByPrinterId(printerId);
        BeanUtils.copyProperties(printerInfo,printerResponse);
        
        //2.查询打印机模板信息
        List<PrinterInfoTemplateResponse> printerInfoTemplateList = getPrinterTemplateHandle(printerId);
        printerResponse.setPrinterTemplateList(printerInfoTemplateList);
        
        //3.查询打印机规则信息
        List<PrinterInfoRuleResponse> printerInfoRuleList = getPrinterRuleListHandle(printerId);
        printerResponse.setPrinterRuleList(printerInfoRuleList);
        
        //4.查询打印机菜品信息
        List<PrinterInfoGoodsResponse> printerGoodsList = getPrinterGoodsListHandle(printerId);
        printerResponse.setPrinterGoodsList(printerGoodsList);
    
        //4.查询打印机菜品信息
        printerResponse.setLinkedPrinters(BeanUtil.parseList(printerInfoRepository.queryPrinterByParentId(printerInfo.getId()), GetPrinterResponse.class));
        return printerResponse;
    }
    
    /**
     * 打印机模板查询方法
     * @param printerId
     * @return
     */
    private List<PrinterInfoTemplateResponse> getPrinterTemplateHandle(Long printerId){
        List<PrinterInfoTemplateResponse> printerInfoTemplateList = new ArrayList<>();
        //1.查询打印机关系表数据信息
        List<PrinterTemplateRef> printerTemplateRefList = printerTemplateRefRepository.queryPrinterTemplateListByPrinterId(printerId);
        
        if(null != printerTemplateRefList){
            for(int i=0;i<printerTemplateRefList.size();i++){
                PrinterInfoTemplateResponse printerInfoTemplate = new PrinterInfoTemplateResponse();
                //2.查询打印机模板数据信息
                PrinterTemplate printerTemplate = printerTemplateRepository.queryPrinterTemplateByTemplateId(printerTemplateRefList.get(i).getTemplateId());
                //3.整理返回数据
                BeanUtils.copyProperties(printerTemplate,printerInfoTemplate);
                printerInfoTemplate.setPrinterId(printerId);
                printerInfoTemplate.setTemplateId(printerTemplate.getId());
                DictionaryConstant dict = DictionaryConstant.getEnumByCode(DictionaryConstant.PRINTER_TEMPLATE_TYPE.getCode(), printerTemplate.getTemplateType());
                printerInfoTemplate.setTemplateTypeName(dict.getValue());
                printerInfoTemplateList.add(printerInfoTemplate);
            }
        }
        return printerInfoTemplateList;
    }
    
    /**
     * 打印机菜品列表查询方法
     * @param printerId
     * @return
     */
    private List<PrinterInfoGoodsResponse> getPrinterGoodsListHandle(Long printerId){
        //1.查询打印机商品关系表数据信息
        List<PrinterGoodsRefInfo> printerGoodsRefInfoList = printerGoodsRefInfoRepository.queryPrinterGoodsRefInfoByPrinterId(printerId);
        List<PrinterInfoGoodsResponse> printerGoodsList = ListUtils.convertListToList(printerGoodsRefInfoList,PrinterInfoGoodsResponse.class);
        return printerGoodsList;
    }
    
    /**
     * 打印机菜品列表查询方法
     * @param printerId
     * @return
     */
    private List<PrinterInfoRuleResponse> getPrinterRuleListHandle(Long printerId){
        List<PrinterInfoRuleResponse> printerRuleList = new ArrayList<>();
        //1.查询打印机规则关系表数据信息
        List<PrinterRuleRef> printerRuleRefList = printerRuleRefRepository.queryPrinterRefRulesByPrinterId(printerId);
        if(null != printerRuleRefList && printerRuleRefList.size() > 0){
            for(int i=0;i<printerRuleRefList.size();i++){
                PrinterRuleRef printerRuleRef = printerRuleRefList.get(i);
                PrinterInfoRuleResponse printerInfoRule = new PrinterInfoRuleResponse();
                BeanUtils.copyProperties(printerRuleRef,printerInfoRule);
                //查询规则值信息
                printerInfoRule.setRuleName(printerRuleRepository.queryPrinterRulesByRuleId(printerRuleRef.getRuleId()).getRuleName());
                printerInfoRule.setPrinterRuleAttributList(getPrinterRuleAttributeListHandle(printerRuleRef.getId()));
                printerRuleList.add(printerInfoRule);
            }
        }
        return printerRuleList;
    }
    
    /**
     * 打印机菜品列表查询方法
     * @return
     */
    private List<PrinterInfoRuleAttributeResponse> getPrinterRuleAttributeListHandle(Long printerRuleId){
        List<PrinterRuleAttribute> printerRuleAttributeList = printerRuleAttributeRepository.queryPrinterRuleAttributeByRuleRefId(printerRuleId);
        List<PrinterInfoRuleAttributeResponse> ruleAttributeList = ListUtils.convertListToList(printerRuleAttributeList,PrinterInfoRuleAttributeResponse.class);
        ruleAttributeList.forEach(ruleAttribute->{
            ruleAttribute.setAttributeName(printerRuleRepository.queryRuleAttributeById(ruleAttribute.attributeId).getAttributeName());
            ruleAttribute.setAttributeValueName(printerRuleRepository.queryRuleAttributeValue(ruleAttribute.attributeId,ruleAttribute.getAttributeValue()).getAttributeLabel());
        });
        
        return ruleAttributeList;
    }
}
