import Foundation

/// Validation errors that can be returned from validation rules
enum ValidationError: LocalizedError {
    case required(field: String)
    case invalidEmail
    case passwordTooShort(minLength: Int)
    case passwordMismatch
    case passwordMissingLetter
    case passwordMissingUppercase
    case passwordMissingLowercase
    case passwordMissingNumber
    case invalidCode(expectedLength: Int)
    case invalidUsername
    case custom(message: String)

    var errorDescription: String? {
        switch self {
        case .required(let field):
            return "\(field) is required"
        case .invalidEmail:
            return "Please enter a valid email address"
        case .passwordTooShort(let minLength):
            return "Password must be at least \(minLength) characters"
        case .passwordMismatch:
            return "Passwords do not match"
        case .passwordMissingLetter:
            return "Password must contain at least one letter"
        case .passwordMissingUppercase:
            return "Password must contain at least one uppercase letter"
        case .passwordMissingLowercase:
            return "Password must contain at least one lowercase letter"
        case .passwordMissingNumber:
            return "Password must contain at least one number"
        case .invalidCode(let length):
            return "Code must be \(length) digits"
        case .invalidUsername:
            return "Username can only contain letters, numbers, and underscores"
        case .custom(let message):
            return message
        }
    }
}

/// Centralized validation rules for the app
/// Use these instead of inline validation logic in ViewModels
enum ValidationRules {

    // MARK: - Email Validation

    /// Validates an email address
    /// - Parameter email: The email to validate
    /// - Returns: ValidationError if invalid, nil if valid
    static func validateEmail(_ email: String) -> ValidationError? {
        let trimmed = email.trimmingCharacters(in: .whitespacesAndNewlines)

        if trimmed.isEmpty {
            return .required(field: "Email")
        }

        let emailRegex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
        let predicate = NSPredicate(format: "SELF MATCHES %@", emailRegex)

        if !predicate.evaluate(with: trimmed) {
            return .invalidEmail
        }

        return nil
    }

    /// Check if email format is valid (without required check)
    /// - Parameter email: The email to check
    /// - Returns: true if valid format
    static func isValidEmail(_ email: String) -> Bool {
        let emailRegex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
        let predicate = NSPredicate(format: "SELF MATCHES %@", emailRegex)
        return predicate.evaluate(with: email)
    }

    // MARK: - Password Validation

    /// Validates a password with minimum length
    /// - Parameters:
    ///   - password: The password to validate
    ///   - minLength: Minimum required length (default: 8)
    /// - Returns: ValidationError if invalid, nil if valid
    static func validatePassword(_ password: String, minLength: Int = 8) -> ValidationError? {
        if password.isEmpty {
            return .required(field: "Password")
        }

        if password.count < minLength {
            return .passwordTooShort(minLength: minLength)
        }

        return nil
    }

    /// Validates a password with uppercase, lowercase, and number requirements
    /// - Parameter password: The password to validate
    /// - Returns: ValidationError if invalid, nil if valid
    static func validatePasswordStrength(_ password: String) -> ValidationError? {
        if password.isEmpty {
            return .required(field: "Password")
        }

        let hasUppercase = password.range(of: "[A-Z]", options: .regularExpression) != nil
        let hasLowercase = password.range(of: "[a-z]", options: .regularExpression) != nil
        let hasNumber = password.range(of: "[0-9]", options: .regularExpression) != nil

        if !hasUppercase {
            return .passwordMissingUppercase
        }

        if !hasLowercase {
            return .passwordMissingLowercase
        }

        if !hasNumber {
            return .passwordMissingNumber
        }

        return nil
    }

    /// Check if password has required strength (uppercase + lowercase + number)
    /// - Parameter password: The password to check
    /// - Returns: true if valid strength
    static func isValidPassword(_ password: String) -> Bool {
        let hasUppercase = password.range(of: "[A-Z]", options: .regularExpression) != nil
        let hasLowercase = password.range(of: "[a-z]", options: .regularExpression) != nil
        let hasNumber = password.range(of: "[0-9]", options: .regularExpression) != nil
        return hasUppercase && hasLowercase && hasNumber
    }

    /// Validates that two passwords match
    /// - Parameters:
    ///   - password: The password
    ///   - confirmPassword: The confirmation password
    /// - Returns: ValidationError if they don't match, nil if they match
    static func validatePasswordMatch(_ password: String, _ confirmPassword: String) -> ValidationError? {
        if password != confirmPassword {
            return .passwordMismatch
        }
        return nil
    }

    // MARK: - Code Validation

    /// Validates a numeric code (like verification codes)
    /// - Parameters:
    ///   - code: The code to validate
    ///   - expectedLength: Expected length of the code (default: 6)
    /// - Returns: ValidationError if invalid, nil if valid
    static func validateCode(_ code: String, expectedLength: Int = 6) -> ValidationError? {
        let trimmed = code.trimmingCharacters(in: .whitespacesAndNewlines)

        if trimmed.isEmpty {
            return .required(field: "Code")
        }

        if trimmed.count != expectedLength || !trimmed.allSatisfy({ $0.isNumber }) {
            return .invalidCode(expectedLength: expectedLength)
        }

        return nil
    }

    // MARK: - Username Validation

    /// Validates a username (alphanumeric + underscores only)
    /// - Parameter username: The username to validate
    /// - Returns: ValidationError if invalid, nil if valid
    static func validateUsername(_ username: String) -> ValidationError? {
        let trimmed = username.trimmingCharacters(in: .whitespacesAndNewlines)

        if trimmed.isEmpty {
            return .required(field: "Username")
        }

        let usernameRegex = "^[A-Za-z0-9_]+$"
        let predicate = NSPredicate(format: "SELF MATCHES %@", usernameRegex)

        if !predicate.evaluate(with: trimmed) {
            return .invalidUsername
        }

        return nil
    }

    // MARK: - Required Field Validation

    /// Validates that a field is not empty
    /// - Parameters:
    ///   - value: The value to check
    ///   - fieldName: The name of the field (for error message)
    /// - Returns: ValidationError if empty, nil if not empty
    static func validateRequired(_ value: String, fieldName: String) -> ValidationError? {
        if value.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
            return .required(field: fieldName)
        }
        return nil
    }

    /// Check if a value is not empty
    /// - Parameter value: The value to check
    /// - Returns: true if not empty
    static func isNotEmpty(_ value: String) -> Bool {
        !value.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
    }
}
