package com.fzm.mall.server.admin.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.SystemClock;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fzm.mall.server.admin.block_chain.util.ChainUtil;
import com.fzm.mall.server.admin.config.Redis;
import com.fzm.mall.server.admin.constant.MallResponseError;
import com.fzm.mall.server.admin.distribution.mapper.ParcelCenterMapper;
import com.fzm.mall.server.admin.distribution.service.IExpressService;
import com.fzm.mall.server.admin.exception.MyException;
import com.fzm.mall.server.admin.goods_center.mapper.GoodsSkuUsufructMapper;
import com.fzm.mall.server.admin.goods_center.mapper.SpuMapper;
import com.fzm.mall.server.admin.home_page.model.Merchant;
import com.fzm.mall.server.admin.home_page.service.IMerchantService;
import com.fzm.mall.server.admin.mq.MsgProducer;
import com.fzm.mall.server.admin.mq.enums.QueueTTLTypeEnum;
import com.fzm.mall.server.admin.order.entity.dto.DeliveryDTO;
import com.fzm.mall.server.admin.order.entity.dto.TranDto;
import com.fzm.mall.server.admin.order.mapper.DeliveryMapper;
import com.fzm.mall.server.admin.order.model.Config;
import com.fzm.mall.server.admin.order.model.Delivery;
import com.fzm.mall.server.admin.order.model.DeliveryLog;
import com.fzm.mall.server.admin.order.service.IDeliveryLogService;
import com.fzm.mall.server.admin.order.service.IDeliveryRefundService;
import com.fzm.mall.server.admin.order.service.IDeliveryService;
import com.fzm.mall.server.admin.user.model.Asset;
import com.fzm.mall.server.admin.user.service.IAssetService;
import com.fzm.mall.server.admin.user_center.model.User;
import com.fzm.mall.server.admin.user_center.service.IUserService;
import com.fzm.mall.server.admin.util.CopyUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 * 用户提货表 服务实现类
 * </p>
 *
 * @author wulixian
 * @since 2021-03-18
 */
