package com.czg.service.order.print;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.czg.account.dto.HandoverRecordDTO;
import com.czg.account.dto.PrintOrderDetailDTO;
import com.czg.account.entity.PrintMachine;
import com.czg.account.entity.ShopInfo;
import com.czg.account.entity.ShopStaff;
import com.czg.account.service.*;
import com.czg.market.service.OrderInfoService;
import com.czg.order.entity.OrderDetail;
import com.czg.order.entity.OrderInfo;
import com.czg.order.service.OrderDetailService;
import com.czg.order.service.PrintMachineLogService;
import com.czg.print.*;
import com.czg.product.entity.ProdSku;
import com.czg.product.service.ProdSkuService;
import com.czg.service.RedisService;
import com.czg.service.order.enums.OrderStatusEnums;
import com.czg.system.service.SysParamsService;
import com.mybatisflex.core.query.QueryWrapper;
import jakarta.annotation.Resource;
import lombok.Getter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.web.client.RestTemplate;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;

/**
 * @author Administrator
 */
@Slf4j
@ToString
public abstract class PrinterHandler {
    public String printerBrand;
    @Resource
    protected PrintConfig printConfig;
    @Resource
    protected RestTemplate restTemplate;
    @Resource
    protected OrderDetailService orderDetailService;
    @Resource
    protected OrderInfoService orderInfoService;
    @Resource
    protected RedisService redisService;
    @Resource
    protected PrintMachineLogService printMachineLogService;
    @DubboReference
    protected SysParamsService sysParamsService;
    @DubboReference
    protected ShopUserService shopUserService;
    @DubboReference
    protected CallQueueService callQueueService;
    @DubboReference
    protected CallTableService callTableService;
    @DubboReference
    protected ShopInfoService shopInfoService;
    @DubboReference
    private ShopTableService shopTableService;
    @DubboReference
    private ShopStaffService shopStaffService;
    @DubboReference
    protected ProdSkuService prodSkuService;
    @DubboReference
    private HandoverRecordRpcService handoverRecordService;

    @Getter
    public enum PrintTypeEnum {
        GUEST_ORDER("客看单"),
        PRE_ORDER("预结算单"),
        ORDER("订单结算单"),
        RETURN_ORDER("退菜单"),
        REFUND_ORDER("退款单"),

        ALL_KITCHEN("后厨整单"),
        ONLY_KITCHEN("后厨分单"),
        REFUND_KITCHEN("后厨退菜单"),

        HANDOVER("交班单"),
        CALL("排队取号"),
        RECHARGE("储值单"),
        STOCK("出入库单"),
        STOCK_CHECK("盘点单"),
        PRODUCT_REPORT("商品报表"),
        DAY_REPORT("经营日报"),
        DAY_ORDER("日结单");

        private final String name;

        PrintTypeEnum(String name) {
            this.name = name;
        }
    }


    public PrinterHandler(String printerBrand) {
        this.printerBrand = printerBrand;
    }


    /**
     * 打印预结算单「前台」
     */
    public void preOrderPrintBefore(PrintMachine machine, OrderInfo orderInfo, List<OrderDetail> orderDetail) {
        // 判断订单是否是先付费或者已结算
        if (OrderStatusEnums.CANCELLED.getCode().equals(orderInfo.getStatus())) {
            log.warn("已取消, 订单信息: {}", orderInfo);
        }

        if (orderDetail.isEmpty()) {
            log.info("待打印列表为空");
            return;
        }
        List<PrintOrderDetailDTO> detailList = new ArrayList<>();
        orderDetail.forEach(it -> {
            Integer isTemporary = ObjectUtil.defaultIfNull(it.getIsTemporary(), 0);
            String remark = "";
            if (isTemporary == 0) {
                ProdSku prodSku = prodSkuService.getById(it.getSkuId());
                if (ObjectUtil.isNotEmpty(prodSku) && ObjectUtil.isNotEmpty(prodSku.getSpecInfo())) {
                    remark = prodSku.getSpecInfo();
                }
            }
            Integer isWaitCall = ObjectUtil.defaultIfNull(it.getIsWaitCall(), 0);
            if (isWaitCall == 1) {
                it.setProductName("【等叫】%s".formatted(it.getProductName()));
            }
            if (isTemporary == 1) {
                it.setProductName("【临】%s".formatted(it.getProductName()));
            }
            it.setPackAmount(it.getPackAmount() == null ? BigDecimal.ZERO : it.getPackAmount());
            boolean isGift = it.getIsGift() != null && it.getIsGift() == 1;
            if (isGift) {
                it.setProductName("【赠】%s".formatted(it.getProductName()));
            }
            BigDecimal unitPrice = it.getPrice();
            if (isGift) {
                unitPrice = BigDecimal.ZERO;
            }
            PrintOrderDetailDTO detail = new PrintOrderDetailDTO(it.getProductName(), it.getNum().toString(), unitPrice.stripTrailingZeros().toPlainString(), remark, it.getProGroupInfo());
            detailList.add(detail);

        });

        if (!detailList.isEmpty()) {
            preOrderPrint(orderInfo, machine, orderDetail);
        }
    }


