package com.magistr.app.repository;

import com.magistr.app.model.AuthLoginRateLimit;
import jakarta.persistence.LockModeType;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Lock;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;

import java.time.Instant;
import java.util.Optional;

public interface AuthLoginRateLimitRepository extends JpaRepository<AuthLoginRateLimit, Long> {

    @Modifying(flushAutomatically = true)
    @Query(value = """
            INSERT INTO auth_login_rate_limits (
                tenant, username_normalized, client_ip, failure_count,
                window_started_at, updated_at
            ) VALUES (:tenant, :username, :clientIp, 0, :now, :now)
            ON CONFLICT (tenant, username_normalized, client_ip) DO NOTHING
            """, nativeQuery = true)
    int ensureExists(@Param("tenant") String tenant,
                     @Param("username") String username,
                     @Param("clientIp") String clientIp,
                     @Param("now") Instant now);

    @Lock(LockModeType.PESSIMISTIC_WRITE)
    @Query("""
            SELECT limit
            FROM AuthLoginRateLimit limit
            WHERE limit.tenant = :tenant
              AND limit.usernameNormalized = :username
              AND limit.clientIp = :clientIp
            """)
    Optional<AuthLoginRateLimit> findForUpdate(@Param("tenant") String tenant,
                                               @Param("username") String username,
                                               @Param("clientIp") String clientIp);

    @Modifying
    @Transactional
    @Query(value = """
            WITH cleanup_candidates AS (
                SELECT id
                FROM auth_login_rate_limits
                WHERE updated_at < :cutoff
                  AND (blocked_until IS NULL OR blocked_until <= :now)
                ORDER BY updated_at, id
                FOR UPDATE SKIP LOCKED
                LIMIT :batchSize
            )
            DELETE FROM auth_login_rate_limits rate_limit
            USING cleanup_candidates candidate
            WHERE rate_limit.id = candidate.id
            """, nativeQuery = true)
    int deleteStaleBatch(@Param("cutoff") Instant cutoff,
                         @Param("now") Instant now,
                         @Param("batchSize") int batchSize);
}
