package cn.iocoder.foodnexus.module.order.service.customerorder;

import cn.iocoder.foodnexus.framework.common.util.CommonUtil;
import cn.iocoder.foodnexus.framework.common.util.spring.SpringUtils;
import cn.iocoder.foodnexus.framework.security.core.LoginUser;
import cn.iocoder.foodnexus.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.foodnexus.module.customerpermission.core.annotation.CustomerVisible;
import cn.iocoder.foodnexus.module.erp.api.PurchaseOrderSplitEvent;
import cn.iocoder.foodnexus.module.erp.api.service.ErpCustomerApi;
import cn.iocoder.foodnexus.module.erp.api.service.ErpSupplierApi;
import cn.iocoder.foodnexus.module.erp.api.service.ErpWarehouseApi;
import cn.iocoder.foodnexus.module.erp.dal.dataobject.sale.ErpSaleOutDO;
import cn.iocoder.foodnexus.module.erp.dal.dataobject.sale.ErpSaleOutItemDO;
import cn.iocoder.foodnexus.module.erp.service.customerwarehouse.CustomerWarehouseService;
import cn.iocoder.foodnexus.module.erp.service.purchase.ErpPurchaseOrderServiceImpl;
import cn.iocoder.foodnexus.module.erp.service.sale.ErpSaleOutService;
import cn.iocoder.foodnexus.module.operations.dal.dataobject.inquiresupplierpush.InquireSupplierPushDO;
import cn.iocoder.foodnexus.module.operations.service.inquiresupplierpush.InquireSupplierPushService;
import cn.iocoder.foodnexus.module.order.api.CustomerOrderApi;
import cn.iocoder.foodnexus.module.order.api.CustomerOrderRecordApi;
import cn.iocoder.foodnexus.module.order.controller.app.customerOrder.vo.AppCustomerOrderItemSaveReqVO;
import cn.iocoder.foodnexus.module.order.controller.app.customerOrder.vo.AppCustomerOrderReceiptRespVO;
import cn.iocoder.foodnexus.module.order.controller.app.customerOrder.vo.AppCustomerOrderRemarkReqVO;
import cn.iocoder.foodnexus.module.order.controller.app.customerOrder.vo.AppCustomerOrderSaveReqVO;
import cn.iocoder.foodnexus.module.order.dto.CustomerAddressInfo;
import cn.iocoder.foodnexus.module.order.dal.dataobject.customerorder.CustomerOrderRemark;
import cn.iocoder.foodnexus.module.order.dal.dataobject.customerorderitem.CustomerOrderItemDO;
import cn.iocoder.foodnexus.module.order.dal.mysql.customerorderitem.CustomerOrderItemMapper;
import cn.iocoder.foodnexus.module.order.dto.CustomerOrderDTO;
import cn.iocoder.foodnexus.module.order.dto.CustomerOrderItemDTO;
import cn.iocoder.foodnexus.module.order.dto.CustomerOrderRecordEvent;
import cn.iocoder.foodnexus.module.order.enums.CustomerOrderStatus;
import cn.iocoder.foodnexus.module.order.service.customeraddress.CustomerAddressService;
import cn.iocoder.foodnexus.module.order.service.customerorderitem.CustomerOrderItemService;
import cn.iocoder.foodnexus.module.product.api.InquireCustomerApi;
import cn.iocoder.foodnexus.module.product.api.dto.CustomerVisibleProductRespDTO;
import cn.iocoder.foodnexus.module.product.api.dto.ProductInfo;
import cn.iocoder.foodnexus.module.product.service.spu.ProductSpuService;
import cn.iocoder.foodnexus.module.system.util.GenCodeUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

import cn.iocoder.foodnexus.module.order.controller.admin.customerorder.vo.*;
import cn.iocoder.foodnexus.module.order.dal.dataobject.customerorder.CustomerOrderDO;
import cn.iocoder.foodnexus.framework.common.pojo.PageResult;
import cn.iocoder.foodnexus.framework.common.util.object.BeanUtils;

