package com.social.media.service.impl;

import com.social.media.dto.UserCredentialsDto;
import com.social.media.domain.entity.UserCredentials;
import com.social.media.repository.UserCredentialsRepository;
import com.social.media.service.UserCredentialsService;
import com.social.media.util.UserCredentialsMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.security.SecureRandom;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * Implementation of UserCredentialsService
 */
@Service
@Transactional
@RequiredArgsConstructor
@Slf4j
public class UserCredentialsServiceImpl implements UserCredentialsService {
    
    private final UserCredentialsRepository repository;
    private final PasswordEncoder passwordEncoder;
    private final SecureRandom secureRandom = new SecureRandom();
    
    // Password policy constants
    private static final int MIN_PASSWORD_LENGTH = 8;
    private static final int MAX_PASSWORD_LENGTH = 128;
    private static final Pattern UPPERCASE_PATTERN = Pattern.compile("[A-Z]");
    private static final Pattern LOWERCASE_PATTERN = Pattern.compile("[a-z]");
    private static final Pattern DIGIT_PATTERN = Pattern.compile("[0-9]");
    private static final Pattern SPECIAL_CHAR_PATTERN = Pattern.compile("[!@#$%^&*()_+\\-=\\[\\]{};':\"\\\\|,.<>?]");
    
    @Override
    public UserCredentialsDto create(UserCredentialsDto dto) {
        log.info("Creating user credentials for user ID: {}", dto.getUserId());
        
        if (repository.existsById(dto.getUserId())) {
            throw new IllegalArgumentException("User credentials already exist for user ID: " + dto.getUserId());
        }
        
        UserCredentials entity = UserCredentialsMapper.toEntity(dto);
        
        // Encode password if provided
        if (dto.getPasswordHash() != null) {
            entity.setPasswordHash(passwordEncoder.encode(dto.getPasswordHash()));
        }
        
        entity.setCreatedAt(LocalDateTime.now());
        entity.setUpdatedAt(LocalDateTime.now());
        
        UserCredentials saved = repository.save(entity);
        log.info("Successfully created user credentials for user ID: {}", saved.getUserId());
        
        return UserCredentialsMapper.toDto(saved);
    }
    
    @Override
    public UserCredentialsDto update(Long userId, UserCredentialsDto dto) {
        log.info("Updating user credentials for user ID: {}", userId);
        
        UserCredentials existing = repository.findById(userId)
                .orElseThrow(() -> new IllegalArgumentException("User credentials not found for user ID: " + userId));
        
        UserCredentialsMapper.updateEntityFromDto(existing, dto);
        existing.setUpdatedAt(LocalDateTime.now());
        
        UserCredentials saved = repository.save(existing);
        log.info("Successfully updated user credentials for user ID: {}", userId);
        
        return UserCredentialsMapper.toDto(saved);
    }
    
    @Override
    @Transactional(readOnly = true)
    public Optional<UserCredentialsDto> findByUserId(Long userId) {
        return repository.findByUserId(userId)
                .map(UserCredentialsMapper::toDto);
    }
    
    @Override
    @Transactional(readOnly = true)
    public Page<UserCredentialsDto> findAll(Pageable pageable) {
        Page<UserCredentials> entities = repository.findAll(pageable);
        return entities.map(UserCredentialsMapper::toDto);
    }
    
    @Override
    public void delete(Long userId) {
        log.info("Deleting user credentials for user ID: {}", userId);
        
        if (!repository.existsById(userId)) {
            throw new IllegalArgumentException("User credentials not found for user ID: " + userId);
        }
        
        repository.deleteById(userId);
        log.info("Successfully deleted user credentials for user ID: {}", userId);
    }
    
    @Override
    @Transactional(readOnly = true)
    public boolean authenticate(Long userId, String rawPassword) {
        Optional<UserCredentials> credentialsOpt = repository.findByUserId(userId);
        
        if (credentialsOpt.isEmpty()) {
            log.warn("Authentication failed: User credentials not found for user ID: {}", userId);
            return false;
        }
        
        UserCredentials credentials = credentialsOpt.get();
        
        if (credentials.isAccountLocked()) {
            log.warn("Authentication failed: Account locked for user ID: {}", userId);
            return false;
        }
        
        boolean passwordMatches = passwordEncoder.matches(rawPassword, credentials.getPasswordHash());
        
        if (passwordMatches) {
            log.info("Authentication successful for user ID: {}", userId);
            recordSuccessfulLogin(userId);
        } else {
            log.warn("Authentication failed: Invalid password for user ID: {}", userId);
            recordFailedLoginAttempt(userId);
        }
        
        return passwordMatches;
    }
    
