package com.test.bijihoudaun.util;

import lombok.extern.slf4j.Slf4j;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 登录锁定工具类
 * 使用本地内存存储登录失败记录，带容量限制和定期清理
 */
@Slf4j
public class LoginLockUtil {

    // 最大失败次数
    private static final int MAX_FAILED_ATTEMPTS = 5;
    // 锁定时间（分钟）
    private static final int LOCK_TIME_MINUTES = 30;
    // 失败记录过期时间（分钟）
    private static final int RECORD_EXPIRE_MINUTES = 60;
    // 最大存储记录数（防止内存溢出）
    private static final int MAX_RECORDS = 10000;

    // 登录失败记录：key=用户名，value=失败记录
    private static final LRUCache<String, LoginAttempt> attempts = new LRUCache<>(MAX_RECORDS);
    private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    // 定期清理线程池
    private static final ScheduledExecutorService cleanupScheduler = Executors.newSingleThreadScheduledExecutor(r -> {
        Thread t = new Thread(r, "login-lock-cleanup");
        t.setDaemon(true);
        return t;
    });

    static {
        // 每10分钟清理一次过期记录
        cleanupScheduler.scheduleAtFixedRate(LoginLockUtil::cleanupExpiredLocks,
                10, 10, TimeUnit.MINUTES);
    }

    private static class LoginAttempt {
        int failedCount;
        LocalDateTime lastAttemptTime;
        LocalDateTime lockUntil;

        LoginAttempt() {
            this.failedCount = 0;
            this.lastAttemptTime = LocalDateTime.now();
            this.lockUntil = null;
        }

        boolean isExpired() {
            return ChronoUnit.MINUTES.between(lastAttemptTime, LocalDateTime.now()) > RECORD_EXPIRE_MINUTES;
        }
    }

    /**
     * 简单的 LRU 缓存实现
     */
    private static class LRUCache<K, V> extends LinkedHashMap<K, V> {
        private final int maxSize;

        LRUCache(int maxSize) {
            super(maxSize, 0.75f, true);
            this.maxSize = maxSize;
        }

        @Override
        protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
            boolean shouldRemove = size() > maxSize;
            if (shouldRemove) {
                log.warn("登录锁定记录达到上限，移除最旧的记录: {}", eldest.getKey());
            }
            return shouldRemove;
        }
    }

    /**
     * 记录登录失败
     * @param username 用户名
     */
    public static void recordFailedAttempt(String username) {
        if (username == null || username.isEmpty()) return;

        lock.writeLock().lock();
        try {
            LoginAttempt attempt = attempts.computeIfAbsent(username, k -> new LoginAttempt());
            attempt.failedCount++;
            attempt.lastAttemptTime = LocalDateTime.now();

            // 达到最大失败次数，锁定账号
            if (attempt.failedCount >= MAX_FAILED_ATTEMPTS) {
                attempt.lockUntil = LocalDateTime.now().plusMinutes(LOCK_TIME_MINUTES);
                log.warn("账号 [{}] 已被锁定 {} 分钟", username, LOCK_TIME_MINUTES);
            }
        } finally {
            lock.writeLock().unlock();
        }
    }

    /**
     * 记录登录成功（清除失败记录）
     * @param username 用户名
     */
    public static void recordSuccess(String username) {
        if (username == null || username.isEmpty()) return;

        lock.writeLock().lock();
        try {
            attempts.remove(username);
        } finally {
            lock.writeLock().unlock();
        }
    }

    /**
     * 检查账号是否被锁定
     * @param username 用户名
     * @return true-已锁定，false-未锁定
     */
    public static boolean isLocked(String username) {
        if (username == null || username.isEmpty()) return false;

        lock.readLock().lock();
        try {
            LoginAttempt attempt = attempts.get(username);
            if (attempt == null) return false;

            // 检查是否仍在锁定时间内
            if (attempt.lockUntil != null) {
                return LocalDateTime.now().isBefore(attempt.lockUntil);
            }
            return false;
        } finally {
            lock.readLock().unlock();
        }
    }

    /**
     * 获取剩余锁定时间（秒）
     * @param username 用户名
     * @return 剩余秒数，如果未锁定返回0
     */
    public static long getRemainingLockTime(String username) {
        if (username == null || username.isEmpty()) return 0;

        lock.readLock().lock();
        try {
            LoginAttempt attempt = attempts.get(username);
            if (attempt == null || attempt.lockUntil == null) return 0;

            long remaining = ChronoUnit.SECONDS.between(LocalDateTime.now(), attempt.lockUntil);
            return Math.max(0, remaining);
        } finally {
            lock.readLock().unlock();
        }
    }

    /**
     * 获取剩余失败次数
     * @param username 用户名
     * @return 剩余次数
     */
    public static int getRemainingAttempts(String username) {
        if (username == null || username.isEmpty()) return MAX_FAILED_ATTEMPTS;

        lock.readLock().lock();
        try {
            LoginAttempt attempt = attempts.get(username);
            if (attempt == null) return MAX_FAILED_ATTEMPTS;

            return Math.max(0, MAX_FAILED_ATTEMPTS - attempt.failedCount);
        } finally {
            lock.readLock().unlock();
        }
    }

    /**
     * 清理过期的锁定记录
     */
    private static void cleanupExpiredLocks() {
        lock.writeLock().lock();
        try {
            LocalDateTime now = LocalDateTime.now();
            attempts.entrySet().removeIf(entry -> {
                LoginAttempt attempt = entry.getValue();
                // 未锁定且长时间没有登录的记录
                if (attempt.lockUntil == null) {
                    return attempt.isExpired();
                }
                // 锁定已过期的记录
                return now.isAfter(attempt.lockUntil);
            });
        } finally {
            lock.writeLock().unlock();
        }
    }

    /**
     * 获取当前记录数量（用于监控）
     */
    public static int getRecordCount() {
        lock.readLock().lock();
        try {
            return attempts.size();
        } finally {
            lock.readLock().unlock();
        }
    }
}
