package com.izouma.nineth.repo;

import com.izouma.nineth.domain.Order;
import com.izouma.nineth.enums.CollectionSource;
import com.izouma.nineth.enums.OrderStatus;
import com.izouma.nineth.enums.PayMethod;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;

import javax.transaction.Transactional;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Optional;

public interface OrderRepo extends JpaRepository<Order, Long>, JpaSpecificationExecutor<Order> {
    @Query("update Order t set t.del = true where t.id = ?1")
    @Modifying
    @Transactional
    void softDelete(Long id);

    Optional<Order> findByIdAndDelFalse(Long id);

    List<Order> findByStatusAndCreatedAtBeforeAndDelFalse(OrderStatus status, LocalDateTime time);

    List<Order> findByCollectionId(Long collectionId);

    int countByUserIdAndCollectionIdAndStatusIn(Long userId, Long collectionId, Iterable<OrderStatus> orderStatuses);

    int countByUserIdAndCountIdAndStatusIn(Long userId, String countId, Iterable<OrderStatus> orderStatuses);

    List<Order> findByStatus(OrderStatus orderStatus);

    @Query("select count(o) from Order o join Collection c on o.collectionId = c.id " +
            "where c.minterId = ?1 and c.source = 'OFFICIAL' and o.status <> 'NOT_PAID' and o.status <> 'CANCELLED'")
    long countSales(Long userId);

    Order findByTransactionId(String txId);

    long countAllByPayTimeAfter(LocalDateTime payTime);

    @Query("select id from Order where userId = ?1 and opened = false")
    List<Long> findAllByUserIdAndOpenedFalse(Long userId);

    int countByUserIdAndCollectionIdAndVipTrueAndStatusIn(Long userId, Long collectionId, Collection<OrderStatus> status);

    List<Order> findAllByStatus(OrderStatus status);

    List<Order> findAllByStatusAndMinterId(OrderStatus status, Long minterId);

    List<Order> findAllByCreatedAtIsAfterAndStatusIn(LocalDateTime payTime, List<OrderStatus> status);

    List<Order> findAllByCreatedAtBetweenAndStatusIn(LocalDateTime start, LocalDateTime end, List<OrderStatus> status);

    List<Order> findAllByCreatedAtIsAfterAndMinterIdAndStatusIn(LocalDateTime payTime, Long minterId, List<OrderStatus> status);

    List<Order> findAllByCreatedAtBetweenAndMinterIdAndStatusIn(LocalDateTime start, LocalDateTime end, Long minterId, List<OrderStatus> status);

    @Query(nativeQuery = true, value = "select user_id, sum(price) total from raex.order_info " +
            "where (status = 'FINISH' or status = 'PROCESSING') " +
            "group by user_id order by sum(price) desc limit 50")
    List<Object[]> sumPrice();

    List<Order> findByCreatedAtBetweenAndSourceAndStatusIn(LocalDateTime start, LocalDateTime end, CollectionSource source, Collection<OrderStatus> statuses);

    @Query(nativeQuery = true, value = "select o.user_id  from order_info o " +
            " WHERE o.created_at < ?2 and o.created_at > ?1 and o.status = 'CANCELLED' " +
            "GROUP BY o.user_id " +
            "HAVING count(*) > 2")
    List<Long> checkBlackList(LocalDateTime start, LocalDateTime end);

    @Query("update Order o set o.status = com.izouma.nineth.enums.OrderStatus.FINISH where o.id = ?1")
    @Transactional
    @Modifying
    int finishOrder(Long id);

    @Query("update Order o set o.status = com.izouma.nineth.enums.OrderStatus.PROCESSING, " +
            "o.payTime = ?2, o.payMethod = ?3, o.transactionId = ?4 where o.id = ?1")
    @Transactional
    @Modifying
    int processingOrder(Long id, LocalDateTime payTime, PayMethod payMethod, String transactionId);

    @Query(value = "select sum(price) from order_info where user_id = ?1 and status = 'FINISH'", nativeQuery = true)
    BigDecimal sumUserPrice(Long userId);

    @Query("select o from Order o " +
            "  join Asset a on a.id = o.assetId " +
            "  join a.tags t on t.id = ?1 " +
            "where o.userId not in ?2 " +
            "  and o.source = com.izouma.nineth.enums.CollectionSource.TRANSFER " +
            "  and o.status = com.izouma.nineth.enums.OrderStatus.FINISH")
    Page<Order> byTag(Long tagId, List<Long> excludeUserId, Pageable pageable);

    @Query("select o from Order o " +
            "  join Asset a on a.id = o.assetId " +
            "  join a.tags t on t.id = ?1 " +
            "where o.userId = ?2" +
            "  and o.source = com.izouma.nineth.enums.CollectionSource.TRANSFER " +
            "  and o.status = com.izouma.nineth.enums.OrderStatus.FINISH")
    Page<Order> byTag(Long tagId, Long userId, Pageable pageable);

    @Query(value = "select user_id from asset where id = ?1", nativeQuery = true)
    Long selectUserId(Long assetId);

    int countAllByUserIdAndCollectionIdAndStatusIn(Long userId, Long collectionId, Collection<OrderStatus> status);

    Order findFirstByCollectionIdOrderByCreatedAtDesc(Long collectionId);
}