    @Override
    public void recordSuccessfulLogin(Long userId) {
        log.info("Recording successful login for user ID: {}", userId);
        
        UserCredentials credentials = repository.findByUserId(userId)
                .orElseThrow(() -> new IllegalArgumentException("User credentials not found for user ID: " + userId));
        
        credentials.recordSuccessfulLogin();
        repository.save(credentials);
        
        logSecurityEvent(userId, "SUCCESSFUL_LOGIN", "User logged in successfully");
    }
    
    @Override
    public void recordFailedLoginAttempt(Long userId) {
        log.info("Recording failed login attempt for user ID: {}", userId);
        
        UserCredentials credentials = repository.findByUserId(userId)
                .orElseThrow(() -> new IllegalArgumentException("User credentials not found for user ID: " + userId));
        
        credentials.incrementFailedAttempts();
        
        // Auto-lock if too many failed attempts
        if (credentials.getFailedLoginAttempts() >= credentials.getMaxFailedAttempts()) {
            int lockoutMinutes = credentials.getLockoutDurationMinutes();
            if (lockoutMinutes > 0) {
                credentials.lockAccount(lockoutMinutes);
                log.warn("Account locked for {} minutes due to {} failed attempts for user ID: {}", 
                        lockoutMinutes, credentials.getFailedLoginAttempts(), userId);
            } else {
                credentials.lockAccountPermanently();
                log.warn("Account permanently locked due to {} failed attempts for user ID: {}", 
                        credentials.getFailedLoginAttempts(), userId);
            }
        }
        
        repository.save(credentials);
        
        logSecurityEvent(userId, "FAILED_LOGIN", 
                "Failed login attempt #" + credentials.getFailedLoginAttempts());
    }
    
    @Override
    @Transactional(readOnly = true)
    public boolean isAccountLocked(Long userId) {
        return repository.findByUserId(userId)
                .map(UserCredentials::isAccountLocked)
                .orElse(false);
    }
    
    @Override
    public void lockAccount(Long userId, int lockoutMinutes) {
        log.info("Locking account for user ID: {} for {} minutes", userId, lockoutMinutes);
        
        UserCredentials credentials = repository.findByUserId(userId)
                .orElseThrow(() -> new IllegalArgumentException("User credentials not found for user ID: " + userId));
        
        credentials.lockAccount(lockoutMinutes);
        repository.save(credentials);
        
        logSecurityEvent(userId, "ACCOUNT_LOCKED", 
                "Account locked for " + lockoutMinutes + " minutes");
    }
    
    @Override
    public void lockAccountPermanently(Long userId) {
        log.info("Permanently locking account for user ID: {}", userId);
        
        UserCredentials credentials = repository.findByUserId(userId)
                .orElseThrow(() -> new IllegalArgumentException("User credentials not found for user ID: " + userId));
        
        credentials.lockAccountPermanently();
        repository.save(credentials);
        
        logSecurityEvent(userId, "ACCOUNT_PERMANENTLY_LOCKED", 
                "Account permanently locked");
    }
    
    @Override
    public void unlockAccount(Long userId) {
        log.info("Unlocking account for user ID: {}", userId);
        
        UserCredentials credentials = repository.findByUserId(userId)
                .orElseThrow(() -> new IllegalArgumentException("User credentials not found for user ID: " + userId));
        
        credentials.unlockAccount();
        repository.save(credentials);
        
        logSecurityEvent(userId, "ACCOUNT_UNLOCKED", "Account unlocked by administrator");
    }
    
