package com.clx.performance.service.impl.settle;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.clx.performance.dao.OrderChildDao;
import com.clx.performance.dao.settle.SettlementDriverDetailDao;
import com.clx.performance.dao.settle.SettlementOwnerDetailDao;
import com.clx.performance.enums.settle.SettlementOwnerDetailEnum;
import com.clx.performance.enums.settle.SettlementWayEnum;
import com.clx.performance.model.OrderChild;
import com.clx.performance.model.OrderGoods;
import com.clx.performance.model.settle.SettlementDriverDetail;
import com.clx.performance.model.settle.SettlementOwnerDetail;
import com.clx.performance.param.pc.owner.PageCarrierSettlementOwnerDetailParam;
import com.clx.performance.param.pc.owner.PageOwnerSettlementOwnerDetailParam;
import com.clx.performance.service.settle.SettlementCommonService;
import com.clx.performance.service.settle.SettlementOwnerDetailService;
import com.clx.performance.struct.settle.SettlementOwnerDetailStruct;
import com.clx.performance.utils.excel.ExcelData;
import com.clx.performance.utils.excel.ExcelField;
import com.clx.performance.utils.excel.ExcelSheet;
import com.clx.performance.utils.excel.ExcelUtil;
import com.clx.performance.vo.pc.carrier.settle.CarrierPageSettlementOwnerDetailVO;
import com.clx.performance.vo.pc.owner.settle.PageOwnerSettlementOwnerDetailVO;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.springframework.stereotype.Service;

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

/**
 * @author liruixin
 * Date 2023-10-12
 * Time 09:43
 */
@Service
@Slf4j
@AllArgsConstructor
public class SettlementOwnerDetailServiceImpl  implements SettlementOwnerDetailService {

    private final SettlementOwnerDetailDao settlementOwnerDetailDao;
    private final SettlementOwnerDetailStruct settlementOwnerDetailStruct;
    private final OrderChildDao orderChildDao;
    private final SettlementDriverDetailDao settlementDriverDetailDao;
    private final SettlementCommonService settlementCommonService;


