from functools import wraps
from flask import jsonify
from flask_jwt_extended import verify_jwt_in_request, get_jwt_identity
from app.database import db
from app.models import User, UserRole

def get_password_hash(password: str) -> str:
    """Hash a password using bcrypt."""
    import bcrypt
    
    # Bcrypt has a 72 byte limit, so truncate if necessary
    if isinstance(password, str):
        password_bytes = password.encode('utf-8')
        if len(password_bytes) > 72:
            password_bytes = password_bytes[:72]
        # Use bcrypt directly to avoid passlib compatibility issues
        salt = bcrypt.gensalt()
        return bcrypt.hashpw(password_bytes, salt).decode('utf-8')
    else:
        # If it's already bytes
        password_bytes = password if isinstance(password, bytes) else str(password).encode('utf-8')
        if len(password_bytes) > 72:
            password_bytes = password_bytes[:72]
        salt = bcrypt.gensalt()
        return bcrypt.hashpw(password_bytes, salt).decode('utf-8')

def verify_password(plain_password: str, hashed_password: str) -> bool:
    """Verify a password against a hash."""
    import bcrypt
    
    # Bcrypt has a 72 byte limit, so truncate if necessary
    if isinstance(plain_password, str):
        password_bytes = plain_password.encode('utf-8')
        if len(password_bytes) > 72:
            password_bytes = password_bytes[:72]
    else:
        password_bytes = plain_password if isinstance(plain_password, bytes) else str(plain_password).encode('utf-8')
        if len(password_bytes) > 72:
            password_bytes = password_bytes[:72]
    
    if isinstance(hashed_password, str):
        hashed_password_bytes = hashed_password.encode('utf-8')
    else:
        hashed_password_bytes = hashed_password
    
    try:
        return bcrypt.checkpw(password_bytes, hashed_password_bytes)
    except Exception:
        return False

def get_current_user():
    """Get the current authenticated user."""
    try:
        verify_jwt_in_request()
        user_id = get_jwt_identity()
        return db.session.get(User, user_id)
    except:
        return None

def require_auth(f):
    """Decorator to require authentication."""
    @wraps(f)
    def decorated_function(*args, **kwargs):
        verify_jwt_in_request()
        return f(*args, **kwargs)
    return decorated_function

def require_role(allowed_roles):
    """Decorator to require specific roles."""
    def decorator(f):
        @wraps(f)
        @require_auth
        def decorated_function(*args, **kwargs):
            user = get_current_user()
            if not user or user.role.value not in allowed_roles:
                return jsonify({'error': 'Not enough permissions'}), 403
            return f(*args, **kwargs)
        return decorated_function
    return decorator

def require_super_admin(f):
    """Decorator to require super admin role."""
    return require_role(['super-admin'])(f)

def require_business_admin_or_super_admin(business_id_param='business_id'):
    """Decorator factory to require business admin or super admin."""
    def decorator(f):
        @wraps(f)
        @require_auth
        def decorated_function(*args, **kwargs):
            user = get_current_user()
            if not user:
                return jsonify({'error': 'Authentication required'}), 401
            
            if user.role.value == 'super-admin':
                return f(*args, **kwargs)
            
            business_id = kwargs.get(business_id_param) or kwargs.get('id')
            if user.role.value != 'business-admin' or user.business_id != business_id:
                return jsonify({'error': 'Business admin access required for this business'}), 403
            
            return f(*args, **kwargs)
        return decorated_function
    return decorator
