package com.honey.honey.repository;

import com.honey.honey.model.Payout;
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.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import java.math.BigDecimal;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
import java.util.Set;

@Repository
public interface PayoutRepository extends JpaRepository<Payout, Long>, JpaSpecificationExecutor<Payout> {
    List<Payout> findByUserIdOrderByCreatedAtDesc(Integer userId);
    org.springframework.data.domain.Page<Payout> findByUserIdAndType(Integer userId, Payout.PayoutType type, org.springframework.data.domain.Pageable pageable);

    /**
     * Finds CRYPTO payouts by status in (PROCESSING, WAITING) with non-null payment_id, ordered by updated_at ASC (oldest first).
     * Used by withdrawal status sync cron to process up to page size (e.g. 20) per run. STARS and GIFT are ignored.
     */
    List<Payout> findByTypeAndStatusInAndPaymentIdIsNotNullOrderByUpdatedAtAsc(
            Payout.PayoutType type,
            Set<Payout.PayoutStatus> statuses,
            Pageable pageable);

    /**
     * Returns whether the user has at least one payout with the given status.
     * Used to enforce at most one PROCESSING payout per user.
     */
    boolean existsByUserIdAndStatus(Integer userId, Payout.PayoutStatus status);
    
    List<Payout> findByStatusOrderByCreatedAtDesc(Payout.PayoutStatus status);
    
    Optional<Payout> findByIdAndStatus(Long id, Payout.PayoutStatus status);
    
    /**
     * Finds the last N payouts for a specific user, ordered by creation date descending.
     * Uses the index idx_payouts_user_created for optimal performance.
     */
    @Query("SELECT p FROM Payout p WHERE p.userId = :userId ORDER BY p.createdAt DESC")
    List<Payout> findLastPayoutsByUserId(@Param("userId") Integer userId, org.springframework.data.domain.Pageable pageable);
    
    /**
     * Sums total for all payouts with given status.
     */
    @Query("SELECT COALESCE(SUM(p.total), 0) FROM Payout p WHERE p.status = :status")
    Optional<Long> sumTotalByStatus(@Param("status") Payout.PayoutStatus status);
    
    /**
     * Sums total for payouts with given status created after the specified date.
     */
    @Query("SELECT COALESCE(SUM(p.total), 0) FROM Payout p WHERE p.status = :status AND p.createdAt >= :after")
    Optional<Long> sumTotalByStatusAndCreatedAtAfter(
            @Param("status") Payout.PayoutStatus status,
            @Param("after") Instant after
    );
    
    /**
     * Sums stars_amount for all payouts with given status.
     */
    @Query("SELECT COALESCE(SUM(p.starsAmount), 0) FROM Payout p WHERE p.status = :status")
    Optional<Integer> sumStarsAmountByStatus(@Param("status") Payout.PayoutStatus status);
    
    /**
     * Sums stars_amount for payouts with given status created after the specified date.
     */
    @Query("SELECT COALESCE(SUM(p.starsAmount), 0) FROM Payout p WHERE p.status = :status AND p.createdAt >= :after")
    Optional<Integer> sumStarsAmountByStatusAndCreatedAtAfter(
            @Param("status") Payout.PayoutStatus status,
            @Param("after") Instant after
    );
    
    /**
     * Sums stars_amount for payouts with given status created between two dates.
     */
    @Query("SELECT COALESCE(SUM(p.starsAmount), 0) FROM Payout p WHERE p.status = :status AND p.createdAt >= :start AND p.createdAt < :end")
    Optional<Integer> sumStarsAmountByStatusAndCreatedAtBetween(
            @Param("status") Payout.PayoutStatus status,
            @Param("start") Instant start,
            @Param("end") Instant end
    );

    /** Sum usd_amount for CRYPTO payouts with given status. */
    @Query("SELECT COALESCE(SUM(p.usdAmount), 0) FROM Payout p WHERE p.type = 'CRYPTO' AND p.status = :status")
    Optional<BigDecimal> sumUsdAmountByTypeCryptoAndStatus(@Param("status") Payout.PayoutStatus status);

    /** Sum usd_amount for CRYPTO payouts with given status created after the specified date. */
    @Query("SELECT COALESCE(SUM(p.usdAmount), 0) FROM Payout p WHERE p.type = 'CRYPTO' AND p.status = :status AND p.createdAt >= :after")
    Optional<BigDecimal> sumUsdAmountByTypeCryptoAndStatusAndCreatedAtAfter(
            @Param("status") Payout.PayoutStatus status,
            @Param("after") Instant after
    );

    /** Sum usd_amount for a user's completed CRYPTO payouts. */
    @Query("SELECT COALESCE(SUM(p.usdAmount), 0) FROM Payout p WHERE p.userId = :userId AND p.type = 'CRYPTO' AND p.status = 'COMPLETED'")
    Optional<BigDecimal> sumUsdAmountByUserIdAndTypeCryptoAndCompleted(@Param("userId") Integer userId);

    /** Sum usd_amount for CRYPTO payouts with given status created between two dates. */
    @Query("SELECT COALESCE(SUM(p.usdAmount), 0) FROM Payout p WHERE p.type = 'CRYPTO' AND p.status = :status AND p.createdAt >= :start AND p.createdAt < :end")
    Optional<BigDecimal> sumUsdAmountByTypeCryptoAndStatusAndCreatedAtBetween(
            @Param("status") Payout.PayoutStatus status,
            @Param("start") Instant start,
            @Param("end") Instant end
    );

    /** Sum usd_amount for all payouts with given status (null usd ignored by SUM). */
    @Query("SELECT COALESCE(SUM(p.usdAmount), 0) FROM Payout p WHERE p.status = :status")
    Optional<BigDecimal> sumUsdAmountByStatus(@Param("status") Payout.PayoutStatus status);
}