    @Override
    public void resetFailedAttempts(Long userId) {
        log.info("Resetting failed attempts for user ID: {}", userId);
        
        UserCredentials credentials = repository.findByUserId(userId)
                .orElseThrow(() -> new IllegalArgumentException("User credentials not found for user ID: " + userId));
        
        credentials.resetFailedAttempts();
        repository.save(credentials);
        
        logSecurityEvent(userId, "FAILED_ATTEMPTS_RESET", "Failed login attempts reset");
    }
    
    @Override
    public void changePassword(Long userId, String currentPassword, String newPassword) {
        log.info("Changing password for user ID: {}", userId);
        
        UserCredentials credentials = repository.findByUserId(userId)
                .orElseThrow(() -> new IllegalArgumentException("User credentials not found for user ID: " + userId));
        
        // Verify current password
        if (!passwordEncoder.matches(currentPassword, credentials.getPasswordHash())) {
            throw new IllegalArgumentException("Current password is incorrect");
        }
        
        // Validate new password
        if (!meetsPasswordPolicy(newPassword)) {
            throw new IllegalArgumentException("New password does not meet policy requirements");
        }
        
        String encodedPassword = passwordEncoder.encode(newPassword);
        credentials.updatePassword(encodedPassword);
        repository.save(credentials);
        
        logSecurityEvent(userId, "PASSWORD_CHANGED", "Password changed by user");
        log.info("Password changed successfully for user ID: {}", userId);
    }
    
    @Override
    public void resetPasswordByAdmin(Long userId, String newPassword) {
        log.info("Admin resetting password for user ID: {}", userId);
        
        UserCredentials credentials = repository.findByUserId(userId)
                .orElseThrow(() -> new IllegalArgumentException("User credentials not found for user ID: " + userId));
        
        // Validate new password
        if (!meetsPasswordPolicy(newPassword)) {
            throw new IllegalArgumentException("New password does not meet policy requirements");
        }
        
        String encodedPassword = passwordEncoder.encode(newPassword);
        credentials.updatePassword(encodedPassword);
        repository.save(credentials);
        
        logSecurityEvent(userId, "PASSWORD_RESET_BY_ADMIN", "Password reset by administrator");
        log.info("Password reset by admin successfully for user ID: {}", userId);
    }
    
    @Override
    public String generatePasswordResetToken(Long userId, int expirationHours) {
        log.info("Generating password reset token for user ID: {}", userId);
        
        UserCredentials credentials = repository.findByUserId(userId)
                .orElseThrow(() -> new IllegalArgumentException("User credentials not found for user ID: " + userId));
        
        String token = generateSecureToken();
        credentials.setPasswordResetToken(token, expirationHours);
        repository.save(credentials);
        
        logSecurityEvent(userId, "PASSWORD_RESET_TOKEN_GENERATED", 
                "Password reset token generated");
        
        return token;
    }
    
    @Override
    @Transactional(readOnly = true)
    public boolean validatePasswordResetToken(String token) {
        return repository.findByPasswordResetToken(token)
                .map(UserCredentials::isPasswordResetTokenValid)
                .orElse(false);
    }
    
    @Override
    public boolean resetPasswordWithToken(String token, String newPassword) {
        log.info("Resetting password with token");
        
        Optional<UserCredentials> credentialsOpt = repository.findByPasswordResetToken(token);
        
        if (credentialsOpt.isEmpty()) {
            log.warn("Password reset failed: Invalid token");
            return false;
        }
        
        UserCredentials credentials = credentialsOpt.get();
        
        if (!credentials.isPasswordResetTokenValid()) {
            log.warn("Password reset failed: Token expired for user ID: {}", credentials.getUserId());
            return false;
        }
        
        // Validate new password
        if (!meetsPasswordPolicy(newPassword)) {
            throw new IllegalArgumentException("New password does not meet policy requirements");
        }
        
        String encodedPassword = passwordEncoder.encode(newPassword);
        credentials.updatePassword(encodedPassword);
        repository.save(credentials);
        
        logSecurityEvent(credentials.getUserId(), "PASSWORD_RESET_WITH_TOKEN", 
                "Password reset using token");
        
        log.info("Password reset with token successful for user ID: {}", credentials.getUserId());
        return true;
    }
    