import cn.iocoder.foodnexus.module.order.dal.mysql.customerorder.CustomerOrderMapper;

import static cn.iocoder.foodnexus.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.foodnexus.module.order.enums.ErrorCodeConstants.*;

/**
 * 客户总订单 Service 实现类
 *
 * @author 超级管理员
 */
@Service
@Validated
public class CustomerOrderServiceImpl implements CustomerOrderService, CustomerOrderApi {

    @Resource
    private CustomerOrderMapper customerOrderMapper;

    @Autowired
    private CustomerOrderItemService customerOrderItemService;

    @Autowired
    private CustomerOrderItemMapper customerOrderItemMapper;

    @Autowired
    private CustomerWarehouseService customerWarehouseService;

    @Autowired
    private InquireCustomerApi inquireCustomerApi;

    @Autowired
    private ErpCustomerApi customerApi;

    @Autowired
    private ErpWarehouseApi warehouseApi;

    @Autowired
    private CustomerAddressService addressService;

    @Autowired
    private InquireSupplierPushService inquireSupplierPushService;

    @Autowired
    private ErpSupplierApi supplierApi;

    @Autowired
    @Lazy
    private CustomerOrderRecordApi orderRecordApi;

    @Autowired
    private ProductSpuService productSpuService;

    @Autowired
    @Lazy
    private ErpSaleOutService saleOutService;

    @Autowired
    private GenCodeUtils genCodeUtils;

    @Override
    public Long createCustomerOrder(CustomerOrderSaveReqVO createReqVO) {
        // 插入
        CustomerOrderDO customerOrder = BeanUtils.toBean(createReqVO, CustomerOrderDO.class);
        customerOrderMapper.insert(customerOrder);

        // 返回
        return customerOrder.getId();
    }

    @Override
    public void updateCustomerOrder(CustomerOrderSaveReqVO updateReqVO) {
        // 校验存在
        validateCustomerOrderExists(updateReqVO.getId());
        // 更新
        CustomerOrderDO updateObj = BeanUtils.toBean(updateReqVO, CustomerOrderDO.class);
        customerOrderMapper.updateById(updateObj);
    }

    @Override
    public void deleteCustomerOrder(Long id) {
        // 校验存在
        validateCustomerOrderExists(id);
        // 删除
        customerOrderMapper.deleteById(id);
    }

    @Override
        public void deleteCustomerOrderListByIds(List<Long> ids) {
        // 删除
        customerOrderMapper.deleteByIds(ids);
        }


    private void validateCustomerOrderExists(Long id) {
        if (customerOrderMapper.selectById(id) == null) {
            throw exception(CUSTOMER_ORDER_NOT_EXISTS);
        }
    }

    @Override
    public CustomerOrderDO getCustomerOrder(Long id) {
        return customerOrderMapper.selectById(id);
    }

    @Override
    public PageResult<CustomerOrderDO> getCustomerOrderPage(CustomerOrderPageReqVO pageReqVO) {
        return customerOrderMapper.selectPage(pageReqVO);
    }