    /**
     * 仅打印结算单「前台」
     */
    public void orderPrintBefore(PrintMachine machine, OrderInfo orderInfo, List<OrderDetail> tbOrderDetailList) {
        // 判断订单是否是先付费或者已结算
        if (OrderStatusEnums.UNPAID.getCode().equals(orderInfo.getStatus()) || OrderStatusEnums.CANCELLED.getCode().equals(orderInfo.getStatus())) {
            log.warn("此订单未支付或已取消, 订单信息: {}", orderInfo);
//            return;
        }

        if (tbOrderDetailList.isEmpty()) {
            log.info("待打印列表为空");
            return;
        }
        List<PrintOrderDetailDTO> detailList = new ArrayList<>();
        tbOrderDetailList.forEach(it -> {
            Integer isTemporary = ObjectUtil.defaultIfNull(it.getIsTemporary(), 0);
            String remark = "";
            if (isTemporary == 0) {
                ProdSku prodSku = prodSkuService.getById(it.getSkuId());
                if (ObjectUtil.isNotEmpty(prodSku) && ObjectUtil.isNotEmpty(prodSku.getSpecInfo())) {
                    remark = prodSku.getSpecInfo();
                }
            }
            Integer isWaitCall = ObjectUtil.defaultIfNull(it.getIsWaitCall(), 0);
            if (isWaitCall == 1) {
                it.setProductName("【等叫】%s".formatted(it.getProductName()));
            }
            if (isTemporary == 1) {
                it.setProductName("【临】%s".formatted(it.getProductName()));
            }
            it.setPackAmount(it.getPackAmount() == null ? BigDecimal.ZERO : it.getPackAmount());
            boolean isGift = it.getIsGift() != null && it.getIsGift() == 1;
            if (isGift) {
                it.setProductName("【赠】%s".formatted(it.getProductName()));
            }
            BigDecimal unitPrice = it.getPrice();
            if (isGift) {
                unitPrice = BigDecimal.ZERO;
            }
            PrintOrderDetailDTO detail = new PrintOrderDetailDTO(it.getProductName(), it.getNum().toString(), unitPrice.stripTrailingZeros().toPlainString(), remark, it.getProGroupInfo());
            detailList.add(detail);
        });
        if (!detailList.isEmpty()) {
            orderPrint(orderInfo, machine, tbOrderDetailList);
        }
    }

    /**
     * 打印排队小票
     */
    public void callNumPrintBefore(PrintMachine machine, String shopName, CallTablePrintDTO queue) {
        if (queue == null || queue.getCallQueue() == null || queue.getCallTable() == null) {
            log.warn("叫号记录不存在");
            return;
        }
        callNumPrint(machine, shopName, queue);
    }

    /**
     * 获取取餐号
     */
    protected static String getPickupNum(OrderInfo orderInfo) {
        return StrUtil.isBlank(orderInfo.getTableName()) ? orderInfo.getTakeCode() : orderInfo.getTableName();
    }