    @Override
    public Integer saveSettlementDetail(OrderChild orderChild, OrderGoods orderGoods) {

        SettlementOwnerDetail settlementOwnerDetail = new SettlementOwnerDetail();
        //设置结算方式
        settlementOwnerDetail.setSettlementWay(orderGoods.getSettlementWay());
        settlementOwnerDetail.setChildNo(orderChild.getChildNo());
        settlementOwnerDetail.setOrderGoodsNo(orderChild.getOrderGoodsNo());
        settlementOwnerDetail.setOrderNo(orderChild.getOrderNo());

        settlementOwnerDetail.setOwnerUserNo(orderChild.getOwnerUserNo());
        settlementOwnerDetail.setOwnerName(orderChild.getOwnerName());

        settlementOwnerDetail.setSendAddressId(orderChild.getSendAddressId());
        settlementOwnerDetail.setSendAddress(orderChild.getSendAddress());
        settlementOwnerDetail.setReceiveAddressId(orderChild.getReceiveAddressId());
        settlementOwnerDetail.setReceiveAddress(orderChild.getReceiveAddress());

        settlementOwnerDetail.setGoodsId(orderChild.getGoodsId());
        settlementOwnerDetail.setGoodsName(orderChild.getGoodsName());

        // 运费
        settlementOwnerDetail.setFreightPrice(orderChild.getOrderFreightPrice());     //平台运费
        settlementOwnerDetail.setWeight(weightCalc(orderGoods.getSettlementWay(),orderChild.getLoadNet(), orderChild.getUnloadNet()));
        settlementOwnerDetail.setFreight(freightCalc(settlementOwnerDetail.getFreightPrice(), settlementOwnerDetail.getWeight()));

        // 亏吨
        settlementOwnerDetail.setLossPrice(orderChild.getLossPrice());
        settlementOwnerDetail.setLossWeight(lossWeightCalc(orderChild.getLoadNet(), orderChild.getUnloadNet()));
        settlementOwnerDetail.setLossFreight(lossFreightCalc(settlementOwnerDetail.getLossPrice(), settlementOwnerDetail.getLossWeight()));

        // 预付运费
        settlementOwnerDetail.setPrepayFreight(BigDecimal.ZERO);

        //借款抵扣金额初始化
        settlementOwnerDetail.setLoanFreight(BigDecimal.ZERO);

        // 开票服务费
        settlementOwnerDetail.setInvoiceServiceFeeRate(
                orderGoods.getInvoiceServiceFeeRate()==null? BigDecimal.ZERO : orderGoods.getInvoiceServiceFeeRate());

        // 开票服务费
        settlementOwnerDetail.setInvoiceServiceFee(
                settlementCommonService.invoiceServiceFeeCalc(orderChild.getSettlementWay(), settlementOwnerDetail));
        // 开票金额
        settlementOwnerDetail.setInvoiceFreight(
                settlementCommonService.invoiceFreightCalc(orderChild.getSettlementWay(),
                orderGoods.getInvoiceServiceFeeRate(), settlementOwnerDetail));

        // 结算金额
        settlementOwnerDetail.setSettlementFreight(
                settlementCommonService.settlementFreightCalc(orderGoods.getSettlementWay(),settlementOwnerDetail));

        settlementOwnerDetail.setFinalPaymentStatus(SettlementOwnerDetailEnum.FinalPaymentStatus.NO.getCode());
        //设置开票公司信息
        settlementOwnerDetail.setInvoicingCompanyId(orderChild.getInvoicingCompanyId());
        settlementOwnerDetail.setInvoicingCompanyShorterName(orderChild.getInvoicingCompanyShorterName());
        settlementOwnerDetail.setInvoicingCompanyGroupCode(orderChild.getInvoicingCompanyGroupCode());
        settlementOwnerDetail.setReportFlag(orderChild.getReportFlag());

        settlementOwnerDetail.setInvoiceChannel(SettlementOwnerDetailEnum.InvoiceChannel.BROKER.getCode());

        settlementOwnerDetailDao.saveEntity(settlementOwnerDetail);
        return settlementOwnerDetail.getId();
    }

    /**
     * 吨数计算
     */
    private BigDecimal weightCalc(Integer settlementWay,BigDecimal loadNet, BigDecimal unLoadNet){
        if(Objects.equals(settlementWay, SettlementWayEnum.WayType.LOAD.getCode())){ //如果该货主的结算方式为“装车净重”时，那么实际净重=装车净重。
            return loadNet;
        }else{ //如果货主的结算方式为“卸车净重”时，那么实际净重=卸车净重。如果卸车净重>装车净重，那么卸车净重=装车净重。
            return unLoadNet.compareTo(loadNet) > 0? loadNet : unLoadNet;
        }
    }

    /**
     *  运费计算
     */
    public BigDecimal freightCalc(BigDecimal freightPrice, BigDecimal weight){

        return freightPrice.multiply(weight).setScale(0, RoundingMode.HALF_UP);
    }

    /**
     * 亏吨计算
     */
    private BigDecimal lossWeightCalc(BigDecimal loadNet, BigDecimal unLoadNet){
        return unLoadNet.compareTo(loadNet) < 0? loadNet.subtract(unLoadNet) : BigDecimal.ZERO;
    }

    /**
     * 亏吨运费
     */
    private BigDecimal lossFreightCalc(BigDecimal freightPrice, BigDecimal weight){
        return freightPrice.multiply(weight).setScale(0, RoundingMode.HALF_UP);

    }

    @Override
    public IPage<CarrierPageSettlementOwnerDetailVO> pageSettlementOwnerDetail(
            PageCarrierSettlementOwnerDetailParam param) {
        IPage<SettlementOwnerDetail> result = settlementOwnerDetailDao.pageSettlementOwnerDetail(param);
        List<CarrierPageSettlementOwnerDetailVO> list = settlementOwnerDetailStruct.covertList(result.getRecords());
        return new Page<CarrierPageSettlementOwnerDetailVO>().setRecords(list).setTotal(result.getTotal()).setPages(result.getPages());
    }

