package com.tutego.ch_07;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
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 org.springframework.scheduling.annotation.Async;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;

// public interface JpaRepository<T, ID> extends ListCrudRepository<T, ID>, ListPagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T>
public interface ProfileRepository extends JpaRepository<Profile, Long>, JpaSpecificationExecutor<Profile> {

    @Query("SELECT p FROM Profile p WHERE p.lastSeen > :lastSeen")
    List<Profile> findProfilesLastSeenAfter(LocalDateTime lastSeen);
    /* findProfilesLastSeenAfter(@Param("lastSeen") LocalDateTime timestamp ); */

    @Query("SELECT p FROM Profile p WHERE p.nickname = :name")
    Optional<Profile> findProfileByNickname(String name);

    @Query("SELECT p FROM Profile p WHERE p.nickname LIKE %:name%")
    List<Profile> findProfilesByContainingName(String name);

    @Query("SELECT p FROM Profile p WHERE p.maneLength BETWEEN :min AND :max")
    List<Profile> findProfilesByManeLengthBetween(short min, short max);

    @Modifying // requires a @Transactional context to run
    @Query("UPDATE Profile p SET p.lastSeen = :lastSeen WHERE p.id = :id")
    int updateLastSeen(long id, LocalDateTime lastSeen);

    @Query("SELECT p FROM Profile p WHERE p.attractedToGender IN :genders")
    List<Profile> findProfilesAttractedToGender(Byte... genders);

    @Query("SELECT p.id as id, p.nickname as nickname FROM Profile p WHERE id=:id")
    Map<String, Object> findSimplifiedProfile(long id);

    @Query("SELECT p.id as id, p.nickname as nickname FROM Profile p")
    List<Map<String, Object>> findAllSimplifiedProfiles();

    @Query("SELECT p FROM Profile p WHERE p.lastSeen > :lastSeen")
        // no need to embed into the name the ordering
    List<Profile> findProfilesLastSeenAfter(LocalDateTime lastSeen, Sort sort);

    @Query("SELECT p FROM Profile p WHERE p.lastSeen > :lastSeen")
    Page<Profile> findProfilesLastSeenAfter(LocalDateTime lastSeen, Pageable p);

    @Query(
            value = "SELECT * FROM Profile WHERE maneLength > ?1",
            nativeQuery = true /* used for distinguishing native queries */,
            countQuery = "SELECT count(*) FROM … WHERE …" // if JPA is not able to correctly generate the right COUNT query
    )
    List<Profile> findProfilesWithManeLengthGreaterThan(short maneLength, Pageable p /* can still pass Pageable */);

    // no need for @Query
    Optional<Profile> findFirstByOrderByManeLengthDesc();

    List<Profile> findByOrderByManeLengthDesc();

    List<Profile> findByManeLengthGreaterThan(short min);

    List<Profile> findFirst10ByOrderByLastSeenDesc();

    @Async
    CompletableFuture<Profile> findByNickname(String nickname);
}