package com.gnx.telemedicine.util;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.regex.Pattern;

/**
 * Utility class for password validation and complexity checking.
 * Implements enterprise-grade password policies.
 * Configurable via application properties.
 */
@Component
public class PasswordValidator {
    
    // Password policy constants - configurable via properties
    @Value("${security.password.min-length:8}")
    private int minLength;
    
    @Value("${security.password.max-length:128}")
    private int maxLength;
    
    @Value("${security.password.require-uppercase:true}")
    private boolean requireUppercase;
    
    @Value("${security.password.require-lowercase:true}")
    private boolean requireLowercase;
    
    @Value("${security.password.require-digit:true}")
    private boolean requireDigit;
    
    @Value("${security.password.require-special-char:true}")
    private boolean requireSpecialChar;
    
    @Value("${security.password.max-repeated-chars:4}")
    private int maxRepeatedChars;
    
    @Value("${security.password.max-sequential-chars:4}")
    private int maxSequentialChars;
    
    @Value("${security.password.min-strength-score:50}")
    private int minStrengthScore;
    
    @Value("${security.password.check-common-passwords:true}")
    private boolean checkCommonPasswords;
    
    // Patterns for password validation
    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(".*\\d.*");
    private static final Pattern SPECIAL_CHAR_PATTERN = Pattern.compile(".*[!@#$%^&*()_+\\-=\\[\\]{};':\"\\\\|,.<>\\/?].*");
    
    // Common weak passwords (should be expanded)
    private static final String[] COMMON_PASSWORDS = {
        "password", "12345678", "password123", "admin123", "qwerty123",
        "welcome123", "password1", "123456789", "letmein", "welcome"
    };
    
    /**
     * Validates password against complexity requirements.
     * 
     * @param password The password to validate
     * @return ValidationResult with isValid flag and error message
     */
    public ValidationResult validate(String password) {
        if (password == null || password.isEmpty()) {
            return new ValidationResult(false, "Password is required");
        }
        
        // Check length
        if (password.length() < minLength) {
            return new ValidationResult(false, 
                String.format("Password must be at least %d characters long", minLength));
        }
        
        if (password.length() > maxLength) {
            return new ValidationResult(false, 
                String.format("Password must be no more than %d characters long", maxLength));
        }
        
        // Check for uppercase letter
        if (requireUppercase && !UPPERCASE_PATTERN.matcher(password).matches()) {
            return new ValidationResult(false, 
                "Password must contain at least one uppercase letter");
        }
        
        // Check for lowercase letter
        if (requireLowercase && !LOWERCASE_PATTERN.matcher(password).matches()) {
            return new ValidationResult(false, 
                "Password must contain at least one lowercase letter");
        }
        
        // Check for digit
        if (requireDigit && !DIGIT_PATTERN.matcher(password).matches()) {
            return new ValidationResult(false, 
                "Password must contain at least one digit");
        }
        
        // Check for special character
        if (requireSpecialChar && !SPECIAL_CHAR_PATTERN.matcher(password).matches()) {
            return new ValidationResult(false, 
                "Password must contain at least one special character (!@#$%^&*()_+-=[]{}|;':\",./<>?)");
        }
        
        // Check for common weak passwords
        if (checkCommonPasswords) {
            String lowerPassword = password.toLowerCase();
            for (String common : COMMON_PASSWORDS) {
                if (lowerPassword.contains(common.toLowerCase())) {
                    return new ValidationResult(false, 
                        "Password is too common or weak. Please choose a stronger password");
                }
            }
        }
        
        // Check for repeated characters (e.g., "aaaaaa")
        if (hasRepeatedCharacters(password, maxRepeatedChars)) {
            return new ValidationResult(false, 
                String.format("Password contains too many repeated characters (maximum %d allowed)", maxRepeatedChars));
        }
        
        // Check for sequential characters (e.g., "1234", "abcd")
        if (hasSequentialCharacters(password, maxSequentialChars)) {
            return new ValidationResult(false, 
                String.format("Password contains sequential characters which are easy to guess (maximum %d allowed)", maxSequentialChars));
        }
        
        // Check minimum strength score
        int strengthScore = calculateStrength(password);
        if (strengthScore < minStrengthScore) {
            return new ValidationResult(false, 
                String.format("Password is too weak. Minimum strength score required: %d (current: %d)", minStrengthScore, strengthScore));
        }
        
        return new ValidationResult(true, "Password is valid");
    }
    
    /**
     * Static method for backward compatibility.
     * Note: This uses default values and should be replaced with instance method.
     * @deprecated Use instance method validate() instead
     */
    @Deprecated
    public static ValidationResult validateStatic(String password) {
        // Create a default instance for static access
        PasswordValidator validator = new PasswordValidator();
        validator.minLength = 8;
        validator.maxLength = 128;
        validator.requireUppercase = true;
        validator.requireLowercase = true;
        validator.requireDigit = true;
        validator.requireSpecialChar = true;
        validator.maxRepeatedChars = 4;
        validator.maxSequentialChars = 4;
        validator.minStrengthScore = 50;
        validator.checkCommonPasswords = true;
        return validator.validate(password);
    }
    
    /**
     * Checks if password contains repeated characters.
     */
    private static boolean hasRepeatedCharacters(String password, int maxRepeats) {
        char[] chars = password.toCharArray();
        int count = 1;
        char prev = chars[0];
        
        for (int i = 1; i < chars.length; i++) {
            if (chars[i] == prev) {
                count++;
                if (count > maxRepeats) {
                    return true;
                }
            } else {
                count = 1;
                prev = chars[i];
            }
        }
        return false;
    }
    
    /**
     * Checks if password contains sequential characters.
     */
    private static boolean hasSequentialCharacters(String password, int sequenceLength) {
        String lowerPassword = password.toLowerCase();
        char[] chars = lowerPassword.toCharArray();
        
        for (int i = 0; i <= chars.length - sequenceLength; i++) {
            boolean isSequential = true;
            for (int j = 1; j < sequenceLength; j++) {
                if (chars[i + j] != chars[i + j - 1] + 1) {
                    isSequential = false;
                    break;
                }
            }
            if (isSequential) {
                return true;
            }
        }
        return false;
    }
    
    /**
     * Calculates password strength score (0-100).
     */
    public int calculateStrength(String password) {
        if (password == null || password.isEmpty()) {
            return 0;
        }
        
        int score = 0;
        
        // Length score (max 30 points)
        if (password.length() >= 12) score += 30;
        else if (password.length() >= 10) score += 20;
        else if (password.length() >= 8) score += 10;
        
        // Character variety score (max 40 points)
        if (UPPERCASE_PATTERN.matcher(password).matches()) score += 10;
        if (LOWERCASE_PATTERN.matcher(password).matches()) score += 10;
        if (DIGIT_PATTERN.matcher(password).matches()) score += 10;
        if (SPECIAL_CHAR_PATTERN.matcher(password).matches()) score += 10;
        
        // Complexity bonus (max 30 points)
        int uniqueChars = (int) password.chars().distinct().count();
        if (uniqueChars >= password.length() * 0.8) score += 30;
        else if (uniqueChars >= password.length() * 0.6) score += 20;
        else if (uniqueChars >= password.length() * 0.4) score += 10;
        
        return Math.min(100, score);
    }
    
    /**
     * Validation result class.
     */
    public static class ValidationResult {
        private final boolean isValid;
        private final String message;
        
        public ValidationResult(boolean isValid, String message) {
            this.isValid = isValid;
            this.message = message;
        }
        
        public boolean isValid() {
            return isValid;
        }
        
        public String getMessage() {
            return message;
        }
    }
}