    /**
     * 客户创建订单
     *
     * @param createReqVO
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @CustomerVisible
    public Long appCreateCustomerOrder(AppCustomerOrderSaveReqVO createReqVO) {
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        Long customerId = customerApi.queryCustomerIdByUserId(loginUser.getId());
        this.validCustomerOrder(createReqVO, customerId);
        CustomerVisibleProductRespDTO dto = inquireCustomerApi.queryCustomerIdByCustomerId(customerId);
        Map<Long, CustomerVisibleProductRespDTO.CustomerProduct> customerProductMap = CommonUtil.listConvertMap(dto.getItems(), CustomerVisibleProductRespDTO.CustomerProduct::getProductId);
        Set<Long> supplierSet = new HashSet<>();
        Set<Long> procuctSet = new HashSet<>();
        for (AppCustomerOrderItemSaveReqVO item : createReqVO.getOrderItems()) {
            if (customerProductMap.containsKey(item.getProductId())) {
                CustomerVisibleProductRespDTO.CustomerProduct customerProduct = customerProductMap.get(item.getProductId());
                item.setOrderItemPrice(customerProduct.getSupplierQuote());
                item.setOrderItemTotal(customerProduct.getSupplierQuote() * item.getOrderItemQuantity());
                InquireSupplierPushDO inquireSupplierPush = inquireSupplierPushService.getInquireSupplierPush(customerProduct.getInquireSupplierId());
                procuctSet.add(inquireSupplierPush.getProductId());

                item.setSupplierId(inquireSupplierPush.getSupplierId());
                item.setSupplierName(supplierApi.queryNameById(inquireSupplierPush.getSupplierId()));
                supplierSet.add(inquireSupplierPush.getSupplierId());
            }
        }

        // 数据补全
        CustomerOrderSaveReqVO createOrder = BeanUtils.toBean(createReqVO, CustomerOrderSaveReqVO.class);
        createOrder.setCode(genCodeUtils.createAmBatch("XS"));
        createOrder.setOrderStatus(CustomerOrderStatus.ORDER_SUCCESS);
        createOrder.setCustomerId(customerId);
        createOrder.setWarehouseInfo(warehouseApi.getInfoByWarehouseAreaId(createOrder.getWarehouseAreaId()));
        createOrder.setAddressInfo(BeanUtils.toBean(addressService.getCustomerAddress(createReqVO.getAddressId()), CustomerAddressInfo.class));
        createOrder.setSupplierCount(supplierSet.size());
        createOrder.setProductCount(procuctSet.size());
        // 子订单补全
        AtomicReference<Integer> orderAmount = new AtomicReference<>(0);
        List<CustomerOrderItemDO> collect = createReqVO.getOrderItems().stream().map(orderItem -> {
            CustomerOrderItemDO item = BeanUtils.toBean(orderItem, CustomerOrderItemDO.class);
            item.setCustomerId(customerId);
            item.setProductInfo(BeanUtils.toBean(productSpuService.getSpu(item.getProductId()),
                    ProductInfo.class));
            item.setProductName(item.getProductInfo().getName());
            item.setSignedQuantity(0);
            item.setSignedTotal(0);
            orderAmount.updateAndGet(v -> v + item.getOrderItemTotal());
            return item;
        }).collect(Collectors.toList());

        createOrder.setOrderAmount(orderAmount.get());

        Long customerOrderId = this.createCustomerOrder(createOrder);
        customerOrderItemService.createBatch(collect, customerOrderId);

        // 拆分采购订单
        orderSplitPurchase(customerOrderId);

        // 订单记录
        CustomerOrderRecordEvent event = new CustomerOrderRecordEvent();
        event.setOrderStatus(CustomerOrderStatus.ORDER_SUCCESS);
        event.setCustomerOrderId(customerOrderId);
        event.setCopyWriter(CommonUtil.asList(String.valueOf(createOrder.getOrderAmount()),
                String.valueOf(createOrder.getProductCount())));
        orderRecordApi.recordEvent(event);

        return customerOrderId;
    }

    /**
     * 客户修改订单
     *
     * @param updateReqVO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @CustomerVisible
    public void appUpdateCustomerOrder(AppCustomerOrderSaveReqVO updateReqVO) {
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        CustomerOrderDO customerOrder = getCustomerOrder(updateReqVO.getId());
        if (CommonUtil.isEmpty(customerOrder)) {
            throw exception(CUSTOMER_ORDER_NOT_EXISTS);
        }
        Long customerId = customerApi.queryCustomerIdByUserId(loginUser.getId());
        this.validCustomerOrder(updateReqVO, customerId);
        // TODO 具体修改什么待定

        // TODO 采购订单拆分更新

    }

    /**
     * 取消订单
     *
     * @param reqVO
     */
    @Override
    public void cancel(AppCustomerOrderRemarkReqVO reqVO) {
        CustomerOrderDO customerOrder = getCustomerOrder(reqVO.getId());
        if (CommonUtil.isEmpty(customerOrder)) {
            throw exception(CUSTOMER_ORDER_NOT_EXISTS);
        }
        // TODO ... 取消订单校验待定（订单状态）
        CustomerOrderStatus orderStatus = customerOrder.getOrderStatus();
        if (orderStatus.equals(CustomerOrderStatus.ORDER_SUCCESS) ||
                orderStatus.equals(CustomerOrderStatus.ORDER_MATCH)) {
            CustomerOrderRemark orderRemark = BeanUtils.toBean(reqVO, CustomerOrderRemark.class, item -> {
                item.setOperTime(LocalDateTime.now());
            });
            customerOrderMapper.update(Wrappers.<CustomerOrderDO>lambdaUpdate()
                    .set(CustomerOrderDO::getOrderRemark, orderRemark)
                    .eq(CustomerOrderDO::getId, reqVO.getId()));

            // 订单记录
            CustomerOrderRecordEvent event = new CustomerOrderRecordEvent();
            event.setOrderStatus(CustomerOrderStatus.CANCEL);
            event.setCustomerOrderId(reqVO.getId());
            orderRecordApi.recordEvent(event);
        } else {
            throw exception(CUSTOMER_ORDER_STATUS_ERROR);
        }
    }

