package com.trade.client.trade.salesorder.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
import com.github.pagehelper.PageInfo;
import com.trade.client.security.utils.UserUtils;
import com.trade.common.constant.HttpStatus;
import com.trade.common.exception.ServiceException;
import com.trade.common.utils.ConstantUtil;
import com.trade.common.utils.StringUtils;
import com.trade.common.utils.uuid.BizNoUtil;
import com.trade.service.DictConstants;
import com.trade.service.enterprise.companyrelated.domain.ImporterEntity;
import com.trade.service.enterprise.companyrelated.service.IImporterEntityService;
import com.trade.service.enterprise.customer.domain.Enterprise;
import com.trade.service.enterprise.customer.domain.EnterpriseRelatedPerson;
import com.trade.service.enterprise.customer.service.EnterpriseBaseService;
import com.trade.service.enterprise.customer.service.EnterpriseRelatedPersonBaseService;
import com.trade.service.system.service.ISysDictDataService;
import com.trade.service.trade.purchaseorder.domain.PurchaseOrderCommodity;
import com.trade.service.trade.purchaseorder.dto.PurchaseOrderDto;
import com.trade.service.trade.purchaseorder.service.PurchaseOrderService;
import com.trade.service.trade.salescontract.domain.ContractTerms;
import com.trade.service.trade.salescontract.domain.SalesCommodity;
import com.trade.service.trade.salescontract.dto.SalesContractDto;
import com.trade.service.trade.salescontract.service.ContractTermsService;
import com.trade.service.trade.salescontract.service.SalesCommodityService;
import com.trade.service.trade.salescontract.service.SalesContractService;
import com.trade.service.trade.salesorder.domain.*;
import com.trade.service.trade.salesorder.dto.*;
import com.trade.client.trade.salesorder.service.SalesOrderCService;
import com.trade.service.filestorage.enums.FileTypeEnum;
import com.trade.service.TransStruct;
import com.trade.service.contract.dto.ContractGenInputItem;
import com.trade.service.contract.dto.ContractGenOutputItem;
import com.trade.service.contract.service.ContractService;
import com.trade.service.contract.service.ISFileTemplateService;
import com.trade.service.trade.salesorder.constant.SaleOrderConstant;
import com.trade.service.trade.salesorder.service.*;
import com.trade.service.trade.salesorder.vo.SalesOrderDtlVO;
import com.trade.service.trade.salesorder.vo.SalesOrderSearch;
import com.trade.service.trade.salesorder.vo.SalesOrderVO;
import com.trade.service.serialNum.service.SerialNumberGenerator;
import com.trade.service.tradingplatform.domain.TradingPlatform;
import com.trade.service.tradingplatform.service.TradingPlatformBaseService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class SalesOrderCServiceImpl implements SalesOrderCService {

    private final ISalesOrderService salesOrderService;
    private final ISalesOrderGoodsService salesOrderGoodsService;
    private final ISalesOrderInvoiceService salesOrderInvoiceService;
    private final ISalesOrderLogisticsService salesOrderLogisticsService;
    private final ISalesOrderContainerService salesOrderContainerService;
    @Resource
    private ContractService contractService;
    @Resource
    private SerialNumberGenerator serialNumberGenerator;
    @Resource
    private TransStruct transStruct;
    @Resource
    private ISFileTemplateService fileTemplateService;
    @Resource
    private SalesContractService salesContractService;
    @Resource
    private IImporterEntityService importerEntityService;
    @Resource
    private ContractTermsService contractTermsService;
    @Resource
    private EnterpriseBaseService enterpriseBaseService;
    @Resource
    private EnterpriseRelatedPersonBaseService relatedPersonBaseService;
    @Resource
    private TradingPlatformBaseService tradingPlatformBaseService;
    @Resource
    private PurchaseOrderService purchaseOrderService;
    @Resource
    private SalesCommodityService salesCommodityService;
    @Resource
    private ISysDictDataService iSysDictDataService;

    public SalesOrderCServiceImpl(ISalesOrderService salesOrderService, ISalesOrderGoodsService salesOrderGoodsService,
            ISalesOrderInvoiceService salesOrderInvoiceService, ISalesOrderLogisticsService salesOrderLogisticsService,
            ISalesOrderContainerService salesOrderContainerService){
        this.salesOrderService = salesOrderService;
        this.salesOrderGoodsService = salesOrderGoodsService;
        this.salesOrderInvoiceService = salesOrderInvoiceService;
        this.salesOrderLogisticsService = salesOrderLogisticsService;
        this.salesOrderContainerService = salesOrderContainerService;
    }

    @Override
    public PageInfo<SalesOrderVO> queryListBySearch(SalesOrderSearch search) {
        search.setCustomerUserId(UserUtils.getCustomerUserId());
        search.setPoint(true);
        if (StringUtils.isNotEmpty(search.getStatus())
                && SaleOrderConstant.ORDER_STATUS_DELIVERING.equals(search.getStatus())) {
            List<Integer> statusList = new ArrayList<>();
            statusList.add(SaleOrderConstant.ORDER_STATUS_DELIVERING);
            statusList.add(SaleOrderConstant.ORDER_STATUS_PARTIAL_COLLECTION);
            statusList.add(SaleOrderConstant.ORDER_STATUS_PARTIAL_REPAYMENT_UNDER_REVIEW);
            statusList.add(SaleOrderConstant.ORDER_STATUS_REPAYMENT_UNDER_REVIEW);
            statusList.add(SaleOrderConstant.ORDER_STATUS_RECEIVED);
            search.setStatus(null);
            search.setStatusList(statusList);
        }
        return salesOrderService.queryListBySearch(search);
    }

    @Override
    @Transactional
    public void save(SalesOrderDTO salesOrderDTO, boolean isSubmit) {
        String loginName = UserUtils.getLoginUser().getUsername();
        SalesOrder salesOrder = salesOrderDTO.getSalesOrder();
        salesOrder.setPendingReceivedAmount(salesOrder.getAmount());
        Long orderId = salesOrder.getId();
        if (isSubmit) {
            checkParams(salesOrderDTO);
            salesOrder.setOrderNo(BizNoUtil.generateSaleOrderNo());
            salesOrder.setStatus(SaleOrderConstant.ORDER_STATUS_WAIT_DELIVERY);
        } else {
            salesOrder.setStatus(SaleOrderConstant.ORDER_STATUS_INIT);
        }
        if (null == orderId) {
            salesOrder.setCreateBy(loginName);
            orderId = salesOrderService.saveSalesOrder(salesOrder);
        } else {
            salesOrder.setUpdateBy(loginName);
            salesOrderService.updateById(salesOrder);
        }
        // 保存订单商品
        saveSalesOrderGoods(salesOrderDTO, orderId);
        // 保存采购订单
        if (isSubmit) {
            savePurchaseOrder(salesOrderDTO, salesOrder, orderId);
        }
    }

    /**
     * 参数校验
     * 
     * @param salesOrderDTO 订单信息
     */
    private void checkParams(SalesOrderDTO salesOrderDTO) {
        salesOrderDTO.getSalesOrderGoodsList().forEach(item -> {
            SalesCommodity byId = salesCommodityService.getById(item.getSalesCommodityId());
            if (item.getCount().compareTo(byId.getNumber()) > 0) {
                throw new ServiceException("商品数量超出销售合同商品数量，请重新输入");
            }
            List<SalesOrderGoods> goodsByCommodityId = salesOrderGoodsService
                    .getByCommodityId(item.getSalesCommodityId());
            BigDecimal count = goodsByCommodityId.stream().map(SalesOrderGoods::getCount).reduce(BigDecimal.ZERO,
                    BigDecimal::add);
            BigDecimal tempCount = count.add(item.getCount());
            if (tempCount.compareTo(byId.getNumber()) > 0) {
                throw new ServiceException("订单中商品数量超出销售合同商品数量，请重新输入");
            }
        });
    }

    private void saveSalesOrderGoods(SalesOrderDTO salesOrderDTO, Long orderId) {
        // 先批量删除，再新增
        salesOrderGoodsService.removeByOrderId(orderId);
        List<SalesOrderGoods> salesOrderGoodsList = salesOrderDTO.getSalesOrderGoodsList();
        if (CollectionUtil.isNotEmpty(salesOrderGoodsList)) {
            for (SalesOrderGoods salesOrderGoods : salesOrderGoodsList) {
                salesOrderGoods.setSaleOrderId(orderId);
                salesOrderGoodsService.save(salesOrderGoods);
            }
        }
    }

    @Override
    @Transactional
    public void submit(SalesOrderDTO salesOrderDTO) {
        log.info("提交销售订单Info：{}", salesOrderDTO.toString());
        save(salesOrderDTO, true);
        SalesOrder salesOrder = salesOrderDTO.getSalesOrder();
        Long userId = salesOrder.getCustomerUserId();
        SalesOrderInvoice salesOrderInvoice = new SalesOrderInvoice();
        salesOrderInvoice.setOrderId(salesOrder.getId());
        salesOrderInvoice.setInvoiceNo(serialNumberGenerator.nextValue(SaleOrderConstant.ORDER_INVOICE_NO_PREFIX));
        salesOrderInvoice.setAmount(salesOrder.getAmount());
        // 生成发票文件并保存文件ID
        ContractGenInputItem item = initContractGenInputItem(salesOrder, salesOrderDTO.getSalesOrderGoodsList(),
                salesOrderInvoice.getInvoiceNo());
        ContractGenOutputItem outputItem = contractService.generateSingleContract(item, userId);
        salesOrderInvoice.setFileId(outputItem.getFileId());
        salesOrderInvoiceService.save(salesOrderInvoice);
    }

    /**
     * 初始化合同生成参数
     * 
     * @param salesOrder 订单信息
     * @param salesOrderGoodsList 订单商品列表
     * @param invoiceNo 发票号
     * @return 合同生成参数
     */
    private ContractGenInputItem initContractGenInputItem(SalesOrder salesOrder,
            List<SalesOrderGoods> salesOrderGoodsList, String invoiceNo) {
        ContractGenInputItem item = new ContractGenInputItem();
        String templateCode = fileTemplateService.selectByFileType(FileTypeEnum.PROFORMA_INVOICE.getCode())
                .getTemplateCode();
        SalesContractDto salesContract = salesContractService.getSalesContractById(salesOrder.getSaleContractId());
        ContractTerms contractTerms = contractTermsService.getByContractIdAndType(salesContract.getId(),
                ConstantUtil.SALES_CONTRACT_NO_PREFIX);
        ImporterEntity importer = importerEntityService.getByEntityId(salesOrder.getImporterId());
        Enterprise enterprise = enterpriseBaseService.getEnterpriseByCustomerId(salesOrder.getCustomerUserId());
        EnterpriseRelatedPerson contact = relatedPersonBaseService
                .getContactByChangeRecordId(enterprise.getChangeRecordId());
        String customerName = enterprise.getName();
        String customerAddress = StringUtils.isEmpty(enterprise.getBusinessAddress()) ? enterprise.getRegisterAddress()
                : enterprise.getBusinessAddress();
        String contractTel = contact.getMobile();
        if (DictConstants.EXPORT_TYPE_PROXY.equals(enterprise.getExportType())) {
            TradingPlatform tradingPlatform = tradingPlatformBaseService.lambdaQuery()
                    .eq(TradingPlatform::getCertNo, salesContract.getAgencyCertNo()).one();
            customerName = tradingPlatform.getPlatformName();
            customerAddress = tradingPlatform.getBusinessAddress();
            contractTel = tradingPlatform.getContactMobile();
        }
        // 支付方式 页面选其他，后台取不到值，默认"" PS: The final interpretation rights belong to the product manager who will never make
        // a mistake
        String paymentWay = Optional
                .ofNullable(iSysDictDataService.selectDictLabel("payment_way_en", contractTerms.getPaymentWay()))
                .orElse("");
        item.setTemplateCode(templateCode);
        Map<String, Object> params = new HashMap<>();
        params.put("customerName", customerName);
        params.put("customerAddress", customerAddress);
        params.put("date", LocalDate.now());
        params.put("tel", contractTel);
        params.put("invoiceNo", invoiceNo);
        params.put("from", salesContract.getPortDeparture());
        params.put("to", salesContract.getPortDestination());
        params.put("incoTerms", salesContract.getTradeTerms());
        params.put("payWay", paymentWay);
        params.put("importerName", importer.getEntityName());
        params.put("importerAddress", importer.getBusinessAddress());
        params.put("currency", contractTerms.getPaymentCurrency());
        params.put("cardNo", contractTerms.getAccountNumber());
        params.put("openBank", contractTerms.getOpeningBank());
        params.put("swiftCode", contractTerms.getSwiftCode());
        params.put("total", salesOrder.getAmount());
        List<GoodsInfo4TL> goodsList = transStruct.transOrder2GoodsInfo4TLList(salesOrderGoodsList);
        params.put(SaleOrderConstant.ORDER_INVOICE_GOODS_LIST_KEY, goodsList);
        item.setParams(params);
        LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
        item.setConfig(Configure.builder().bind(SaleOrderConstant.ORDER_INVOICE_GOODS_LIST_KEY, policy).build());
        return item;
    }

    @Override
    @Transactional
    public void deliver(SalesOrderDeliverDTO orderDeliverDTO) {
        log.info("订单发货Info：{}", orderDeliverDTO.toString());
        Optional<SalesOrder> salesOrderOpt = Optional
                .ofNullable(salesOrderService.getById(orderDeliverDTO.getOrderId()));
        salesOrderOpt.orElseThrow(() -> new ServiceException("订单不存在", HttpStatus.BAD_REQUEST));
        SalesOrder salesOrder = salesOrderOpt.get();
        if (!SaleOrderConstant.ORDER_STATUS_WAIT_DELIVERY.equals(salesOrder.getStatus())) {
            throw new ServiceException("订单状态有误", HttpStatus.BAD_REQUEST);
        }
        SalesOrderLogistics orderLogistics = transStruct.transOrderDeliver2OrderLogistics(orderDeliverDTO);
        salesOrderLogisticsService.save(orderLogistics);
        for (SalesOrderContainer container : orderDeliverDTO.getContainers()) {
            container.setLogisticsId(orderLogistics.getId());
            salesOrderContainerService.save(container);
        }
        salesOrder.setStatus(SaleOrderConstant.ORDER_STATUS_DELIVERING);
        salesOrderService.updateById(salesOrder);
    }

    @Override
    public SalesOrderDtlVO getDetail(Long id, boolean is4Update) {
        SalesOrderDtlVO salesOrderDtlVO = salesOrderService.getDetail(id, is4Update);
        if (salesOrderDtlVO.getSalesOrderVO() != null) {
            if (salesOrderDtlVO.getSalesOrderVO().getStatus().equals(SaleOrderConstant.ORDER_STATUS_PARTIAL_COLLECTION)
                    || salesOrderDtlVO.getSalesOrderVO().getStatus()
                            .equals(SaleOrderConstant.ORDER_STATUS_PARTIAL_REPAYMENT_UNDER_REVIEW)
                    || salesOrderDtlVO.getSalesOrderVO().getStatus()
                            .equals(SaleOrderConstant.ORDER_STATUS_REPAYMENT_UNDER_REVIEW)
                    || salesOrderDtlVO.getSalesOrderVO().getStatus().equals(SaleOrderConstant.ORDER_STATUS_RECEIVED)) {
                salesOrderDtlVO.getSalesOrderVO().setStatus(SaleOrderConstant.ORDER_STATUS_DELIVERING);
            }
        }
        return salesOrderDtlVO;
    }

    @Override
    public boolean delete(Long id) {
        log.info("删除销售订单id：{}", id);
        Optional<SalesOrder> salesOrderOpt = Optional.ofNullable(salesOrderService.getById(id));
        salesOrderOpt.orElseThrow(() -> new ServiceException("订单不存在", HttpStatus.BAD_REQUEST));
        SalesOrder salesOrder = salesOrderOpt.get();
        if (!SaleOrderConstant.ORDER_STATUS_INIT.equals(salesOrder.getStatus())) {
            throw new ServiceException("只有草稿状态的订单才能删除", HttpStatus.BAD_REQUEST);
        }
        return salesOrderService.removeById(id);
    }

    @Override
    public Boolean processGoodsReceivePayment(SalesGoodsReceivePaymentDto salesOrderReceivePayment) {
        return salesOrderService.processGoodsReceivePayment(salesOrderReceivePayment);
    }

    @Override
    public List<ReceivePaymentExtendDto> processOrderReceivePaymentList(
            List<ReceivePaymentExtendDto> receivePaymentExtendDtos) {
        return salesOrderService.processOrderReceivePaymentList(receivePaymentExtendDtos, false);
    }

    @Override
    public Boolean processOrderReceivePayment(ReceivePaymentSubDto receivePaymentSubDto, boolean source) {
        return salesOrderService.processOrderReceivePayment(receivePaymentSubDto, source);
    }

    @Override
    public List<OrderListInfoDto> queryOrderListInfoByOrderIds(List<SalesOrderReceiveExtendDto> salesOrderInfo) {
        return salesOrderService.queryOrderListInfoByOrderIds(salesOrderInfo);
    }

    @Override
    public OrderPaymentAmountDto submitOrderListInfo(List<OrderListInfoDto> salesOrderInfo, int source) {
        return salesOrderService.submitOrderListInfo(salesOrderInfo, source);
    }

    private void savePurchaseOrder(SalesOrderDTO salesOrderDTO, SalesOrder salesOrder, Long orderId) {
        Enterprise enterprise = enterpriseBaseService.getEnterpriseByCustomerId(salesOrder.getCustomerUserId());
        if (enterprise == null) {
            throw new ServiceException("用户未完成认证（用户id：" + salesOrder.getCustomerUserId() + "）", HttpStatus.BAD_REQUEST);
        }
        // “自营出口”的“生产企业”,不生成采购订单
        if (DictConstants.EXPORT_TYPE_SELF.equals(enterprise.getExportType())
                && (DictConstants.ENTERPRISE_TYPE_PRODUCE.equals(enterprise.getEnterpriseType())
                        || DictConstants.ENTERPRISE_TYPE_PRODUCE_EXTERNAL.equals(enterprise.getEnterpriseType()))) {
            return;
        }
        // 保存采购订单
        List<PurchaseOrderDto> purchaseOrderDtos = new ArrayList<>();
        List<SalesOrderGoods> salesOrderGoods = salesOrderDTO.getSalesOrderGoodsList();
        Map<String, List<SalesOrderGoods>> entityNoNotEmp = salesOrderGoods.stream()
                .filter(s -> StringUtils.isNotEmpty(s.getVendorEntityNo()))
                .collect(Collectors.groupingBy(SalesOrderGoods::getVendorEntityNo, HashMap::new, Collectors.toList()));
        List<SalesOrderGoods> entityNoEmps = salesOrderGoods.stream()
                .filter(s -> StringUtils.isEmpty(s.getVendorEntityNo())).collect(Collectors.toList());
        entityNoNotEmp.forEach((k, v) -> {
            purchaseOrderService.createPurchaseOrder(v, orderId, salesOrder, k, purchaseOrderDtos);
        });
        if (CollectionUtil.isNotEmpty(entityNoEmps)) {
            purchaseOrderService.createPurchaseOrder(entityNoEmps, orderId, salesOrder, null, purchaseOrderDtos);
        }
        purchaseOrderService.saveOrder(purchaseOrderDtos, null);
    }
}