    @Override
    public void clearPasswordResetToken(Long userId) {
        log.info("Clearing password reset token for user ID: {}", userId);
        
        UserCredentials credentials = repository.findByUserId(userId)
                .orElseThrow(() -> new IllegalArgumentException("User credentials not found for user ID: " + userId));
        
        credentials.clearPasswordResetToken();
        repository.save(credentials);
    }
    
    @Override
    @Transactional(readOnly = true)
    public boolean needsPasswordChange(Long userId, int maxAgeInDays) {
        return repository.findByUserId(userId)
                .map(credentials -> {
                    if (credentials.getPasswordChangedAt() == null) return true;
                    
                    LocalDateTime threshold = LocalDateTime.now().minusDays(maxAgeInDays);
                    return credentials.getPasswordChangedAt().isBefore(threshold);
                })
                .orElse(false);
    }
    
    @Override
    public String generateEmailVerificationToken(Long userId) {
        log.info("Generating email verification token for user ID: {}", userId);
        
        UserCredentials credentials = repository.findByUserId(userId)
                .orElseThrow(() -> new IllegalArgumentException("User credentials not found for user ID: " + userId));
        
        String token = generateSecureToken();
        credentials.setEmailVerificationToken(token);
        repository.save(credentials);
        
        logSecurityEvent(userId, "EMAIL_VERIFICATION_TOKEN_GENERATED", 
                "Email verification token generated");
        
        return token;
    }
    
    @Override
    @Transactional(readOnly = true)
    public boolean validateEmailVerificationToken(String token) {
        return repository.findByEmailVerificationToken(token).isPresent();
    }
    
    @Override
    public boolean verifyEmailWithToken(String token) {
        log.info("Verifying email with token");
        
        Optional<UserCredentials> credentialsOpt = repository.findByEmailVerificationToken(token);
        
        if (credentialsOpt.isEmpty()) {
            log.warn("Email verification failed: Invalid token");
            return false;
        }
        
        UserCredentials credentials = credentialsOpt.get();
        credentials.clearEmailVerificationToken();
        repository.save(credentials);
        
        logSecurityEvent(credentials.getUserId(), "EMAIL_VERIFIED", 
                "Email verified using token");
        
        log.info("Email verification successful for user ID: {}", credentials.getUserId());
        return true;
    }
    
    @Override
    public void clearEmailVerificationToken(Long userId) {
        log.info("Clearing email verification token for user ID: {}", userId);
        
        UserCredentials credentials = repository.findByUserId(userId)
                .orElseThrow(() -> new IllegalArgumentException("User credentials not found for user ID: " + userId));
        
        credentials.clearEmailVerificationToken();
        repository.save(credentials);
    }
    
    @Override
    public void enableTwoFactor(Long userId) {
        log.info("Enabling two-factor authentication for user ID: {}", userId);
        
        UserCredentials credentials = repository.findByUserId(userId)
                .orElseThrow(() -> new IllegalArgumentException("User credentials not found for user ID: " + userId));
        
        credentials.enableTwoFactor();
        repository.save(credentials);
        
        logSecurityEvent(userId, "TWO_FACTOR_ENABLED", 
                "Two-factor authentication enabled");
    }
    
    @Override
    public void disableTwoFactor(Long userId) {
        log.info("Disabling two-factor authentication for user ID: {}", userId);
        
        UserCredentials credentials = repository.findByUserId(userId)
                .orElseThrow(() -> new IllegalArgumentException("User credentials not found for user ID: " + userId));
        
        credentials.disableTwoFactor();
        repository.save(credentials);
        
        logSecurityEvent(userId, "TWO_FACTOR_DISABLED", 
                "Two-factor authentication disabled");
    }
    