    @Override
    public IPage<PageOwnerSettlementOwnerDetailVO> pageOwnerSettlementOrderDetailList(PageOwnerSettlementOwnerDetailParam param) {

        IPage<SettlementOwnerDetail> settlementOwnerDetail = settlementOwnerDetailDao.pageOwnerSettlementOrderDetailList(param);
        List<PageOwnerSettlementOwnerDetailVO> list = settlementOwnerDetailStruct.covertToOwnerList(settlementOwnerDetail.getRecords());
        return new Page<PageOwnerSettlementOwnerDetailVO>().setRecords(list).setTotal(settlementOwnerDetail.getTotal()).setPages(settlementOwnerDetail.getPages());

    }

    @Override
    public SXSSFWorkbook exportOwnerSettlementOrderDetail(PageOwnerSettlementOwnerDetailParam param) {
        param.setPage(1);
        param.setPageSize(1000000);
        IPage<PageOwnerSettlementOwnerDetailVO> settlementOwnerDetail = pageOwnerSettlementOrderDetailList(param);

        List<PageOwnerSettlementOwnerDetailVO> list = settlementOwnerDetail.getRecords();

        // 组装表头
        List<ExcelField> fieldList = new ArrayList<>();
        fieldList.add(new ExcelField(0, "序号", "index", 2000));
        fieldList.add(new ExcelField(1, "运单编号", "childNo", 5000));
        fieldList.add(new ExcelField(2, "货主编码", "ownerUserNno", 5000));
        fieldList.add(new ExcelField(3, "结算方式", "settlementWay", 5000));
        fieldList.add(new ExcelField(4, "货物名称", "goodsName", 5000));
        fieldList.add(new ExcelField(5, "实际净重（吨）", "weight", 5000));
        fieldList.add(new ExcelField(6, "平台运费报价", "freightPrice", 5000));
        fieldList.add(new ExcelField(7, "实际运费金额", "freight", 5000));
        fieldList.add(new ExcelField(8, "货损吨数", "lossWeight", 5000));
        fieldList.add(new ExcelField(9, "货损单价", "lossPrice", 5000));
        fieldList.add(new ExcelField(10, "货损金额", "lossFreight", 5000));
        fieldList.add(new ExcelField(11, "开票费率", "invoiceServiceFeeRate", 5000));
        fieldList.add(new ExcelField(12, "开票服务费", "invoiceServiceFee", 5000));
        fieldList.add(new ExcelField(13, "预付运费金额", "prepayFreight", 5000));
        fieldList.add(new ExcelField(14, "结算金额", "settlementFreight", 5000));
        fieldList.add(new ExcelField(15, "借款抵扣金额", "loanFreight", 5000));

        fieldList.add(new ExcelField(16, "开票金额", "invoiceFreight", 5000));
        fieldList.add(new ExcelField(17, "开票标识", "invoiceType", 5000));
        fieldList.add(new ExcelField(18, "订单编号", "orderNo", 5000));
        fieldList.add(new ExcelField(19, "结算单号", "settlementNo", 15000));
        fieldList.add(new ExcelField(20, "结算子单号", "settlementSubNo", 15000));
        fieldList.add(new ExcelField(21, "创建时间", "createTime", 5000));
        fieldList.add(new ExcelField(22, "是否已付尾款", "finalPaymentStatus", 5000));
        fieldList.add(new ExcelField(23, "是否已开票", "invoiceStatus", 5000));

        // 组装数据
        List<List<ExcelData>> dataList = new ArrayList<>();
        for (int i=0; i<list.size(); i++){

            PageOwnerSettlementOwnerDetailVO dto = list.get(i);
            List<ExcelData> rowData = new ArrayList<>();

            Optional<SettlementWayEnum.WayType> byCode = SettlementWayEnum.WayType.getByCode(dto.getSettlementWay());
            String settlementWay = byCode.isPresent()?byCode.get().getMsg():"";
            rowData.add(new ExcelData(i+1));
            rowData.add(new ExcelData(dto.getChildNo()));
            rowData.add(new ExcelData(dto.getOwnerUserNo()+""));
            rowData.add(new ExcelData(settlementWay));
            rowData.add(new ExcelData(dto.getGoodsName()));
            rowData.add(new ExcelData(dto.getWeight()));
            rowData.add(new ExcelData(dto.getFreightPrice().movePointLeft(2)));
            rowData.add(new ExcelData(dto.getFreight().movePointLeft(2)));
            rowData.add(new ExcelData(dto.getLossWeight()));
            rowData.add(new ExcelData(dto.getLossPrice().movePointLeft(2)));
            rowData.add(new ExcelData(dto.getLossFreight().movePointLeft(2)));
            rowData.add(new ExcelData(dto.getInvoiceServiceFeeRate().toString()+ "%"));
            rowData.add(new ExcelData(dto.getInvoiceServiceFee().movePointLeft(2)));
            rowData.add(new ExcelData(dto.getPrepayFreight().movePointLeft(2)));
            rowData.add(new ExcelData(dto.getSettlementFreight().movePointLeft(2)));
            rowData.add(new ExcelData(dto.getLoanFreight().movePointLeft(2)));

            rowData.add(new ExcelData(dto.getInvoiceFreight().movePointLeft(2)));
            rowData.add(new ExcelData(dto.getInvoiceTypeMsg()));
            rowData.add(new ExcelData(dto.getOrderNo()));
            rowData.add(new ExcelData(dto.getSettlementNo()));
            rowData.add(new ExcelData(dto.getSettlementSubNo()));
            rowData.add(new ExcelData(dto.getCreateTime()));
            rowData.add(new ExcelData(dto.getFinalPaymentStatusMsg()));
            rowData.add(new ExcelData(dto.getInvoiceStatusMsg()));
            dataList.add(rowData);
        }

        ExcelSheet excelSheet = new ExcelSheet("货主结算运单明细", "货主结算运单明细", fieldList, dataList);

        //创建excel
        return ExcelUtil.create(excelSheet);
    }

