import string
import secrets
import math

class PasswordGenerator:
    def __init__(self, length=16, use_upper=True, use_lower=True, use_digits=True, use_symbols=True):
        self.length = max(8, length)  # Minimum 8 chars
        self.use_upper = use_upper
        self.use_lower = use_lower
        self.use_digits = use_digits
        self.use_symbols = use_symbols
        
    def _get_charset(self):
        charset = ""
        if self.use_lower:
            charset += string.ascii_lowercase
        if self.use_upper:
            charset += string.ascii_uppercase
        if self.use_digits:
            charset += string.digits
        if self.use_symbols:
            charset += "!@#$%^&*()_+-=[]{}|;:,.<>?"
        return charset
    
    def generate(self):
        """Generate a secure password"""
        charset = self._get_charset()
        if not charset:
            raise ValueError("No character sets selected!")
        
        password = ''.join(secrets.choice(charset) for _ in range(self.length))
        
        # Ensure at least one of each selected type
        if self.use_lower and self.use_upper and self.use_digits and self.use_symbols:
            # Simple guarantee: force one of each if all enabled
            result = [
                secrets.choice(string.ascii_lowercase),
                secrets.choice(string.ascii_uppercase),
                secrets.choice(string.digits),
                secrets.choice("!@#$%^&*()_+-=[]{}|;:,.<>?")
            ]
            result.extend(secrets.choice(charset) for _ in range(self.length - 4))
            secrets.SystemRandom().shuffle(result)
            password = ''.join(result)
        
        return password
    
    def entropy(self, password=None):
        """Calculate password entropy in bits"""
        if password is None:
            password = self.generate()
        
        charset_size = len(self._get_charset())
        return len(password) * math.log2(charset_size)