    /**
     * 打印退菜/退款单
     */
    protected OrderPrintDTO returnOrderPrint(String printTitle, String operator, String refundAmount, String refundReason, String refundType,
                                             OrderInfo orderInfo, PrintMachine machine, List<OrderDetail> detailList) {
        ShopInfo shopInfo = shopInfoService.getById(orderInfo.getShopId());
        OrderPrintDTO orderPrintDTO = new OrderPrintDTO().setShopName(shopInfo.getShopName()).setPrintType("收银-堂食")
                .setOrderNo(orderInfo.getOrderNo())
                .setTradeDate(DateUtil.format(orderInfo.getCreateTime(), "yyyy/MM/dd HH:mm:ss"))
                .setOperator(operator)
                .setReturn(true)
                .setPrintTitle(printTitle);
        orderPrintDTO.setSeatNum(orderInfo.getSeatNum() == null ? "" : orderInfo.getSeatNum().toString());
        orderPrintDTO.setPickupNum(orderInfo.getTableName());
        orderPrintDTO.setRefundAmount(refundAmount);
        orderPrintDTO.setRefundReason(refundReason);
        orderPrintDTO.setRefundType("cash".equals(refundType) ? "线下退款" : "原路退回");
        return orderPrintDTO;
    }

    /**
     * 打印客看单
     */
    protected OrderPrintDTO guestOrderPrint(OrderInfo orderInfo, PrintMachine machine, List<OrderDetail> detailList) {
        OrderPrintDTO orderPrintDTO = getPrintInfoDTO(orderInfo, "客看单");

        BigDecimal originalAmount = BigDecimal.ZERO;
        for (OrderDetail orderDetail : detailList) {
            if (orderDetail.getIsGift() == 1) {
            } else if (orderDetail.getDiscountSaleAmount().compareTo(BigDecimal.ZERO) > 0) {
                originalAmount = originalAmount.add((orderDetail.getNum().subtract(orderDetail.getReturnNum()).subtract(orderDetail.getRefundNum())).multiply(orderDetail.getUnitPrice()));
            } else {
                originalAmount = originalAmount.add((orderDetail.getNum().subtract(orderDetail.getReturnNum()).subtract(orderDetail.getRefundNum())).multiply(orderDetail.getPrice()));
            }

            Integer isTemporary = ObjectUtil.defaultIfNull(orderDetail.getIsTemporary(), 0);
            Integer isWaitCall = ObjectUtil.defaultIfNull(orderDetail.getIsWaitCall(), 0);
            if (isWaitCall == 1) {
                orderDetail.setProductName("【等叫】%s".formatted(orderDetail.getProductName()));
            }
            if (isTemporary == 1) {
                orderDetail.setProductName("【临】%s".formatted(orderDetail.getProductName()));
            }
            orderDetail.setPackAmount(orderDetail.getPackAmount() == null ? BigDecimal.ZERO : orderDetail.getPackAmount());
            boolean isGift = orderDetail.getIsGift() != null && orderDetail.getIsGift() == 1;
            if (isGift) {
                orderDetail.setProductName("【赠】%s".formatted(orderDetail.getProductName()));
            }
        }
//        log.info("客看单 第{}次下单--------------------------------------------", orderInfo.getPlaceNum());
        if (orderInfo.getPlaceNum() != null && orderInfo.getPlaceNum() != 1) {
            orderInfo.setSeatAmount(BigDecimal.ZERO);
            orderPrintDTO.setSeatAmount("");
        }
        orderPrintDTO.setOriginalAmount((originalAmount.add(orderInfo.getSeatAmount()).add(orderInfo.getPackFee()))
                .setScale(2, RoundingMode.HALF_UP).toPlainString());
        orderPrintDTO.setPayAmount((originalAmount.add(orderInfo.getSeatAmount()).add(orderInfo.getPackFee())
                .subtract(orderInfo.getDiscountAllAmount())).setScale(2, RoundingMode.HALF_UP).toPlainString());

        if (orderInfo.getPackFee().compareTo(BigDecimal.ZERO) > 0) {
            orderPrintDTO.setPackFee(orderInfo.getPackFee().toPlainString());
        }
        return orderPrintDTO;
    }

