package com.izouma.nineth.repo;

import com.izouma.nineth.domain.Asset;
import com.izouma.nineth.enums.AssetStatus;
import org.springframework.data.jpa.repository.*;
import org.springframework.data.repository.query.Param;

import javax.persistence.LockModeType;
import javax.transaction.Transactional;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

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

    long countByIpfsUrlAndStatusNot(String ipfsUrl, AssetStatus status);

    List<Asset> findByCollectionId(Long collectionId);

    List<Asset> findByCollectionIdAndStatusIn(Long collectionId, Iterable<AssetStatus> statuses);

    List<Asset> findByCreatedAtBefore(LocalDateTime localDateTime);

    List<Asset> findByConsignmentTrue();

    List<Asset> findByTokenIdIn(Iterable<String> tokenId);

    List<Asset> findByTokenIdOrderByCreatedAt(String tokenId);

    List<Asset> findByOrderId(Long orderId);

    @Query("select a from Asset a join Order o on o.assetId = a.id join Asset aa on aa.orderId = o.id where a.id = ?1")
    Optional<Asset> findChild(Long id);

    @Query("select a from Asset a join User u on a.userId = u.id where a.consignment = true and u.settleAccountId is null")
    List<Asset> findNoAccount();

    List<Asset> findByUserId(Long userId);

    Asset findFirstByTokenIdAndCreatedAtAfterOrderByCreatedAt(String tokenId, LocalDateTime time);

    @Query("select a from Asset a where a.txHash is null and a.tokenId is not null " +
            "and a.status = com.izouma.nineth.enums.AssetStatus.NORMAL and a.createdAt < ?1 " +
            "order by a.createdAt desc")
    List<Asset> toMint(LocalDateTime time);

    @Query("select id from Asset where status = ?1")
    List<Long> findAllByStatus(AssetStatus status);

    @Query(nativeQuery = true, value = "select asset.* from asset " +
            "join collection_info on asset.collection_id = collection_info.id " +
            "where asset.number > collection_info.total and status = 'NORMAL'")
    List<Asset> findOverNumber();

    @Query(nativeQuery = true, value = "select * from asset " +
            "where name = ?1 " +
            "and number <= ?2 and public_show = false and consignment = false " +
            "and modified_at < date_sub(now(), INTERVAL 7 DAY) " +
            "and number > 5 and status = 'NORMAL' " +
            "order by modified_at")
    List<Asset> findColdAsset(String name, int number);

    @Query(value = "select c.id, c.pic, c.model3d, c.minter_avatar, c.owner_avatar, c.detail from asset c", nativeQuery = true)
    List<List<String>> selectResource();

    @Modifying
    @Transactional
    @Query(value = "update asset c set c.pic = ?2, c.model3d = ?3, c.minter_avatar = ?4, " +
            "c.owner_avatar = ?5, c.detail = ?6 where c.id = ?1", nativeQuery = true)
    int updateCDN(Long id, String pic, String model3d, String minterAvatar,
                  String ownerAvatar, String detail);

    List<Asset> findByStatusIn(Iterable<AssetStatus> statuses);

    List<Asset> findByFromAssetId(Long id);

    List<Asset> findAllByCollectionIdAndUserIdAndStatus(Long collectionId, Long userId, AssetStatus status);

    /**
     * 根据用户ID 和资产状态获得资产
     * @param userId 用户ID
     * @param status 自残状态
     * @return 资产
     */
    @Query("select asset from Asset asset where asset.projectId = ?1 " +
                "and asset.userId = ?2  and asset.status =?3 ")
    List<Asset> findAllByProjectIdAndUserIdAndStatus(Integer projectId, Long userId, AssetStatus status);


    /**
     * 根据用户ID 和资产状态获得资产
     * @param userId 用户ID
     * @param status 自残状态
     * @return 资产
     */
    @Query("select asset from Asset asset where " +
            "asset.userId = ?1  and asset.status =?2 ")
    List<Asset> findAllByUserIdAndStatus( Long userId, AssetStatus status);

    /**
     * 获取用户持有数量
     * @return
     */
    @Query("select asset from Asset asset where  asset.collectionId = ?1 and asset.projectId = ?2 " +
            " and asset.status in (com.izouma.nineth.enums.AssetStatus.NORMAL)")
    List<Asset> findAssetHeld(Long collectionId, int projectId);


    /**
     * 获取用户持有资产ID 注意  该方法只返回 id , userId , projectId
     * @return
     */
    @Query("select new Asset(asset.id ,asset.userId ,asset.projectId)  from Asset asset where  asset.collectionId = ?1 and asset.projectId = ?2 " +
            " and asset.status in (com.izouma.nineth.enums.AssetStatus.NORMAL)")
    List<Asset> findAssetHeldId(Long collectionId, int projectId);


    @Query("select asset from Asset asset where  asset.collectionId = ?1" )
    List<Asset> findAssetCollectionId(Long collectionId);

    /**
     * 获取用户持有数量
     * @return
     */
    @Query("select count(asset) from Asset asset where asset.userId = ?1" +
            " and asset.status in (com.izouma.nineth.enums.AssetStatus.NORMAL)")
    Integer findAssetNormalCount(Long userId);

    /**
     * 获取用户持有数量
     * @return
     */
    @Query("select count(asset) from Asset asset where asset.userId = ?1 and asset.collectionId = ?2 and asset.projectId = ?3 " +
            " and asset.status in (com.izouma.nineth.enums.AssetStatus.NORMAL)")
    Integer countAssetHeld(Long userId, Long collectionId, int projectId);



    /**
     * 锁定当前用户资产
     * @return
     */
    @Lock(LockModeType.PESSIMISTIC_WRITE)
    @Query("select a from Asset a where a.id = ?1")
    Optional<Asset> findByIdForUpdate(Long id);

    /**
     * 获取需要铸造的资产记录
     * @return
     */
    @Query("select a.id from Asset a join User u on u.id = a.userId where a.status = com.izouma.nineth.enums.AssetStatus.NORMAL " +
            "   and a.txHash is null and a.tokenId is not null and a.del = false ")
    List<Long> findWaitMintAssetList();

    /**
     * 查找自身拥有的资产
     * @return
     */
    @Query(value="select ac.* from asset ac where ac.user_id = ?1 and ac.collection_id = ?2 " +
            "and ac.id not in (?3) and ac.status = 'NORMAL' and ac.tx_hash is not null and ac.del = false limit 1 ",
            nativeQuery = true)
    Optional<Asset> findOwnedAsset(Long userId, Long collectionId, Set<Long> exclusiveAssetIds);

    /**
     * 查找已售的资产数量
     * @param collectionId
     * @param number
     * @return
     */
    @Query(value="select count(1) from asset ac where ac.collection_id = ?1 and ac.number = ?2 " +
            "   and ac.del = false", nativeQuery = true)
    Integer findSaleAssetCount(Long collectionId, Integer number);


    /**
     * 查询低价格售卖的资产
     * @param limitPrice
     * @return
     */
    @Query(value="select ac from Asset ac where ac.del = false and ac.consignment = true " +
            " and ac.status = com.izouma.nineth.enums.AssetStatus.NORMAL " +
            " and ac.collectionId = ?1 and ac.sellPrice <= ?2 ")
    List<Asset> findLimitPriceAsset(Long collectionId, BigDecimal limitPrice);

    /**
     * 查询我卖出的总金额
     * @param user_id
     * @return
     */
    @Query(value="select sum(ac.sellPrice) from Asset ac where ac.del = false " +
            " and ac.status = com.izouma.nineth.enums.AssetStatus.TRANSFERRED and ac.userId = ?1 ")
    BigDecimal sellAmountFromUserId(Long user_id);

    @Modifying
    @Transactional
    @Query(value = "update asset set categories = ?1 where collection_id = ?2 ", nativeQuery = true)
    void updateCategoriesByCollectionId(String categories, Long collectionId);

    @Query(value="select COUNT(1) as total, " +
            "( select COUNT(1) from asset a where a.user_id = ?1 and a.project_id = ?2 and public_show = 1 and a.status in ('NORMAL', 'TRADING', 'GIFTING')) as publicShow, " +
            "( select COUNT(1) from asset a where a.user_id = ?1 and a.project_id = ?2 and consignment = 1 and a.status in ('NORMAL', 'TRADING', 'GIFTING') ) as consignment " +
            "from asset a where a.user_id = ?1 and a.project_id = ?2 and a.status in ('NORMAL', 'TRADING', 'GIFTING')",nativeQuery = true)
    Map<String, Object> userHoldNum(Long userId, int projectId);


    /**
     * 更新铸造信息
     * @param asset
     * @return
     */
    @Query("update Asset set tokenId = :#{#asset.tokenId} , blockNumber= :#{#asset.blockNumber}, txHash = :#{#asset.txHash}," +
            " gasUsed = :#{#asset.gasUsed}, ipfsUrl = :#{#asset.ipfsUrl}" +
            "   where id = :#{#asset.id} ")
    @Modifying
    int updateMintInfo(@Param("asset") Asset asset);


    /**
     * 查找用户持有指定资产的记录
     *
     * @return
     */
    @Query("select count(aa) from Asset aa where aa.status in (" +
            "com.izouma.nineth.enums.AssetStatus.NORMAL," +
            "com.izouma.nineth.enums.AssetStatus.TRADING," +
            "com.izouma.nineth.enums.AssetStatus.GIFTING) " +
            "   and  aa.tokenId = ?1 ")
    Integer countAssetByTokenId(String tokenId);

    @Query(value="SELECT user_id, sum((num * score)) as total_score from(\n" +
            "select\n" +
            "\tcount(user_id) as num,\n" +
            "\tuser_id ,\n" +
            "\tcollection_id,\n" +
            "\tcase collection_id\n" +
            "\twhen 3193294 then 5\n" +
            "\twhen 3681144 then 3\n" +
            "\twhen 3681173 then 3\n" +
            "\twhen 2644615 then 10\n" +
            "\twhen 3799729 then 1\n" +
            "\telse 0 \n" +
            "\tend  as score\n" +
            "from\n" +
            "\tasset a\n" +
            "where\n" +
            "\tcollection_id in (3193294,3681144,3681173,2644615,3799729)\n" +
            "\tand status in ('NORMAL','TRADING')\n" +
            "\tand user_id != 2900030\n" +
            "group by\n" +
            "\tuser_id,\n" +
            "\tcollection_id \n" +
            ") a\n" +
            "group by user_id \n" +
            "order by total_score desc\n" +
            "limit 30",nativeQuery = true)
    List<Map<String, Object>> ownerScoreTop30();


    /**
     * 根据用户ID 查找时间段内持有过的藏品
     * @param userId 用户ID
     * @return 资产
     */
    @Query("select asset from Asset asset where " +
            "asset.userId = ?1  and asset.outTime >?2 and asset.outTime <?3 and asset.id not in (?4)")
    List<Asset> findUserAssetOutTime( Long userId, LocalDateTime satrtTime ,LocalDateTime endTime,Set<Long> exclusiveAssetIds);


    @Query("select asset from Asset asset where " +
            "asset.userId = ?1 and asset.consignment = 1 and asset.status =?2 ")
    List<Asset> findAllConsignmentByUserIdAndStatus(Long id, AssetStatus normal);
}