package com.izouma.nineth.service;

import com.izouma.nineth.annotations.RedisLock;
import com.izouma.nineth.config.GeneralProperties;
import com.izouma.nineth.config.RedisKeys;
import com.izouma.nineth.domain.*;
import com.izouma.nineth.dto.PayQuery;
import com.izouma.nineth.enums.AuctionOrderStatus;
import com.izouma.nineth.enums.MintOrderStatus;
import com.izouma.nineth.enums.OrderStatus;
import com.izouma.nineth.exception.BusinessException;
import com.izouma.nineth.repo.*;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
@ConditionalOnProperty(value = "general.notify-server", havingValue = "true")
@Slf4j
@AllArgsConstructor
public class OrderCancelService {
    private final GeneralProperties             generalProperties;
    private final OrderRepo                     orderRepo;
    private final OrderService                  orderService;
    private final SysConfigRepo                 sysConfigRepo;
    private final MintOrderRepo                 mintOrderRepo;
    private final MintOrderService              mintOrderService;
    private final GiftOrderRepo                 giftOrderRepo;
    private final GiftOrderService              giftOrderService;
    private final OrderPayService               orderPayService;
    private final RedisTemplate<String, Object> redisTemplate;
    private final AuctionOrderRepo              auctionOrderRepo;
    private final AuctionOrderService           auctionOrderService;

    private static int orderCancelInterval = 210;

    public static void setOrderCancelInterval(int orderCancelInterval) {
        log.info("设置订单取消时间间隔为 {}S", orderCancelInterval);
        OrderCancelService.orderCancelInterval = orderCancelInterval;
    }

    @PostConstruct
    public void init() {
        orderCancelInterval = sysConfigRepo.findByName("order_cancel_time")
                .map(SysConfig::getValue).map(Integer::parseInt).orElse(210);
    }

    @Scheduled(fixedRate = 30000, initialDelay = 10000)
    @RedisLock(value = "order_batch_cancel", expire = 3, unit = TimeUnit.MINUTES)
    public void batchCancel() {
        List<Order> orders = orderRepo.findByStatusAndCreatedAtBeforeAndDelFalse(OrderStatus.NOT_PAID,
                LocalDateTime.now().minusSeconds(orderCancelInterval));
        orders.parallelStream().forEach(o -> {
            try {
                Order order = orderRepo.findById(o.getId()).orElseThrow(new BusinessException("订单不存在"));
                if (order.getStatus() == OrderStatus.NOT_PAID && canCancel(order.getId().toString())) {
                    orderService.cancel(order);
                }
            } catch (Exception e) {
                log.error("取消订单错误 " + o.getId(), e);
            }
        });
    }

    @Scheduled(fixedRate = 30000, initialDelay = 20000)
    @RedisLock(value = "mint_order_batch_cancel", expire = 3, unit = TimeUnit.MINUTES)
    public void batchCancelMintOrder() {
        List<MintOrder> orders = mintOrderRepo.findByStatusAndCreatedAtBeforeAndDelFalse(MintOrderStatus.NOT_PAID,
                LocalDateTime.now().minusSeconds(orderCancelInterval));
        orders.forEach(o -> {
            try {
                MintOrder order = mintOrderRepo.findById(o.getId()).orElseThrow(new BusinessException("订单不存在"));
                if (order.getStatus() == MintOrderStatus.NOT_PAID && canCancel(order.getId().toString())) {
                    mintOrderService.cancel(order, false);
                }
            } catch (Exception ignored) {
            }
        });
    }

    @Scheduled(fixedRate = 30000, initialDelay = 30000)
    @RedisLock(value = "gift_order_batch_cancel", expire = 3, unit = TimeUnit.MINUTES)
    public void batchCancelGiftOrder() {
        List<GiftOrder> orders = giftOrderRepo.findByStatusAndCreatedAtBeforeAndDelFalse(OrderStatus.NOT_PAID,
                LocalDateTime.now().minusSeconds(orderCancelInterval));
        orders.forEach(o -> {
            try {
                GiftOrder order = giftOrderRepo.findById(o.getId()).orElseThrow(new BusinessException("订单不存在"));
                if (order.getStatus() == OrderStatus.NOT_PAID && canCancel(order.getId().toString())) {
                    giftOrderService.cancel(order);
                }
            } catch (Exception ignored) {
            }
        });
    }

    private boolean canCancel(String id) {
        String channel = null;
        Object payTmp = redisTemplate.opsForValue().get(RedisKeys.PAY_TMP + id);
        log.info("payTmp {}", payTmp);
        if (payTmp != null) {
            channel = (String) payTmp;
        }
        PayQuery query = new PayQuery();
        try {
            query = orderPayService.query(id, channel);
        } catch (Exception e) {
            query.setExist(false);
        }
        if (query.isExist()) {
            if (query.getStatus() != null) {
                switch (query.getStatus()) {
                    case SUCCESS:
                    case PENDING:
                        log.info("订单 {}, 状态 {}, 不能取消", id, query.getStatus().name());
                        return false;
                    default:
                        log.info("订单 {}, 状态 {}, 可以取消", id, query.getStatus().name());
                        return true;
                }
            }
        }
        return true;
    }

    @Scheduled(fixedRate = 30000)
    public void batchCancelledAuctionOrder() {
        List<AuctionOrder> orders = auctionOrderRepo
                .findByStatusAndCreatedAtBeforeAndDelFalse(AuctionOrderStatus.NOT_PAID,
                        LocalDateTime.now().minusSeconds(orderCancelInterval));
        orders.parallelStream().forEach(o -> {
            try {
                AuctionOrder order = auctionOrderRepo.findById(o.getId()).orElseThrow(new BusinessException("订单不存在"));
                if (order.getStatus() == AuctionOrderStatus.NOT_PAID) {
                    auctionOrderService.cancel(order);
                }
            } catch (Exception e) {
                log.error("取消拍卖订单错误 " + o.getId(), e);
            }
        });
    }
}