    /**
     * 打印预结算单
     */
    protected OrderPrintDTO preOrderPrint(OrderInfo orderInfo, PrintMachine machine, List<OrderDetail> detailList) {
        OrderPrintDTO orderPrintDTO = getPrintInfoDTO(orderInfo, "预结算单");

        BigDecimal originalAmount = BigDecimal.ZERO;
        for (OrderDetail orderDetail : detailList) {
            if (orderDetail.getIsGift() == 1) {
            } else if (orderDetail.getDiscountSaleAmount().compareTo(BigDecimal.ZERO) > 0) {
                originalAmount = originalAmount.add((orderDetail.getNum().subtract(orderDetail.getReturnNum()).subtract(orderDetail.getRefundNum())).multiply(orderDetail.getUnitPrice()));
            } else {
                originalAmount = originalAmount.add((orderDetail.getNum().subtract(orderDetail.getReturnNum()).subtract(orderDetail.getRefundNum())).multiply(orderDetail.getPrice()));
            }
        }
        orderPrintDTO.setOriginalAmount((originalAmount.add(orderInfo.getSeatAmount()).add(orderInfo.getPackFee()))
                .setScale(2, RoundingMode.HALF_UP).toPlainString());
        orderPrintDTO.setPayAmount(originalAmount.add(orderInfo.getSeatAmount()).add(orderInfo.getPackFee())
                .subtract(orderInfo.getDiscountAllAmount()).setScale(2, RoundingMode.HALF_UP).toPlainString());
        if (orderInfo.getPackFee().compareTo(BigDecimal.ZERO) > 0) {
            orderPrintDTO.setPackFee(orderInfo.getPackFee().toPlainString());
        }
        return orderPrintDTO;
    }

    /**
     * 打印结算单
     */
    protected OrderPrintDTO orderPrint(OrderInfo orderInfo, PrintMachine machine, List<OrderDetail> detailList) {
        OrderPrintDTO orderPrintDTO = getPrintInfoDTO(orderInfo, "结算单");

        BigDecimal originalAmount = orderInfo.getOriginAmount() == null ? BigDecimal.ZERO : orderInfo.getOriginAmount();
        orderPrintDTO.setOriginalAmount(originalAmount.toPlainString());
        orderPrintDTO.setPayAmount(originalAmount.subtract(orderInfo.getDiscountAllAmount()).setScale(2, RoundingMode.HALF_UP).toPlainString());
        if (orderInfo.getPackFee().compareTo(BigDecimal.ZERO) > 0) {
            orderPrintDTO.setPackFee(orderInfo.getPackFee().toPlainString());
        }
        return orderPrintDTO;
    }

    /**
     * 后厨 总单打印
     */
    protected OrderPrintDTO allKitchenPrint(OrderInfo orderInfo, List<OrderDetail> orderDetails, PrintMachine machine) {
        return getPrintInfoDTO(orderInfo, "后厨总单");
    }

    /**
     * 后厨 菜品打印
     */
    protected abstract void onlyKitchenPrint(Integer isCode, OrderInfo orderInfo, OrderDetail orderDetail, PrintMachine machine);

    /**
     * 后厨 退菜整单打印
     */
    protected OrderPrintDTO returnKitchenPrint(String operator, OrderInfo orderInfo, List<OrderDetail> orderDetail, PrintMachine machine) {
        OrderPrintDTO printInfo = getPrintInfoDTO(orderInfo, "后厨退菜单");
        printInfo.setOperator(operator);
        return printInfo;
    }

    /**
     * 叫号打印
     */
    protected abstract void callNumPrint(PrintMachine machine, String shopName, CallTablePrintDTO queue);

    /**
     * 出入库打印单 √
     */
    protected abstract void stockPrint(PrintMachine machine, String shopName, StockPrintDTO record);

    /**
     * 经营日报打印
     */
    protected abstract void dayReportPrint(PrintMachine machine, String shopName, DayReportPrintDTO record);

    /**
     * 日结算单打印
     */
    protected abstract void daySettlePrint(PrintMachine machine, String shopName, DaySettlePrintDTO record);