    /**
     * 签收订单
     *
     * @param reqVO
     */
    @Override
    public void receipt(AppCustomerOrderRemarkReqVO reqVO) {
        // TODO .. 校验订单
        CustomerOrderDO customerOrder = getCustomerOrder(reqVO.getId());
        if (CommonUtil.isEmpty(customerOrder)) {
            throw exception(CUSTOMER_ORDER_NOT_EXISTS);
        }
        if (!customerOrder.getOrderStatus().equals(CustomerOrderStatus.ARRIVAL)) {
            throw exception(CUSTOMER_ORDER_STATUS_ERROR);
        }
        List<CustomerOrderItemDO> customerOrderItems = customerOrderItemMapper.selectList(CustomerOrderItemDO::getOrderId, reqVO.getId());
        ErpSaleOutDO saleOut = saleOutService.getSaleOutByCustomerOrderId(reqVO.getId());
        if (CommonUtil.isEmpty(saleOut)) {
            throw exception(CUSTOMER_ORDER_OUT_ERROR);
        }
        List<ErpSaleOutItemDO> saleOutItems = saleOutService.getSaleOutItemListByOutId(saleOut.getId());
        Map<Long, ErpSaleOutItemDO> productMap = CommonUtil.listConvertMap(saleOutItems, ErpSaleOutItemDO::getProductId);

        List<CustomerOrderItemDO> updateBatch = new ArrayList<>();
        customerOrderItems.forEach(customerOrderItem -> {
            CustomerOrderItemDO updateItem  = new CustomerOrderItemDO();
            updateItem.setId(customerOrderItem.getId());
            if (productMap.containsKey(customerOrderItem.getProductId())) {
                ErpSaleOutItemDO saleOutItem = productMap.get(customerOrderItem.getProductId());
                updateItem.setSignedQuantity(saleOutItem.getCount());
                CustomerOrderRemark remark = new CustomerOrderRemark();
                remark.setOperTime(saleOutItem.getCreateTime());
                remark.setRemarkFiles(saleOutItem.getRemarkFiles());
                remark.setRemark(saleOutItem.getRemark());
                updateItem.setOrderRemark(remark);
            } else {
                updateItem.setSignedQuantity(customerOrderItem.getOrderItemQuantity());
            }
            updateItem.setSignedTotal(updateItem.getSignedQuantity() * customerOrderItem.getOrderItemPrice());
            updateBatch.add(updateItem);
        });

        customerOrderItemMapper.updateBatch(updateBatch);

        // 订单记录
        CustomerOrderRecordEvent event = new CustomerOrderRecordEvent();
        event.setOrderStatus(CustomerOrderStatus.SIGN_RECEIPT);
        event.setCustomerOrderId(reqVO.getId());
        // TODO 签收单链接
        event.setCopyWriter(CommonUtil.asList("TODO 签收单链接"));
        orderRecordApi.recordEvent(event);
    }

