import pyotp
from typing import Optional, Tuple
import qrcode
import io
import base64

from genovation_advanced_auth.core.config import AuthConfig, get_config
from genovation_advanced_auth.core.security import generate_backup_codes, hash_backup_codes


class MFAManager:
    """Manager for TOTP-based Multi-Factor Authentication."""
    
    def __init__(self, config: Optional[AuthConfig] = None):
        """
        Initialize MFA manager.
        
        Args:
            config: Optional AuthConfig instance. If None, uses global config from .env.
        """
        self.config = config or get_config()
    
    @staticmethod
    def generate_secret() -> str:
        """Generate a new TOTP secret."""
        return pyotp.random_base32()
    
    @staticmethod
    def get_totp(secret: str) -> pyotp.TOTP:
        """Get TOTP instance from secret."""
        return pyotp.TOTP(secret)
    
    def generate_provisioning_uri(self, secret: str, username: str) -> str:
        """
        Generate provisioning URI for QR code.
        
        Args:
            secret: TOTP secret
            username: User identifier
        
        Returns:
            Provisioning URI string
        """
        totp = pyotp.TOTP(secret)
        return totp.provisioning_uri(
            name=username,
            issuer_name=self.config.mfa_issuer_name  # ✅ From config
        )
    
    @staticmethod
    def verify_code(secret: str, code: str, window: int = 1) -> bool:
        """
        Verify TOTP code.
        
        Args:
            secret: User's TOTP secret
            code: 6-digit code from authenticator app
            window: Number of 30-second windows to check
        
        Returns:
            True if code is valid, False otherwise
        """
        if not code or len(code) != 6:
            return False
        
        try:
            totp = pyotp.TOTP(secret)
            return totp.verify(code, valid_window=window)
        except Exception:
            return False
    
    @staticmethod
    def get_current_code(secret: str) -> str:
        """Get current TOTP code (for testing/debugging only)."""
        totp = pyotp.TOTP(secret)
        return totp.now()
    
    def setup_mfa(self, username: str) -> Tuple[str, str, list[str]]:
        """
        Complete MFA setup for a user.
        
        Args:
            username: User identifier
        
        Returns:
            Tuple of (secret, provisioning_uri, backup_codes)
        """
        secret = self.generate_secret()
        uri = self.generate_provisioning_uri(secret, username)
        backup_codes = generate_backup_codes(self.config.mfa_backup_codes_count)  # ✅ From config
        
        return secret, uri, backup_codes
    
    @staticmethod
    def generate_qr_code(data: str, file_path: str = None) -> bytes:
        """
        Generate QR code from data.
        
        Args:
            data: Data to encode (e.g., TOTP provisioning URI)
            file_path: Optional path to save QR code image
        
        Returns:
            QR code image as bytes
        """
        qr = qrcode.QRCode(
            version=1,
            error_correction=qrcode.constants.ERROR_CORRECT_L,
            box_size=10,
            border=4,
        )
        qr.add_data(data)
        qr.make(fit=True)
        
        img = qr.make_image(fill_color="black", back_color="white")
        
        if file_path:
            img.save(file_path)
        
        buffer = io.BytesIO()
        img.save(buffer, format='PNG')
        buffer.seek(0)
        return buffer.getvalue()
    
    @staticmethod
    def generate_qr_code_base64(data: str) -> str:
        """
        Generate QR code and return as base64 string.
        
        Returns:
            Base64-encoded PNG image
        """
        qr_bytes = MFAManager.generate_qr_code(data)
        qr_base64 = base64.b64encode(qr_bytes).decode('utf-8')
        return f"data:image/png;base64,{qr_base64}"


# ✅ Convenience function using global config
def get_mfa_manager(config: Optional[AuthConfig] = None) -> MFAManager:
    """
    Get MFA manager instance.
    
    Args:
        config: Optional custom config. If None, uses global config.
    
    Returns:
        MFAManager instance
    """
    return MFAManager(config)