    @Override
    @Transactional(readOnly = true)
    public boolean isTwoFactorEnabled(Long userId) {
        return repository.findByUserId(userId)
                .map(UserCredentials::getTwoFactorEnabled)
                .orElse(false);
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<UserCredentialsDto> findAccountsWithFailedAttempts() {
        return repository.findAccountsWithFailedAttempts()
                .stream()
                .map(UserCredentialsMapper::toDto)
                .collect(Collectors.toList());
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<UserCredentialsDto> findLockedAccounts() {
        return repository.findLockedAccounts()
                .stream()
                .map(UserCredentialsMapper::toDto)
                .collect(Collectors.toList());
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<UserCredentialsDto> findPermanentlyLockedAccounts() {
        return repository.findPermanentlyLockedAccounts()
                .stream()
                .map(UserCredentialsMapper::toDto)
                .collect(Collectors.toList());
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<UserCredentialsDto> findAccountsWithPendingPasswordReset() {
        return repository.findAccountsWithPendingPasswordReset()
                .stream()
                .map(UserCredentialsMapper::toDto)
                .collect(Collectors.toList());
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<UserCredentialsDto> findExpiredPasswordResetTokens() {
        return repository.findExpiredPasswordResetTokens()
                .stream()
                .map(UserCredentialsMapper::toDto)
                .collect(Collectors.toList());
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<UserCredentialsDto> findAccountsWithPendingEmailVerification() {
        return repository.findAccountsWithPendingEmailVerification()
                .stream()
                .map(UserCredentialsMapper::toDto)
                .collect(Collectors.toList());
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<UserCredentialsDto> findAccountsWithTwoFactorEnabled() {
        return repository.findAccountsWithTwoFactorEnabled()
                .stream()
                .map(UserCredentialsMapper::toDto)
                .collect(Collectors.toList());
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<UserCredentialsDto> findAccountsWithRecentLogin(int daysBack) {
        LocalDateTime since = LocalDateTime.now().minusDays(daysBack);
        return repository.findAccountsWithRecentLogin(since)
                .stream()
                .map(UserCredentialsMapper::toDto)
                .collect(Collectors.toList());
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<UserCredentialsDto> findInactiveAccounts(int daysBack) {
        LocalDateTime before = LocalDateTime.now().minusDays(daysBack);
        return repository.findInactiveAccounts(before)
                .stream()
                .map(UserCredentialsMapper::toDto)
                .collect(Collectors.toList());
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<UserCredentialsDto> findAccountsWithOldPasswords(int daysBack) {
        LocalDateTime before = LocalDateTime.now().minusDays(daysBack);
        return repository.findAccountsWithOldPasswords(before)
                .stream()
                .map(UserCredentialsMapper::toDto)
                .collect(Collectors.toList());
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<UserCredentialsDto> findAccountsNeedingSecurityReview() {
        LocalDateTime oldPasswordThreshold = LocalDateTime.now().minusMonths(6);
        LocalDateTime inactivityThreshold = LocalDateTime.now().minusDays(90);
        
        return repository.findCredentialsNeedingSecurityReview(oldPasswordThreshold, inactivityThreshold)
                .stream()
                .map(UserCredentialsMapper::toDto)
                .collect(Collectors.toList());
    }
    
    @Override
    @Transactional(readOnly = true)
    public Page<UserCredentialsDto> searchBySecurityCriteria(
            boolean hasFailedAttempts, boolean isLocked, boolean hasTwoFactor,
            Boolean twoFactorEnabled, boolean hasRecentLogin, int recentLoginDays,
            Pageable pageable) {
        
        LocalDateTime recentLoginThreshold = LocalDateTime.now().minusDays(recentLoginDays);
        
        Page<UserCredentials> entities = repository.findBySecurityCriteria(
                hasFailedAttempts, isLocked, hasTwoFactor, twoFactorEnabled,
                hasRecentLogin, recentLoginThreshold, pageable);
        
        return entities.map(UserCredentialsMapper::toDto);
    }
    
    @Override
    public int clearExpiredPasswordResetTokens() {
        log.info("Clearing expired password reset tokens");
        int cleared = repository.clearExpiredPasswordResetTokens();
        log.info("Cleared {} expired password reset tokens", cleared);
        return cleared;
    }
    
    @Override
    public int unlockExpiredAccountLocks() {
        log.info("Unlocking accounts with expired lockouts");
        int unlocked = repository.unlockExpiredAccountLocks();
        log.info("Unlocked {} accounts with expired lockouts", unlocked);
        return unlocked;
    }
    
    @Override
    public int resetFailedAttemptsForUnlockedAccounts() {
        log.info("Resetting failed attempts for unlocked accounts");
        int reset = repository.resetFailedAttemptsForUnlockedAccounts();
        log.info("Reset failed attempts for {} unlocked accounts", reset);
        return reset;
    }
    
    @Override
    public void bulkEnableTwoFactor(List<Long> userIds) {
        log.info("Bulk enabling two-factor authentication for {} users", userIds.size());
        
        for (Long userId : userIds) {
            try {
                enableTwoFactor(userId);
            } catch (Exception e) {
                log.error("Failed to enable two-factor authentication for user ID: {}", userId, e);
            }
        }
    }
    
    @Override
    public void bulkDisableTwoFactor(List<Long> userIds) {
        log.info("Bulk disabling two-factor authentication for {} users", userIds.size());
        
        for (Long userId : userIds) {
            try {
                disableTwoFactor(userId);
            } catch (Exception e) {
                log.error("Failed to disable two-factor authentication for user ID: {}", userId, e);
            }
        }
    }
    
    @Override
    public void bulkUnlockAccounts(List<Long> userIds) {
        log.info("Bulk unlocking {} accounts", userIds.size());
        
        for (Long userId : userIds) {
            try {
                unlockAccount(userId);
            } catch (Exception e) {
                log.error("Failed to unlock account for user ID: {}", userId, e);
            }
        }
    }
    
    @Override
    @Transactional(readOnly = true)
    public Map<String, Object> getSecurityStatistics() {
        LocalDateTime recentThreshold = LocalDateTime.now().minusDays(30);
        Object[] stats = repository.getSecurityStatistics(recentThreshold);
        
        Map<String, Object> result = new HashMap<>();
        result.put("twoFactorEnabled", stats[0]);
        result.put("withFailedAttempts", stats[1]);
        result.put("locked", stats[2]);
        result.put("recentlyActive", stats[3]);
        result.put("total", stats[4]);
        
        return result;
    }
    
    @Override
    @Transactional(readOnly = true)
    public Map<String, Object> getSecurityAnalytics(int daysPeriod) {
        LocalDateTime since = LocalDateTime.now().minusDays(daysPeriod);
        
        Map<String, Object> analytics = new HashMap<>();
        
        analytics.put("period", daysPeriod + " days");
        analytics.put("totalAccounts", repository.count());
        analytics.put("lockedAccounts", repository.countLockedAccounts());
        analytics.put("twoFactorEnabled", repository.countTwoFactorEnabledAccounts());
        analytics.put("activeAccounts", repository.countActiveAccounts(since));
        analytics.put("accountsWithFailedAttempts", repository.countAccountsWithFailedAttempts(1));
        
        return analytics;
    }
    
    @Override
    @Transactional(readOnly = true)
    public Map<String, Long> getSecurityStatusCounts() {
        Map<String, Long> counts = new HashMap<>();
        
        LocalDateTime recentThreshold = LocalDateTime.now().minusDays(30);
        counts.put("total", repository.count());
        counts.put("locked", repository.countLockedAccounts());
        counts.put("twoFactorEnabled", repository.countTwoFactorEnabledAccounts());
        counts.put("active", repository.countActiveAccounts(recentThreshold));
        counts.put("withFailedAttempts", repository.countAccountsWithFailedAttempts(1));
        
        return counts;
    }
    
    @Override
    @Transactional(readOnly = true)
    public Map<String, Object> getFailedLoginStatistics(int daysPeriod) {
        Map<String, Object> stats = new HashMap<>();
        
        stats.put("accountsWithFailedAttempts", repository.countAccountsWithFailedAttempts(1));
        stats.put("accountsWithMultipleFailures", repository.countAccountsWithFailedAttempts(3));
        stats.put("lockedAccounts", repository.countLockedAccounts());
        
        return stats;
    }
    
    @Override
    @Transactional(readOnly = true)
    public Map<String, Object> getPasswordStatistics() {
        Map<String, Object> stats = new HashMap<>();
        
        LocalDateTime oldPasswordThreshold = LocalDateTime.now().minusDays(90);
        List<UserCredentials> oldPasswords = repository.findAccountsWithOldPasswords(oldPasswordThreshold);
        
        stats.put("accountsWithOldPasswords", oldPasswords.size());
        stats.put("passwordExpiryThreshold", "90 days");
        
        return stats;
    }
    
    @Override
    @Transactional(readOnly = true)
    public Map<String, Object> getTwoFactorStatistics() {
        Map<String, Object> stats = new HashMap<>();
        
        long totalAccounts = repository.count();
        long twoFactorEnabled = repository.countTwoFactorEnabledAccounts();
        
        stats.put("totalAccounts", totalAccounts);
        stats.put("twoFactorEnabled", twoFactorEnabled);
        stats.put("twoFactorDisabled", totalAccounts - twoFactorEnabled);
        stats.put("adoptionRate", totalAccounts > 0 ? (double) twoFactorEnabled / totalAccounts * 100 : 0);
        
        return stats;
    }
    
    @Override
    public Map<String, Integer> performSecurityMaintenance() {
        log.info("Performing routine security maintenance");
        
        Map<String, Integer> results = new HashMap<>();
        results.put("expiredPasswordResetTokensCleared", clearExpiredPasswordResetTokens());
        results.put("expiredLocksUnlocked", unlockExpiredAccountLocks());
        results.put("failedAttemptsReset", resetFailedAttemptsForUnlockedAccounts());
        
        log.info("Security maintenance completed: {}", results);
        return results;
    }
    
    @Override
    @Transactional(readOnly = true)
    public Map<String, Object> generateSecurityReport() {
        Map<String, Object> report = new HashMap<>();
        
        report.put("timestamp", LocalDateTime.now());
        report.put("statistics", getSecurityStatistics());
        report.put("analytics", getSecurityAnalytics(30));
        report.put("statusCounts", getSecurityStatusCounts());
        report.put("twoFactorStats", getTwoFactorStatistics());
        
        return report;
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<String> checkSecurityAnomalies() {
        List<String> anomalies = new ArrayList<>();
        
        // Check for high numbers of locked accounts
        long lockedCount = repository.countLockedAccounts();
        if (lockedCount > 10) {
            anomalies.add("High number of locked accounts: " + lockedCount);
        }
        
        // Check for accounts with many failed attempts
        long failedAttemptsCount = repository.countAccountsWithFailedAttempts(3);
        if (failedAttemptsCount > 5) {
            anomalies.add("High number of accounts with failed login attempts: " + failedAttemptsCount);
        }
        
        // Check two-factor adoption
        Map<String, Object> twoFactorStats = getTwoFactorStatistics();
        double adoptionRate = (Double) twoFactorStats.get("adoptionRate");
        if (adoptionRate < 50.0) {
            anomalies.add("Low two-factor authentication adoption rate: " + String.format("%.1f%%", adoptionRate));
        }
        
        return anomalies;
    }
    
    @Override
    @Transactional(readOnly = true)
    public boolean isPasswordStrong(String password) {
        return getPasswordStrengthScore(password) >= 80;
    }
    
    @Override
    @Transactional(readOnly = true)
    public int getPasswordStrengthScore(String password) {
        if (password == null || password.isEmpty()) {
            return 0;
        }
        
        int score = 0;
        
        // Length scoring
        if (password.length() >= MIN_PASSWORD_LENGTH) score += 20;
        if (password.length() >= 12) score += 10;
        if (password.length() >= 16) score += 10;
        
        // Character variety scoring
        if (UPPERCASE_PATTERN.matcher(password).find()) score += 15;
        if (LOWERCASE_PATTERN.matcher(password).find()) score += 15;
        if (DIGIT_PATTERN.matcher(password).find()) score += 15;
        if (SPECIAL_CHAR_PATTERN.matcher(password).find()) score += 15;
        
        return Math.min(100, score);
    }
    
    @Override
    @Transactional(readOnly = true)
    public boolean meetsPasswordPolicy(String password) {
        return getPasswordPolicyViolations(password).isEmpty();
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<String> getPasswordPolicyViolations(String password) {
        List<String> violations = new ArrayList<>();
        
        if (password == null || password.isEmpty()) {
            violations.add("Password is required");
            return violations;
        }
        
        if (password.length() < MIN_PASSWORD_LENGTH) {
            violations.add("Password must be at least " + MIN_PASSWORD_LENGTH + " characters long");
        }
        
        if (password.length() > MAX_PASSWORD_LENGTH) {
            violations.add("Password must not exceed " + MAX_PASSWORD_LENGTH + " characters");
        }
        
        if (!UPPERCASE_PATTERN.matcher(password).find()) {
            violations.add("Password must contain at least one uppercase letter");
        }
        
        if (!LOWERCASE_PATTERN.matcher(password).find()) {
            violations.add("Password must contain at least one lowercase letter");
        }
        
        if (!DIGIT_PATTERN.matcher(password).find()) {
            violations.add("Password must contain at least one digit");
        }
        
        if (!SPECIAL_CHAR_PATTERN.matcher(password).find()) {
            violations.add("Password must contain at least one special character");
        }
        
        return violations;
    }
    
    @Override
    @Transactional(readOnly = true)
    public boolean isPasswordResetTokenValid(String token) {
        return validatePasswordResetToken(token);
    }
    
    @Override
    @Transactional(readOnly = true)
    public boolean isEmailVerificationTokenValid(String token) {
        return validateEmailVerificationToken(token);
    }
    
    @Override
    @Transactional(readOnly = true)
    public Optional<Long> findUserIdByPasswordResetToken(String token) {
        return repository.findByPasswordResetToken(token)
                .map(UserCredentials::getUserId);
    }
    
    @Override
    @Transactional(readOnly = true)
    public Optional<Long> findUserIdByEmailVerificationToken(String token) {
        return repository.findByEmailVerificationToken(token)
                .map(UserCredentials::getUserId);
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<String> getSecurityEvents(Long userId, int daysPeriod) {
        // This is a placeholder - in a real implementation, you would query a security audit log
        List<String> events = new ArrayList<>();
        events.add("This feature requires audit log implementation");
        return events;
    }
    
    @Override
    public void logSecurityEvent(Long userId, String eventType, String description) {
        // This is a placeholder - in a real implementation, you would log to an audit system
        log.info("Security Event - User ID: {}, Event: {}, Description: {}", userId, eventType, description);
    }
    
    @Override
    @Transactional(readOnly = true)
    public Map<String, Object> checkSecurityHealth() {
        Map<String, Object> health = new HashMap<>();
        
        health.put("status", "HEALTHY");
        health.put("timestamp", LocalDateTime.now());
        
        // Check various health metrics
        long totalAccounts = repository.count();
        long lockedAccounts = repository.countLockedAccounts();
        
        if (totalAccounts > 0) {
            double lockRate = (double) lockedAccounts / totalAccounts;
            if (lockRate > 0.1) { // More than 10% locked
                health.put("status", "WARNING");
                health.put("issue", "High account lock rate: " + String.format("%.1f%%", lockRate * 100));
            }
        }
        
        health.put("totalAccounts", totalAccounts);
        health.put("lockedAccounts", lockedAccounts);
        
        return health;
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<String> getSecurityRecommendations() {
        List<String> recommendations = new ArrayList<>();
        
        // Check two-factor adoption
        Map<String, Object> twoFactorStats = getTwoFactorStatistics();
        double adoptionRate = (Double) twoFactorStats.get("adoptionRate");
        if (adoptionRate < 80.0) {
            recommendations.add("Increase two-factor authentication adoption (currently " + 
                             String.format("%.1f%%", adoptionRate) + ")");
        }
        
        // Check for old passwords
        LocalDateTime oldThreshold = LocalDateTime.now().minusDays(90);
        List<UserCredentials> oldPasswords = repository.findAccountsWithOldPasswords(oldThreshold);
        if (!oldPasswords.isEmpty()) {
            recommendations.add("Encourage " + oldPasswords.size() + " users to update old passwords");
        }
        
        // Check for inactive accounts
        LocalDateTime inactiveThreshold = LocalDateTime.now().minusDays(180);
        List<UserCredentials> inactiveAccounts = repository.findInactiveAccounts(inactiveThreshold);
        if (!inactiveAccounts.isEmpty()) {
            recommendations.add("Review " + inactiveAccounts.size() + " inactive accounts for possible deactivation");
        }
        
        return recommendations;
    }
    
    // Helper methods
    
    private String generateSecureToken() {
        byte[] tokenBytes = new byte[32];
        secureRandom.nextBytes(tokenBytes);
        return Base64.getUrlEncoder().withoutPadding().encodeToString(tokenBytes);
    }
}
