package com.trade.client.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.StringUtils;
import com.trade.common.utils.uuid.BizNoUtil;
import com.trade.service.DictConstants;
import com.trade.service.companyrelated.domain.ImporterEntity;
import com.trade.service.companyrelated.service.IImporterEntityService;
import com.trade.service.enterprise.domain.Enterprise;
import com.trade.service.enterprise.domain.EnterpriseRelatedPerson;
import com.trade.service.enterprise.service.EnterpriseBaseService;
import com.trade.service.enterprise.service.EnterpriseRelatedPersonBaseService;
import com.trade.service.order.domain.PurchaseOrderCommodity;
import com.trade.service.order.dto.PurchaseOrderDto;
import com.trade.service.order.service.PurchaseOrderService;
import com.trade.service.sales.domain.ContractTerms;
import com.trade.service.sales.dto.SalesContractDto;
import com.trade.service.sales.service.ContractTermsService;
import com.trade.service.sales.service.SalesContractService;
import com.trade.service.salesorder.domain.*;
import com.trade.service.salesorder.dto.SalesOrderDTO;
import com.trade.service.salesorder.dto.SalesOrderDeliverDTO;
import com.trade.client.salesorder.service.SalesOrderCService;
import com.trade.service.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.salesorder.constant.SaleOrderConstant;
import com.trade.service.salesorder.dto.GoodsInfo4TL;
import com.trade.service.salesorder.vo.SalesOrderDtlVO;
import com.trade.service.salesorder.vo.SalesOrderSearch;
import com.trade.service.salesorder.vo.SalesOrderVO;
import com.trade.service.salesorder.service.*;
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.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;

    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());
        return salesOrderService.queryListBySearch(search);
    }

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

    private void saveSalesOrderGoods(SalesOrderDTO salesOrderDTO, Long orderId) {
        // 先批量删除，再新增
        salesOrderGoodsService.removeByOrderId(orderId);
        List<SalesOrderGoods> salesOrderGoodsList = salesOrderDTO.getSalesOrderGoodsList();
        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.lambdaQuery()
                .eq(ContractTerms::getContractNo, salesContract.getContractNo()).one();
        ImporterEntity importer = importerEntityService.getByEntityId(salesOrder.getImporterId());
        Enterprise enterprise = enterpriseBaseService.getEnterpriseByCustomerId(salesOrder.getCustomerUserId());
        EnterpriseRelatedPerson contact = relatedPersonBaseService
                .getContactByChangeRecordId(enterprise.getChangeRecordId());
        String customerName = enterprise.getName();
        String customerAddress = 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();
        }
        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", contractTerms.getPaymentWay());
        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) {
        return salesOrderService.getDetail(id, is4Update);
    }

    @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);
    }

    private void savePurchaseOrder(SalesOrderDTO salesOrderDTO, SalesOrder salesOrder, Long orderId) {
        // 保存采购订单
        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(s -> s.getVendorEntityNo(), HashMap::new, Collectors.toList()));
        List<SalesOrderGoods> entityNoEmps = salesOrderGoods.stream()
                .filter(s -> StringUtils.isEmpty(s.getVendorEntityNo())).collect(Collectors.toList());
        entityNoNotEmp.forEach((k, v) -> {
            createPurchaseOrder(v, orderId, salesOrder, k, purchaseOrderDtos);
        });
        if (CollectionUtil.isNotEmpty(entityNoEmps)) {
            createPurchaseOrder(entityNoEmps, orderId, salesOrder, null, purchaseOrderDtos);
        }
        purchaseOrderService.saveOrder(purchaseOrderDtos);
    }

    private void createPurchaseOrder(List<SalesOrderGoods> salesOrderGoods, Long finalOrderId, SalesOrder salesOrder,
            String entityNo, List<PurchaseOrderDto> purchaseOrderDtos) {
        PurchaseOrderDto purchaseOrderDto = new PurchaseOrderDto();
        purchaseOrderDto.setSalesOrderId(finalOrderId);
        purchaseOrderDto.setSalesOrderNo(salesOrder.getOrderNo());
        purchaseOrderDto.setVendorEntityNo(entityNo);
        purchaseOrderDto.setCustomerUserId(salesOrder.getCustomerUserId());
        purchaseOrderDto.setSaleContractId(salesOrder.getSaleContractId());
        List<PurchaseOrderCommodity> commodities = new ArrayList<>();
        for (SalesOrderGoods orderGoods : salesOrderGoods) {
            PurchaseOrderCommodity commodity = new PurchaseOrderCommodity();
            commodity.setPriceUnit(orderGoods.getPrice());
            commodity.setAmount(orderGoods.getPriceTotal());
            commodity.setHsCode(orderGoods.getHsCode());
            commodity.setMeasurementUnit(orderGoods.getUnit());
            commodity.setModel(orderGoods.getModel());
            commodity.setName(orderGoods.getName());
            commodity.setPackSpec(orderGoods.getPackingSpecifications());
            commodity.setNumber(orderGoods.getCount());
            commodity.setSalesCommodityId(orderGoods.getSalesCommodityId());
            commodity.setTaxRefundRate(orderGoods.getTaxRefundRate());
            commodities.add(commodity);
        }
        purchaseOrderDto.setCommodities(commodities);
        purchaseOrderDtos.add(purchaseOrderDto);
    }
}
