package com.izouma.nineth.service;

import com.izouma.nineth.config.*;
import com.izouma.nineth.domain.*;
import com.izouma.nineth.dto.PageQuery;
import com.izouma.nineth.dto.PageWrapDTO;
import com.izouma.nineth.enums.*;
import com.izouma.nineth.exception.BusinessException;
import com.izouma.nineth.lock.RedisLockable;
import com.izouma.nineth.repo.*;
import com.izouma.nineth.utils.IPUtils;
import com.izouma.nineth.utils.JpaUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.transaction.Transactional;
import java.math.BigDecimal;
import java.time.LocalDateTime;

@Service
@AllArgsConstructor
@Slf4j
public class GiftOrderService {

    private AssetRepo         assetRepo;
    private UserRepo          userRepo;
    private SysConfigService  sysConfigService;
    private GiftOrderRepo     giftOrderRepo;
    private AssetService      assetService;
    private    ErrorOrderRepo    errorOrderRepo;
    private RedisTemplate<String, Object> redisTemplate;

    private final EntityManager entityManager;


    /**
     * 转让订单创建
     * @param assetId
     * @param toUserId
     * @return
     */
    @RedisLockable(prefix = RedisKeys.OPERATION_ASSET_LOCK, key="#userId + '_' + #assetId")
    @Transactional
    public GiftOrder createGiftOrder(Long userId, Long assetId, Long toUserId) {
        //锁定用户持有资产
        Asset asset = assetRepo.findByIdForUpdate(assetId).orElseThrow(new BusinessException("资产不存在"));
        //上架检查
        assetService.onShelfCheck(asset);

        if (!asset.getUserId().equals(userId)) {
            throw new BusinessException("无权限");
        }
        if (toUserId.equals(userId)) {
            throw new BusinessException("不能送给自己");
        }
        if (asset.getStatus() != AssetStatus.NORMAL) {
            throw new BusinessException("当前状态不可转赠");
        }
        if (asset.isConsignment()) {
            throw new BusinessException("请先取消寄售");
        }

//        int holdDays = sysConfigService.getInt(SysConstants.HOLD_DAYS);
//        if (ChronoUnit.DAYS.between(asset.getCreatedAt(), LocalDateTime.now()) < holdDays) {
//            throw new BusinessException("需持有满" + holdDays + "天才能转赠");
//        }

        asset.setStatus(AssetStatus.GIFTING);
        assetRepo.save(asset);

        //暂时使用统一配置
        BigDecimal gasPrice = sysConfigService.getBigDecimal(SysConstants.GIFT_GAS_FEE);
        GiftOrder giftOrder = GiftOrder.builder()
                .userId(userId)
                .assetId(assetId)
                .collectionId(asset.getCollectionId())
                .name(asset.getName())
                .toUserId(toUserId)
                .gasPrice(gasPrice)
                .status(OrderStatus.NOT_PAID)
                .projectId(asset.getProjectId())
                .ip(IPUtils.getIpAddr())
                .build();
        return giftOrderRepo.save(giftOrder);
    }


    /**
     * 转赠支付通知
     * @param giftOrderId
     * @param payMethod
     * @param transactionId
     */
    @RedisLockable(prefix = RedisKeys.GIFT_ORDER_LOCK, key="#giftOrderId", expiration = 360)
    @Transactional
    public void giftNotify(Long giftOrderId, PayMethod payMethod, String transactionId) {
        GiftOrder giftOrder = giftOrderRepo.findById(giftOrderId).orElseThrow(new BusinessException("订单不存在"));
        if (giftOrder.getStatus() != OrderStatus.NOT_PAID) {
            throw new BusinessException("转赠订单状态错误 " + giftOrder.getStatus());
        }

        Asset asset = assetRepo.findById(giftOrder.getAssetId()).orElseThrow(new BusinessException("资产不存在"));
        User newOwner = userRepo.findById(giftOrder.getToUserId()).orElseThrow(new BusinessException("用户不存在"));

        giftOrder.setPayMethod(payMethod);
        giftOrder.setStatus(OrderStatus.FINISH);
        giftOrder.setTransactionId(transactionId);
        giftOrder.setPayTime(LocalDateTime.now());
        if(payMethod == null) {
            giftOrder.setPayMethod(PayMethod.ALIPAY);
        }
        assetService.transfer(asset, asset.getPrice(), newOwner,  OperationType.give.getDescription(), null);
    }


    @RedisLockable(prefix = RedisKeys.GIFT_ORDER_LOCK, key="#giftOrderId", expiration = 360)
    @Transactional
    public void cancel(Long giftOrderId) {
        GiftOrder giftOrder = giftOrderRepo.findById(giftOrderId).orElseThrow(new BusinessException("转让订单不存在"));
        if (giftOrder.getStatus() != OrderStatus.NOT_PAID) {
            throw new BusinessException("不是待支付订单无法取消:" + giftOrder.getId());
        }

        Asset asset = assetRepo.findById(giftOrder.getAssetId()).orElseThrow(new BusinessException("藏品不存在"));
        asset.setStatus(AssetStatus.NORMAL);
        assetRepo.save(asset);

        giftOrder.setStatus(OrderStatus.CANCELLED);
        giftOrder.setCancelTime(LocalDateTime.now());
        giftOrderRepo.save(giftOrder);
    }

    public PageWrapDTO<GiftOrder> all(PageQuery pageQuery) {
        Specification<GiftOrder> specification = JpaUtils.toSpecification(pageQuery, GiftOrder.class);
        Page<GiftOrder> page = giftOrderRepo.findAll(specification, JpaUtils.toPageRequest(pageQuery));
        PageWrapDTO<GiftOrder> pageWrapDTO = new PageWrapDTO<>(page.toList(), page.getPageable(), page.getTotalElements());
        BigDecimal totalAmount = sumGasAmount(specification);
        pageWrapDTO.setTotalAmount(totalAmount);
        return pageWrapDTO;
    }

    public BigDecimal sumGasAmount(Specification specification) {
        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
        CriteriaQuery<BigDecimal> criteriaQuery = criteriaBuilder.createQuery(BigDecimal.class);
        Root<GiftOrder> root = criteriaQuery.from(GiftOrder.class);
        criteriaQuery.select(
                criteriaBuilder.sum(root.get("gasPrice"))
        );
        Predicate predicate = specification.toPredicate(root, criteriaQuery, criteriaBuilder);
        criteriaQuery.where(predicate);
        TypedQuery<BigDecimal> query = entityManager.createQuery(criteriaQuery);
        return query.getSingleResult();
    }
}