    @Override
    public Integer getInvoiceType(String orderNo) {
        return settlementOwnerDetailDao.getInvoiceType(orderNo);
    }

    @Override
    public Boolean thawAndLockSettlement(String orderNo) {
        boolean orderChildrenFlag = false;
        boolean ownerDetailsFlag = false;
        boolean loanFlag = false;

        List<OrderChild> orderChildren = orderChildDao.selectInTransitOrderChildLtUnsettle(orderNo);

        if (CollectionUtil.isNotEmpty(orderChildren)) {
            log.info("当前订单{},存在在途运单,不予锁定", orderNo);
            orderChildrenFlag = true;
        } else {
            List<SettlementOwnerDetail> ownerDetails = settlementOwnerDetailDao.selectListByOrderNo(orderNo);
            if (CollectionUtil.isNotEmpty(ownerDetails)) {
                log.info("当前订单{},存在结算单编号是null的货主计费,不予锁定", orderNo);

                ownerDetailsFlag = true;
            } else {
                //开票标识“网运单”，预付标识“未预付”，借款标识”未借款“的车主计费，如存在不允许锁定
                SettlementDriverDetail settlementDriverDetail = settlementDriverDetailDao.selectLoanFlag(orderNo);
                if (settlementDriverDetail != null) {
                    log.info("当前订单{},开票标识“网运单”，预付标识“未预付”，借款标识”未借款“的车主计费,不予锁定", orderNo);
                    loanFlag = true;
                } else {
                    //开票标识“网运单”，预付标识“未预付”，借款类型“虚拟货币”，借款标识”已借款“的车主计费，如存在不允许锁定
                    settlementDriverDetail = settlementDriverDetailDao.selectLoanTypeFlag(orderNo);
                    if (settlementDriverDetail != null) {
                        log.info("当前订单{},开票标识“网运单”，预付标识“未预付”，借款标识”未借款“的车主计费,不予锁定", orderNo);
                        loanFlag = true;
                    }
                }

            }
        }

        if (ownerDetailsFlag || orderChildrenFlag || loanFlag) {
            return true;
        } else {
            return false;
        }
    }
}
