package com.writeoff.security;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.sql.Timestamp;
import java.time.Duration;
import java.time.LocalDateTime;

@Service
public class RateLimitService {

    private static final int GLOBAL_LIMIT_PER_MINUTE = 300;
    private static final int LOGIN_LIMIT_PER_MINUTE = 20;

    private final JdbcTemplate jdbcTemplate;

    public RateLimitService(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @Transactional
    public RateLimitDecision tryAcquire(String clientIp, boolean loginRequest) {
        String scope = loginRequest ? "LOGIN" : "API";
        int limit = loginRequest ? LOGIN_LIMIT_PER_MINUTE : GLOBAL_LIMIT_PER_MINUTE;
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime windowStartAt = now.withSecond(0).withNano(0);
        LocalDateTime expiresAt = windowStartAt.plusMinutes(2);
        String bucketKey = scope + ":" + clientIp;

        jdbcTemplate.update(
                "INSERT INTO api_rate_limit_counter " +
                        "(bucket_key, scope, client_ip, window_start_at, expires_at, request_count, created_at, updated_at) " +
                        "VALUES (?, ?, ?, ?, ?, LAST_INSERT_ID(1), CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) " +
                        "ON DUPLICATE KEY UPDATE " +
                        "scope = VALUES(scope), " +
                        "client_ip = VALUES(client_ip), " +
                        "request_count = LAST_INSERT_ID(IF(window_start_at = VALUES(window_start_at), request_count + 1, 1)), " +
                        "window_start_at = VALUES(window_start_at), " +
                        "expires_at = VALUES(expires_at), " +
                        "updated_at = CURRENT_TIMESTAMP",
                bucketKey,
                scope,
                clientIp,
                Timestamp.valueOf(windowStartAt),
                Timestamp.valueOf(expiresAt)
        );

        Integer currentCount = jdbcTemplate.queryForObject("SELECT LAST_INSERT_ID()", Integer.class);
        int requestCount = currentCount == null ? 1 : currentCount.intValue();
        long retryAfterSeconds = Math.max(1, Duration.between(now, windowStartAt.plusMinutes(1)).getSeconds());
        return new RateLimitDecision(scope, limit, requestCount <= limit, requestCount, retryAfterSeconds);
    }

    public static class RateLimitDecision {
        private final String scope;
        private final int limit;
        private final boolean allowed;
        private final int currentCount;
        private final long retryAfterSeconds;

        RateLimitDecision(String scope, int limit, boolean allowed, int currentCount, long retryAfterSeconds) {
            this.scope = scope;
            this.limit = limit;
            this.allowed = allowed;
            this.currentCount = currentCount;
            this.retryAfterSeconds = retryAfterSeconds;
        }

        public String getScope() {
            return scope;
        }

        public int getLimit() {
            return limit;
        }

        public boolean isAllowed() {
            return allowed;
        }

        public int getCurrentCount() {
            return currentCount;
        }

        public long getRetryAfterSeconds() {
            return retryAfterSeconds;
        }
    }
}