@Service
@Transactional
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class DeliveryServiceImpl extends ServiceImpl<DeliveryMapper, Delivery> implements IDeliveryService {

    @Value("${pay.pay-in-chain}")
    private Boolean payInChain;

    private final DeliveryMapper deliveryMapper;
    private final ParcelCenterMapper parcelCenterMapper;
    private final IDeliveryLogService deliveryLogService;
    private final IAssetService assetService;
    private final IExpressService expressService;
    private final Redis redis;
    private final MsgProducer producer;
    private final IDeliveryRefundService deliveryRefundService;
    private final IUserService userService;
    private final IMerchantService merchantService;
    private final ChainUtil chainUtil;
    private final SpuMapper spuMapper;
    private final GoodsSkuUsufructMapper goodsSkuUsufructMapper;

    @Override
    public void send(String adminId, String orderDeliveryId, String expressType, String expressNo, Long updateTime) {

        /**
         * 提货单操作记录
         */
        DeliveryLog log = new DeliveryLog(orderDeliveryId, Delivery.DELIVERY_STATE_TO_SEND, DeliveryLog.DELIVERY_SENDED, adminId, null);
        deliveryLogService.save(log);

        UpdateWrapper<Delivery> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("order_delivery_id", orderDeliveryId)
                .eq("update_time", updateTime);
        Delivery delivery = new Delivery();
        delivery.setExpressType(expressType);
        delivery.setExpressNo(expressNo);
        delivery.setDeliveryState(Delivery.DELIVERY_STATE_SENDED);
        delivery.setOrderDeliveryId(orderDeliveryId);
        delivery.setDeliveryTime(SystemClock.now());
        //发货时重置物流详情为null，避免前台生成提货订单是，物流信息为"[]"
        delivery.setExpressDetail(null);
        int update = deliveryMapper.update(delivery, updateWrapper);
        if (update == 0) {
            throw new MyException(MallResponseError.UPDATE_FAIL);
        }

        /**
         * 开启提货订单到期自动关单
         */
        String configStr = redis.getKey(Config.ORDER_CONFIG_REDIS_KEY);
        Config config = JSON.parseObject(configStr, Config.class);
        //单位：天 -》转为秒
        Long goodsReceivedLimit = config.getGoodsReceivedLimit() * 24 * 60 * 60 * 1000;
        Map<String, Object> map = new HashMap<>();
        map.put("orderDeliveryId", delivery.getOrderDeliveryId());
        producer.sendTTL(map, QueueTTLTypeEnum.ORDER_DELIVERY_TTL, goodsReceivedLimit);
    }

    @Override
    public void examine(String adminId, String orderDeliveryId, String addressId, Boolean isApproved, String remark, Long updateTime) {
        String operation = DeliveryLog.DELIVERY_EXAMINE_FAILED;
        Integer delliveryState = Delivery.DELIVERY_STATE_REFUSED;
        if (isApproved) {
            operation = DeliveryLog.DELIVERY_EXAMINE_SUCCESS;
            delliveryState = Delivery.DELIVERY_STATE_RETURN_TO_BE_DESIGNED;
        }

        UpdateWrapper<Delivery> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("order_delivery_id", orderDeliveryId)
                .eq("update_time", updateTime)
                .set("delivery_state", delliveryState);
        int update = deliveryMapper.update(new Delivery(), updateWrapper);
        if (update == 0) {
            throw new MyException(MallResponseError.UPDATE_FAIL);
        }

        deliveryRefundService.refundExamine(orderDeliveryId, isApproved, remark, addressId);
        /**
         * 提货单操作记录
         */
        DeliveryLog log = new DeliveryLog(orderDeliveryId, Delivery.DELIVERY_STATE_TO_REFUND, operation, adminId, remark);
        deliveryLogService.save(log);
    }

    @Override
    public DeliveryDTO read(String orderDeliveryId) {
        Delivery delivery = deliveryMapper.read(orderDeliveryId);
        delivery.listExpressDetail(expressService);
        delivery.listGoods(parcelCenterMapper, orderDeliveryId);
        delivery.listLogs(deliveryLogService, orderDeliveryId);
        delivery.listReturnImg();

        return CopyUtil.to(delivery, DeliveryDTO.class);
    }

    @Override
    public IPage<DeliveryDTO> page(Page<DeliveryDTO> page, String uid, String orderDeliveryId, String gid, Integer deliveryState, String merchantId) {
        IPage<DeliveryDTO> result = deliveryMapper.list(page, uid, orderDeliveryId, gid, deliveryState, merchantId);
        return result;
    }

    @Override
    public void returnReceiptExamine(String adminId, String orderDeliveryId, Boolean isApproved, String remark, Long updateTime) {
        String operation = DeliveryLog.DELIVERY_RETURN_RECEIPT_EXAMINE_FAILED;
        Integer delliveryState = Delivery.DELIVERY_STATE_CLOSED;
        if (isApproved) {
            operation = DeliveryLog.DELIVERY_RETURN_RECEIPT_EXAMINE_SUCCESS;
            delliveryState = Delivery.DELIVERY_STATE_REFUNDED;
            /**
             * 退还提货的币
             */
            refundCoin(orderDeliveryId);
        }

        UpdateWrapper<Delivery> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("order_delivery_id", orderDeliveryId)
                .eq("update_time", updateTime)
                .set("delivery_state", delliveryState);
        int update = deliveryMapper.update(new Delivery(), updateWrapper);
        if (update == 0) {
            throw new MyException(MallResponseError.UPDATE_FAIL);
        }

        deliveryRefundService.returnReceiptExamine(orderDeliveryId, isApproved, remark);

        /**
         * 提货单操作记录
         */
        DeliveryLog log = new DeliveryLog(orderDeliveryId, Delivery.DELIVERY_STATE_TO_REFUND, operation, adminId, remark);
        deliveryLogService.save(log);
    }

    /**
     * 提货单退货，返回币到用户资产
     */
    private void refundCoin(String orderDeliveryId) {
        QueryWrapper<Delivery> queryWrapper = new QueryWrapper<Delivery>().eq("order_delivery_id", orderDeliveryId)
                .eq("is_delete", false);
        Delivery delivery = deliveryMapper.selectOne(queryWrapper);

        QueryWrapper<Asset> wrapper = new QueryWrapper<Asset>().eq("uid", delivery.getUid())
                .eq("coin", delivery.getCoin());
        Asset asset = assetService.getOne(wrapper);
        BigDecimal number = new BigDecimal(delivery.getNumber());
        BigDecimal amount = asset.getAmount() == null ? BigDecimal.ZERO.add(number) : asset.getAmount().add(number);
        asset.setAmount(amount);
        BigDecimal frozen = asset.getFrozen();
        BigDecimal result = frozen.subtract(number);
        frozen = result.compareTo(BigDecimal.ZERO) > 0 ? result : BigDecimal.ZERO;
        asset.setFrozen(frozen);
        UpdateWrapper updateWrapper = new UpdateWrapper<Asset>().eq("uid", asset.getUid())
                .eq("coin", asset.getCoin())
                .eq("update_time", asset.getUpdateTime());
        boolean update = assetService.update(asset, updateWrapper);
        if (!update) {
            throw new MyException(MallResponseError.UPDATE_FAIL);
        }
        /**
         * 当前用户提货没有走链，提货的币并没有给商户，只是数据库冻结，用户提货单“确认收货”或“未收货”n天之后，因此不需要在退货时讲商品币由商家还给用户
         */
//        /**
//         * 1.调用链上方法将币还给用户 , 成功后，将order_delivery_refund的state改为5退款成功
//         * 2.nft不支持提货
//         */
//        User user = userService.getOne(new QueryWrapper<User>().eq("uid", delivery.getUid()));
//        Merchant merchant = merchantService.getOne(new QueryWrapper<Merchant>().eq("merchant_id", delivery.getMerchantId()));
//        assetService.refundCoinOnChain(TranDto.TABLE_TYPE_DELIVERY, delivery.getOrderDeliveryId(), delivery.getCoin(),
//                delivery.getNumber(), null, merchant.getTokenIssueAddr(), user.getAddress());
    }
}