    @Override
    public List<AppCustomerOrderReceiptRespVO> queryReceipt(Long id) {
        CustomerOrderDO customerOrder = getCustomerOrder(id);
        if (CommonUtil.isEmpty(customerOrder)) {
            throw exception(CUSTOMER_ORDER_NOT_EXISTS);
        }
        if (!customerOrder.getOrderStatus().equals(CustomerOrderStatus.ARRIVAL)) {
            throw exception(CUSTOMER_ORDER_STATUS_ERROR);
        }
        List<CustomerOrderItemDO> customerOrderItems = customerOrderItemMapper.selectList(CustomerOrderItemDO::getOrderId, id);
        ErpSaleOutDO saleOut = saleOutService.getSaleOutByCustomerOrderId(id);
        if (CommonUtil.isEmpty(saleOut)) {
            throw exception(CUSTOMER_ORDER_OUT_ERROR);
        }
        List<ErpSaleOutItemDO> saleOutItems = saleOutService.getSaleOutItemListByOutId(saleOut.getId());
        Map<Long, ErpSaleOutItemDO> productMap = CommonUtil.listConvertMap(saleOutItems, ErpSaleOutItemDO::getProductId);

        return CommonUtil.listConvert(customerOrderItems, customerOrderItem -> {
            AppCustomerOrderReceiptRespVO result = new AppCustomerOrderReceiptRespVO();
            result.setProductId(customerOrderItem.getProductId());
            if (productMap.containsKey(customerOrderItem.getProductId())) {
                ErpSaleOutItemDO saleOutItem = productMap.get(customerOrderItem.getProductId());
                result.setSignedQuantity(saleOutItem.getCount());
                result.setRemark(saleOutItem.getRemark());
                result.setRemarkFiles(saleOutItem.getRemarkFiles());
            } else {
                result.setSignedQuantity(customerOrderItem.getOrderItemQuantity());
            }
            return result;
        });
    }

    /**
     * {@link ErpPurchaseOrderServiceImpl#orderSplit(PurchaseOrderSplitEvent)}
     */
    private void orderSplitPurchase(Long customerOrderId) {
        PurchaseOrderSplitEvent event = new PurchaseOrderSplitEvent();
        event.setCustomerOrderId(customerOrderId);
        SpringUtils.getApplicationContext().publishEvent(event);
    }

    private void validCustomerOrder(AppCustomerOrderSaveReqVO createReqVO, Long customerId) {
        if (!warehouseApi.existsById(createReqVO.getWarehouseId())) {
            throw exception(CUSTOMER_ORDER_WAREHOUSE_NOEXISTS);
        }
        if (!warehouseApi.existsById(createReqVO.getWarehouseAreaId())) {
            throw exception(CUSTOMER_ORDER_WAREHOUSE_NOEXISTS);
        }
        if (!customerWarehouseService.exists(createReqVO.getWarehouseAreaId(), customerId)) {
            throw exception(CUSTOMER_WAREHOUSE_NOT_BIND);
        }
        if (!addressService.existsById(createReqVO.getAddressId())) {
            throw exception(CUSTOMER_ORDER_ADDRESS_NOEXISTS);
        }
    }

    @Override
    public CustomerOrderDTO queryById(Long customerOrderId) {
        return BeanUtils.toBean(customerOrderMapper.selectById(customerOrderId), CustomerOrderDTO.class);
    }

    @Override
    public List<CustomerOrderItemDTO> queryItemsByOrderId(Long customerOrderId) {
        return BeanUtils.toBean(customerOrderItemMapper.selectList(CustomerOrderItemDO::getOrderId, customerOrderId),
                CustomerOrderItemDTO.class);
    }

    @Override
    public void updateOrderStatus(Long customerOrderId, CustomerOrderStatus updateOrderStatus) {
        customerOrderMapper.update(Wrappers.<CustomerOrderDO>lambdaUpdate()
                .set(CustomerOrderDO::getOrderStatus, updateOrderStatus)
                .eq(CustomerOrderDO::getId, customerOrderId));
    }
}