    /**
     * 商品报表打印
     */
    protected abstract void productReportPrint(PrintMachine machine, String shopName, ProductReportPrintDTO record);

    /**
     * 储值单打印 √
     */
    protected abstract void rechargePrint(PrintMachine machine, String shopName, RechargePrintDTO record);

    /**
     * 库存盘点打印 √
     */
    protected abstract void stockCheckPrint(PrintMachine machine, String shopName, StockCheckPrintDTO record);


    /**
     * 交班打印
     */
    protected abstract void handoverPrint(PrintMachine machine, String shopName, HandoverRecordDTO record);


    private OrderPrintDTO getPrintInfoDTO(OrderInfo orderInfo, String printTitle) {
        ShopInfo shopInfo = shopInfoService.getById(orderInfo.getShopId());
        OrderPrintDTO orderPrintDTO = new OrderPrintDTO().setShopName(shopInfo.getShopName())
                .setPrintTitle(printTitle)
                .setPickupNum(orderInfo.getTableName())
                .setOrderNo(orderInfo.getOrderNo()).setPayAmount(orderInfo.getPayAmount().toPlainString()).setReturn(false)
                .setOutNumber(orderInfo.getTakeCode())
                .setRemark(orderInfo.getRemark())
                // 使用累计的总优惠金额，null 表示没有优惠
                .setDiscountAmount(orderInfo.getDiscountAllAmount() != null
                        ? orderInfo.getDiscountAllAmount().toPlainString()
                        : "0.00");

        orderPrintDTO.setSeatNum(orderInfo.getSeatNum() == null ? "" : orderInfo.getSeatNum().toString());
        if (orderInfo.getSeatNum() != null && orderInfo.getSeatAmount().compareTo(BigDecimal.ZERO) > 0) {
            orderPrintDTO.setSeatAmount(orderInfo.getSeatAmount().divide(BigDecimal.valueOf(orderInfo.getSeatNum()), 2, RoundingMode.DOWN).toPlainString());
        }
        if (StrUtil.isNotBlank(orderInfo.getDineMode())) {
            switch (orderInfo.getDineMode()) {
                case "dine-in" -> orderPrintDTO.setPrintType("收银-堂食");
                case "take-out" -> orderPrintDTO.setPrintType("收银-外带");
                case "take-away" -> orderPrintDTO.setPrintType("收银-外卖");
            }
            if (!"dine-in".equals(orderInfo.getDineMode())) {
                orderInfo.setSeatAmount(BigDecimal.ZERO);
                orderPrintDTO.setSeatAmount("");
            }
        }
        if (orderInfo.getStaffId() != null) {
            ShopStaff detail = shopStaffService.getOne(new QueryWrapper().eq(ShopStaff::getId, orderInfo.getStaffId()).eq(ShopStaff::getShopId, orderInfo.getShopId()));
            if (detail != null) {
                orderPrintDTO.setOperator(detail.getName());
            } else {
                orderPrintDTO.setOperator("");
            }
        } else {
            orderPrintDTO.setOperator("");
        }
        if ("结算单".equals(printTitle)) {
            if (StrUtil.isNotBlank(orderInfo.getPayType())) {
                switch (orderInfo.getPayType()) {
                    case "main_scan" -> orderPrintDTO.setPayType("二维码收款");
                    case "back_scan" -> orderPrintDTO.setPayType("扫码收款");
                    case "wechat_mini" -> orderPrintDTO.setPayType("微信小程序");
                    case "alipay_mini" -> orderPrintDTO.setPayType("支付宝小程序");
                    case "vip_pay" -> orderPrintDTO.setPayType("余额支付");
                    case "cash_pay" -> orderPrintDTO.setPayType("现金支付");
                    case "credit_pay" -> orderPrintDTO.setPayType("挂账支付");
                    case "free_pay" -> orderPrintDTO.setPayType("霸王餐支付");
                }
            }
            orderPrintDTO.setTradeDate(DateUtil.format(orderInfo.getPaidTime(), "yyyy/MM/dd HH:mm:ss"));
        }
        return orderPrintDTO;
    }
}
