"""
User authentication and profile management routes
"""
import uuid  # Add missing import
import time  # Add missing import
from datetime import datetime, timedelta
from flask import Blueprint, render_template, redirect, url_for, flash, request, current_app, session, jsonify
from flask_login import login_user, current_user, logout_user, login_required  # Ensure flask_login is imported
from werkzeug.security import generate_password_hash, check_password_hash
from sqlalchemy.exc import IntegrityError

from musicround.models import db, User, Role, SystemSetting
from musicround.helpers.utils import get_available_voices
from musicround.helpers.auth_helpers import oauth, find_or_create_user, update_oauth_tokens, get_google_user_info, get_authentik_user_info, get_spotify_user_info, get_oauth_redirect_uri
from musicround.helpers.spotify_helper import get_spotify_token, get_current_user_spotify_token, get_spotify_user_info as spotify_helper_get_user_info

users_bp = Blueprint('users', __name__, url_prefix='/users')

# Helper function for processing Spotify account linking
def _process_spotify_link(user, token_payload, spotify_user_data):
    """Processes the linking of a Spotify account to a user."""
    spotify_id = spotify_user_data['id']

    # Check if this Spotify account is already linked to another user
    existing_user_with_spotify_id = User.query.filter(User.spotify_id == spotify_id, User.id != user.id).first()
    if existing_user_with_spotify_id:
        flash(f"This Spotify account is already linked to another user ({existing_user_with_spotify_id.username}). "
              "Please use a different Spotify account or log in with that user.", "danger")
        return False

    user.spotify_id = spotify_id
    update_oauth_tokens(user, token_payload, 'spotify') # Saves token, refresh_token, expiry to user model

    # Store comprehensive user info in session
    session['spotify_user_info'] = spotify_user_data

    db.session.commit()
    flash('Your Spotify account has been successfully linked!', 'success')
    current_app.logger.info(f"User {user.id} successfully linked Spotify account {spotify_id}.")
    return True

# Helper function for processing Spotify account disconnection
def _process_spotify_disconnect(user):
    """Processes the disconnection of a user's Spotify account."""
    current_app.logger.info(f"User {user.id} requested Spotify disconnect.")
    
    user.spotify_id = None
    user.spotify_token = None
    user.spotify_refresh_token = None
    user.spotify_token_expiry = None
    
    # Clear related session variables
    session.pop('spotify_display_name', None) 
    session.pop('spotify_user_info', None)
        
    db.session.commit()
    flash('Your Spotify account has been disconnected.', 'success')
    current_app.logger.info(f"User {user.id} successfully disconnected their Spotify account.")

def admin_required(f):
    from functools import wraps
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if not current_user.is_authenticated or not current_user.is_admin:
            flash('Admin access required.', 'danger')
            return redirect(url_for('users.profile'))
        return f(*args, **kwargs)
    return decorated_function

# OAuth login routes for Google
@users_bp.route('/login/google')
def google_login():
    """Initiate Google OAuth login flow"""
    if current_user.is_authenticated:
        return redirect(url_for('core.index'))    # Google login is disabled if client ID is not set
    if not current_app.config.get('GOOGLE_CLIENT_ID'):
        flash('Google login is not configured.', 'danger')
        return redirect(url_for('users.login'))
    
    redirect_uri = get_oauth_redirect_uri('users.google_callback')
    return oauth.google.authorize_redirect(redirect_uri)

@users_bp.route('/login/google/callback')
def google_callback():
    """Handle Google OAuth callback"""
    try:
        token = oauth.google.authorize_access_token()
        user_info = oauth.google.userinfo()
        
        # Debug the response
        current_app.logger.debug(f"Google user info response: {user_info}")
        
        # Check for required fields and adapt to Google's response format
        if 'sub' in user_info and 'id' not in user_info:
            user_info['id'] = user_info['sub']  # Google uses 'sub' for the user ID
        
        # Find or create user
        user = find_or_create_user(user_info, 'google')
        if not user:
            flash('Could not authenticate with Google. Please try again.', 'danger')
            return redirect(url_for('users.login'))
            
        # Update tokens and log in the user
        update_oauth_tokens(user, token, 'google')
        login_user(user)
        
        # Set last_login as a datetime object for consistency
        user.last_login = datetime.now()
        db.session.commit()
        
        # Redirect to next page or home
        next_page = request.args.get('next')
        if not next_page or not next_page.startswith('/'):
            next_page = url_for('core.index')
            
        flash('You have been logged in via Google!', 'success')
        return redirect(next_page)
        
    except Exception as e:
        current_app.logger.error(f"Error in Google callback: {str(e)}")
        flash('An error occurred during Google authentication. Please try again.', 'danger')
        return redirect(url_for('users.login'))

# OAuth login routes for Authentik
@users_bp.route('/login/authentik')
def authentik_login():
    """Initiate Authentik OAuth login flow"""
    if current_user.is_authenticated:
        return redirect(url_for('core.index'))    # Authentik login is disabled if client ID is not set
    if not current_app.config.get('AUTHENTIK_CLIENT_ID'):
        flash('Authentik login is not configured.', 'danger')
        return redirect(url_for('users.login'))
    
    redirect_uri = get_oauth_redirect_uri('users.authentik_callback')
    return oauth.authentik.authorize_redirect(redirect_uri)

@users_bp.route('/login/authentik/callback')
def authentik_callback():
    """Handle Authentik OAuth callback"""
    try:
        token = oauth.authentik.authorize_access_token()
        user_info = token.get('userinfo')
        
        # Debug the response
        current_app.logger.debug(f"Authentik user info response: {user_info}")
        
        if not user_info:
            # If userinfo not in token, fetch it separately
            user_info = get_authentik_user_info(token)
            
        # Check for required fields and adapt to Authentik's response format
        if user_info and 'sub' in user_info and 'id' not in user_info:
            user_info['id'] = user_info['sub']  # Authentik uses 'sub' for the user ID
            
        # Find or create user
        user = find_or_create_user(user_info, 'authentik')
        if not user:
            flash('Could not authenticate with Authentik. Please try again.', 'danger')
            return redirect(url_for('users.login'))
            
        # Update tokens and log in the user
        update_oauth_tokens(user, token, 'authentik')
        login_user(user)
        
        user.last_login = datetime.now()
        db.session.commit()
        
        # Redirect to next page or home
        next_page = request.args.get('next')
        if not next_page or not next_page.startswith('/'):
            next_page = url_for('core.index')
            
        flash('You have been logged in via Authentik!', 'success')
        return redirect(next_page)
        
    except Exception as e:
        current_app.logger.error(f"Error in Authentik callback: {str(e)}")
        flash('An error occurred during Authentik authentication. Please try again.', 'danger')
        return redirect(url_for('users.login'))

# OAuth login routes for Dropbox
@users_bp.route('/dropbox/auth')
@login_required
def dropbox_auth():
    """Initiate Dropbox OAuth flow"""
    from musicround.helpers.dropbox_helper import get_dropbox_auth_url
    
    # Generate the authorization URL
    auth_url = get_dropbox_auth_url()
    
    if auth_url:
        # Store state in session to prevent CSRF
        return redirect(auth_url)
    else:
        flash('Error initiating Dropbox authorization', 'error')
        return redirect(url_for('users.profile'))

@users_bp.route('/dropbox/callback')
@login_required
def dropbox_callback():
    """Handle Dropbox OAuth callback"""
    from musicround.helpers.dropbox_helper import exchange_code_for_token, get_dropbox_account_info
    
    error = request.args.get('error')
    if error:
        flash(f'Dropbox authorization failed: {error}', 'error')
        return redirect(url_for('users.profile'))
    
    code = request.args.get('code')
    if not code:
        flash('No authorization code received from Dropbox', 'error')
        return redirect(url_for('users.profile'))
    
    # Exchange the code for a token
    result = exchange_code_for_token(code)
    
    if not result or not result.get('access_token'):
        flash('Failed to obtain Dropbox access token', 'error')
        return redirect(url_for('users.profile'))
    
    # Store the tokens in the user's account
    current_user.dropbox_token = result.get('access_token')
    current_user.dropbox_refresh_token = result.get('refresh_token')
    
    # Convert expires_in (seconds from now) to an actual datetime
    expires_in = result.get('expires_in', 14400)  # Default to 4 hours if not provided
    current_user.dropbox_token_expiry = datetime.now() + timedelta(seconds=expires_in)
    
    # Get account information to store the account ID
    account_info = get_dropbox_account_info(current_user.dropbox_token)
    
    if account_info and account_info.get('account_id'):
        current_user.dropbox_id = account_info.get('account_id')
        
        # Store additional info in session for display
        session['dropbox_user_info'] = {
            'name': account_info.get('name', {}).get('display_name', ''),
            'email': account_info.get('email', ''),
            'picture': account_info.get('profile_photo_url', '')
        }
        
        # If it's the first time setting up Dropbox, set a default export path
        if not current_user.dropbox_export_path:
            current_user.dropbox_export_path = '/QuizzicalBeats'
        
        db.session.commit()
        flash('Successfully connected to Dropbox!', 'success')
    else:
        flash('Connected to Dropbox, but failed to get account information', 'warning')
        db.session.commit()
    
    return redirect(url_for('users.profile'))

@users_bp.route('/dropbox/disconnect', methods=['POST'])
@login_required
def dropbox_disconnect():
    """Disconnect user's Dropbox account"""
    # Revoke token if present (optional but good practice)
    if current_user.dropbox_token:
        try:
            from musicround.helpers.dropbox_helper import revoke_token
            revoke_token(current_user.dropbox_token)
        except Exception as e:
            current_app.logger.error(f"Error revoking Dropbox token: {str(e)}")
    
    # Clear Dropbox credentials
    current_user.dropbox_token = None
    current_user.dropbox_refresh_token = None
    current_user.dropbox_token_expiry = None
    current_user.dropbox_id = None
    
    # Keep the export path in case they reconnect
    
    # Remove session info
    if 'dropbox_user_info' in session:
        session.pop('dropbox_user_info')
    
    db.session.commit()
    flash('Dropbox account disconnected successfully', 'success')
    
    return redirect(url_for('users.profile'))

@users_bp.route('/dropbox/export-path', methods=['POST'])
@login_required
def update_dropbox_export_path():
    """Update the user's Dropbox export path"""
    export_path = request.form.get('dropbox_export_path', '/QuizzicalBeats')
    
    # Simple validation
    if not export_path.startswith('/'):
        export_path = '/' + export_path
    
    # Remove any trailing slash
    if export_path.endswith('/') and len(export_path) > 1:
        export_path = export_path[:-1]
    
    # Save the path
    current_user.dropbox_export_path = export_path
    db.session.commit()
    
    flash('Dropbox export path updated successfully', 'success')
    return redirect(url_for('users.profile'))

@users_bp.route('/register', methods=['GET', 'POST'])
def register():
    """Register a new user"""
    if current_user.is_authenticated:
        return redirect(url_for('core.index'))
    
    # Check if new signups are allowed
    allow_signups = SystemSetting.get('allow_signups', 'true') == 'true'
    if not allow_signups:
        flash('New user registration is currently disabled.', 'danger')
        return redirect(url_for('users.login'))
    
    if request.method == 'POST':
        username = request.form.get('username')
        email = request.form.get('email')
        password = request.form.get('password')
        confirm_password = request.form.get('confirm_password')
        first_name = request.form.get('first_name')
        last_name = request.form.get('last_name')
        
        # Validate the form data
        if not username or not email or not password or not confirm_password:
            flash('All fields are required', 'danger')
            return render_template('users/register.html')
        
        if password != confirm_password:
            flash('Passwords do not match', 'danger')
            return render_template('users/register.html')
        
        # Check if username or email already exists
        if User.query.filter_by(username=username).first():
            flash('Username already exists', 'danger')
            return render_template('users/register.html')
        
        if User.query.filter_by(email=email).first():
            flash('Email already registered', 'danger')
            return render_template('users/register.html')
        
        # Create new user
        try:
            new_user = User(
                username=username,
                email=email,
                password_hash=generate_password_hash(password),
                first_name=first_name,
                last_name=last_name,
                created_at=datetime.now(),
                last_login=datetime.now()
            )
            db.session.add(new_user)
            db.session.commit()
            
            flash('Registration successful! You can now log in.', 'success')
            return redirect(url_for('users.login'))
        except Exception as e:
            db.session.rollback()
            current_app.logger.error(f"Error registering user: {e}")
            flash('An error occurred during registration', 'danger')
            return render_template('users/register.html')
    
    return render_template('users/register.html')

@users_bp.route('/login', methods=['GET', 'POST'])
def login():
    """Log in a user"""
    if current_user.is_authenticated:
        return redirect(url_for('core.index'))
    
    # Check which OAuth providers are configured
    oauth_providers = {
        'google': bool(current_app.config.get('GOOGLE_CLIENT_ID')),
        'authentik': bool(current_app.config.get('AUTHENTIK_CLIENT_ID'))
    }
    
    if request.method == 'POST':
        username_or_email = request.form.get('username')
        password = request.form.get('password')
        remember = True if request.form.get('remember') else False
        
        # Find user by username or email
        user = User.query.filter_by(username=username_or_email).first()
        if not user:
            user = User.query.filter_by(email=username_or_email).first()
        
        # Check if user exists and password is correct
        if not user or not check_password_hash(user.password_hash, password):
            flash('Invalid username/email or password', 'danger')
            return render_template('users/login.html', oauth_providers=oauth_providers)
        
        # Log in the user
        login_user(user, remember=remember)
        user.last_login = datetime.now()
        db.session.commit()
        
        next_page = request.args.get('next')
        if not next_page or not next_page.startswith('/'):
            next_page = url_for('core.index')
            
        flash('You have been logged in!', 'success')
        return redirect(next_page)
    
    return render_template('users/login.html', oauth_providers=oauth_providers)

@users_bp.route('/logout')
@login_required
def logout():
    """Log out a user"""
    logout_user()
    flash('You have been logged out', 'success')
    return redirect(url_for('users.login'))

@users_bp.route('/profile')
@login_required
def profile():
    """Display user profile"""
    # Check which view functions exist to avoid using current_app in template
    available_routes = {
        'rounds_list': 'rounds.rounds_list' in current_app.view_functions,
        'view_songs': 'core.view_songs' in current_app.view_functions,
        'create_round': 'rounds.create' in current_app.view_functions
    }
    
    # Check if any admin users exist
    admin_role = Role.query.filter_by(name='admin').first()
    admin_exists = False
    if admin_role:
        admin_exists = admin_role.users.count() > 0
      # Get current time for token expiry checks
    now = datetime.now()
    
    # Get info about current tokens
    system_refresh_token = SystemSetting.get('fallback_spotify_refresh_token', '')
    session_bearer = session.get('access_token', '') # This is the manually entered token or system token
    token_source = session.get('token_source', '')
    client_token_expiry = session.get('client_token_expiry', 0) # For system client_credentials token
    
    # Use centralized token management to get the best available token
    spotify_token, spotify_token_source = get_spotify_token()
    
    # Fetch user info for the active token
    spotify_user_info = None # This is passed to the template
    active_username = None
    active_user_id = None
    active_user_image = None
    active_token_expiry = None

    # If we have a valid token from centralized management, use it
    if spotify_token and spotify_token_source in ['user', 'system']:
        try:
            spotify_user_info = spotify_helper_get_user_info(spotify_token)
            if spotify_user_info:
                token_source = spotify_token_source
                session['token_source'] = spotify_token_source
                active_user_id = spotify_user_info.get('id')
                active_username = spotify_user_info.get('display_name') or active_user_id
                images = spotify_user_info.get('images', [])
                if images: 
                    active_user_image = images[0].get('url')
                
                # Set token expiry based on source
                if spotify_token_source == 'user':
                    active_token_expiry = current_user.spotify_token_expiry
                else:  # system token
                    system_token_expiry_str = SystemSetting.get('system_spotify_token_expiry', '')
                    if system_token_expiry_str:
                        try:
                            active_token_expiry = datetime.fromisoformat(system_token_expiry_str)
                        except ValueError:
                            pass
                
                current_app.logger.debug(f"Fetched Spotify user info for {active_username} using {spotify_token_source} token.")
        except Exception as e:
            current_app.logger.error(f"Error fetching Spotify user info with {spotify_token_source} token: {str(e)}")
            if spotify_token_source == 'user':
                flash("Your Spotify connection may have expired. Please try re-linking.", "warning")
      
    # Priority 2: Manually entered bearer token from session (fallback)
    if not spotify_user_info and session_bearer: # if no valid token from centralized management, check manual token
        current_app.logger.debug(f"Attempting to use session_bearer token. Source: {token_source}")
            
        # If token_source indicates it's a user-like token or generic 'manual'
        if token_source in ['manual', 'user_manual', 'user']: # 'user' if somehow set without db token
            try:
                resp = oauth.spotify.get('https://api.spotify.com/v1/me', token={'access_token': session_bearer, 'token_type': 'Bearer'})
                if resp.ok:
                    spotify_user_info = resp.json()
                    if spotify_user_info and 'id' in spotify_user_info:
                        active_user_id = spotify_user_info.get('id')
                        active_username = spotify_user_info.get('display_name') or active_user_id
                        images = spotify_user_info.get('images', [])
                        if images: active_user_image = images[0].get('url')
                        current_app.logger.debug(f"Fetched Spotify user info for {active_username} using manual bearer token.")
                        # Determine expiry for manual token (typically 1 hour from when it was added)
                        token_added_time = session.get('bearer_token_added', now.timestamp())
                        active_token_expiry = datetime.fromtimestamp(token_added_time) + timedelta(hours=1)
                        session['token_source'] = 'user_manual' # Clarify token source
                        token_source = 'user_manual'
                    else: # Token worked but no user ID, or not OK
                         current_app.logger.warning(f"Manual bearer token ({token_source}) did not return valid user info. Status: {resp.status_code}")
                         spotify_user_info = None # Ensure it's None
                else:
                    current_app.logger.error(f"Error fetching Spotify user info with manual bearer token: {resp.status_code} {resp.text}")
                    spotify_user_info = None
                    if resp.status_code in [401, 403]: flash("Manually entered Spotify token is invalid or expired.", "warning")

            except Exception as user_info_error:
                current_app.logger.error(f"Exception fetching Spotify user info with manual bearer token: {str(user_info_error)}")
                spotify_user_info = None
        
        # If token_source indicates it's client_credentials or if user fetch failed, it might be client_credentials
        if not spotify_user_info and token_source in ['client_credentials', 'client_credentials_manual']:
            try:
                resp_cc = oauth.spotify.get('https://api.spotify.com/v1/browse/new-releases', params={'limit':1}, token={'access_token': session_bearer, 'token_type': 'Bearer'})
                if resp_cc.ok:
                    current_app.logger.info("Manual/Session token confirmed as working client credentials.")
                    if token_source == 'client_credentials_manual':
                        token_added_time = session.get('bearer_token_added', now.timestamp())
                        active_token_expiry = datetime.fromtimestamp(token_added_time) + timedelta(hours=1)
                    else: # system client_credentials
                        active_token_expiry = datetime.fromtimestamp(client_token_expiry) if client_token_expiry else None
                else:
                    current_app.logger.warning(f"Manual/Session client credentials token validation failed. Status: {resp_cc.status_code}")
                    if resp_cc.status_code in [401, 403]: flash("The client credentials token in session is invalid.", "warning")

            except Exception as cc_error:
                current_app.logger.error(f"Exception validating client credentials token from session: {str(cc_error)}")

    # Determine Spotify connection status based on the findings
    spotify_status = 'none'
    if spotify_token_source == 'user':
        spotify_status = 'user'
    elif spotify_token_source == 'system':
        spotify_status = 'system'
    elif token_source == 'user_manual' and spotify_user_info: # Successfully used manual token as user
        spotify_status = 'bearer' 
    elif token_source in ['client_credentials', 'client_credentials_manual'] and session_bearer:
        spotify_status = 'client_credentials'
    
    return render_template(
        'users/profile.html', 
        available_routes=available_routes, 
        admin_exists=admin_exists,
        spotify_status=spotify_status,
        system_refresh_token=system_refresh_token,
        session_bearer=session_bearer,
        token_source=token_source,
        client_token_expiry=client_token_expiry,
        now=now,
        spotify_user_info=spotify_user_info,
        active_username=active_username,
        active_user_id=active_user_id,
        active_user_image=active_user_image,
        active_token_expiry=active_token_expiry
    )

@users_bp.route('/edit-profile', methods=['GET', 'POST'])
@login_required
def edit_profile():
    """Edit user profile"""
    if request.method == 'POST':
        username = request.form.get('username')
        email = request.form.get('email')
        first_name = request.form.get('first_name')
        last_name = request.form.get('last_name')
        dropbox_export_path = request.form.get('dropbox_export_path', '/QuizzicalBeats').strip()
        
        # Check if username or email already exists and belongs to another user
        if username != current_user.username and User.query.filter_by(username=username).first():
            flash('That username is already taken', 'danger')
            return render_template('users/edit_profile.html')
        
        if email != current_user.email and User.query.filter_by(email=email).first():
            flash('That email is already registered', 'danger')
            return render_template('users/edit_profile.html')
        
        # Update user profile
        current_user.username = username
        current_user.email = email
        current_user.first_name = first_name
        current_user.last_name = last_name
        current_user.dropbox_export_path = dropbox_export_path
        
        try:
            db.session.commit()
            flash('Your profile has been updated', 'success')
            return redirect(url_for('users.profile'))
        except Exception as e:
            db.session.rollback()
            current_app.logger.error(f"Error updating profile: {e}")
            flash('An error occurred while updating your profile', 'danger')
    
    return render_template('users/edit_profile.html')

@users_bp.route('/change-password', methods=['GET', 'POST'])
@login_required
def change_password():
    """Change user password"""
    if request.method == 'POST':
        current_password = request.form.get('current_password')
        new_password = request.form.get('new_password')
        confirm_password = request.form.get('confirm_password')
        
        # Check if current password is correct
        if not check_password_hash(current_user.password_hash, current_password):
            flash('Current password is incorrect', 'danger')
            return render_template('users/change_password.html')
        
        # Check if new passwords match
        if new_password != confirm_password:
            flash('New passwords do not match', 'danger')
            return render_template('users/change_password.html')
        
        # Update password
        current_user.password_hash = generate_password_hash(new_password)
        
        try:
            db.session.commit()
            flash('Your password has been changed', 'success')
            return redirect(url_for('users.profile'))
        except Exception as e:
            db.session.rollback()
            current_app.logger.error(f"Error changing password: {e}")
            flash('An error occurred while changing your password', 'danger')
    
    return render_template('users/change_password.html')

@users_bp.route('/forgot-password', methods=['GET', 'POST'])
def forgot_password():
    """Handle forgot password requests"""
    if current_user.is_authenticated:
        return redirect(url_for('core.index'))
    
    if request.method == 'POST':
        email = request.form.get('email')
        
        # Find user by email
        user = User.query.filter_by(email=email).first()
        
        if user:
            # Generate reset token
            token = str(uuid.uuid4())
            user.reset_token = token
            user.reset_token_expiry = datetime.now() + timedelta(hours=24)
            
            try:
                db.session.commit()
                
                # Build reset URL
                reset_url = url_for('users.reset_password', token=token, _external=True)
                
                # Email content
                subject = "Quizzical Beats Password Reset"
                body_text = f"""Hello {user.username},

You recently requested to reset your password for your Quizzical Beats account.
Please click the link below to reset your password:

{reset_url}

This link will expire in 24 hours.

If you did not request a password reset, please ignore this email or contact support if you have questions.

Best regards,
The Quizzical Beats Team
"""
                
                # Import and use the email helper
                from musicround.helpers.email_helper import send_email
                success, message = send_email(recipient=email, subject=subject, body_text=body_text)
                
                if success:
                    flash('Password reset instructions have been sent to your email address.', 'success')
                    current_app.logger.info(f"Password reset email sent to {email}")
                else:
                    # Log the failure but don't reveal to user that the email exists
                    current_app.logger.error(f"Failed to send password reset email: {message}")
                    flash('If your email is registered, you will receive a password reset link.', 'success')
            except Exception as e:
                db.session.rollback()
                current_app.logger.error(f"Error generating reset token: {e}")
                flash('An error occurred. Please try again.', 'danger')
        else:
            # Don't reveal that email doesn't exist
            # Add a small delay to prevent email enumeration
            time.sleep(1)
            flash('If your email is registered, you will receive a password reset link.', 'success')
            current_app.logger.info(f"Password reset attempted for non-existent email: {email}")
    
    return render_template('users/forgot_password.html')

@users_bp.route('/reset-password/<token>', methods=['GET', 'POST'])
def reset_password(token):
    """Reset password using token"""
    if current_user.is_authenticated:
        return redirect(url_for('core.index'))
    
    # Find user by reset token
    user = User.query.filter_by(reset_token=token).first()
    
    # Check if token is valid and not expired
    if not user or not user.reset_token_expiry or user.reset_token_expiry < datetime.now():
        flash('The password reset link is invalid or has expired', 'danger')
        return redirect(url_for('users.forgot_password'))
    
    if request.method == 'POST':
        password = request.form.get('password')
        confirm_password = request.form.get('confirm_password')
        
        if password != confirm_password:
            flash('Passwords do not match', 'danger')
            return render_template('users/reset_password.html', token=token)
        
        # Update password and clear token
        user.password_hash = generate_password_hash(password)
        user.reset_token = None
        user.reset_token_expiry = None
        
        try:
            db.session.commit()
            
            # Send confirmation email
            subject = "Your Quizzical Beats Password Has Been Reset"
            body_text = f"""Hello {user.username},

Your password has been successfully reset. You can now log in with your new password.

If you did not reset your password, please contact support immediately.

Best regards,
The Quizzical Beats Team
"""
            
            # Import and use email helper
            from musicround.helpers.email_helper import send_email
            send_email(recipient=user.email, subject=subject, body_text=body_text)
            
            flash('Your password has been reset. You can now log in.', 'success')
            current_app.logger.info(f"Password reset successful for user: {user.username}")
            return redirect(url_for('users.login'))
        except Exception as e:
            db.session.rollback()
            current_app.logger.error(f"Error resetting password: {e}")
            flash('An error occurred while resetting your password', 'danger')
    
    return render_template('users/reset_password.html', token=token)

@users_bp.route('/spotify-link', methods=['GET', 'POST'])
@login_required
def spotify_link():
    """
    GET: Show management UI (manage_spotify.html) with current status and options.    POST: Trigger Spotify OAuth flow for linking/re-linking.
    """
    if request.method == 'POST':
        # Only POST triggers the OAuth flow
        if not current_app.config.get('SPOTIFY_CLIENT_ID'):
            flash('Spotify integration is not configured.', 'danger')
            return redirect(url_for('users.spotify_link')) # Redirect to spotify_link page
        redirect_uri = get_oauth_redirect_uri('users.spotify_link_callback')
        return oauth.spotify.authorize_redirect(redirect_uri, show_dialog='true')

    # GET: Show management UI
    spotify_user_details = None 
    if current_user.spotify_id:
        spotify_user_details = {
            "id": current_user.spotify_id,
            "display_name": session.get('spotify_display_name', current_user.spotify_id) 
        }
    
    now = datetime.now()
    spotify_user_info = session.get('spotify_user_info')
    return render_template('users/manage_spotify.html', 
                           spotify_user_details=spotify_user_details, 
                           now=now, 
                           spotify_user_info=spotify_user_info)

@users_bp.route('/spotify-link/callback')
@login_required
def spotify_link_callback():
    """Callback for linking Spotify to an existing user account."""
    try:
        token = oauth.spotify.authorize_access_token()
        user_info = get_spotify_user_info(token)
        
        if not user_info or 'id' not in user_info:
            flash('Failed to get user information from Spotify.', 'danger')
            current_app.logger.error(f"Spotify link callback: Missing ID in user_info for user {current_user.id}. Info: {user_info}")
            return redirect(url_for('users.spotify_link')) # Redirect to spotify_link page

        _process_spotify_link(current_user, token, user_info)

    except Exception as e:
        current_app.logger.error(f"Error in Spotify link callback for user {current_user.id}: {str(e)}")
        flash('An error occurred while linking your Spotify account. Please try again.', 'danger')
    
    return redirect(url_for('users.spotify_link')) # Redirect to spotify_link page

@users_bp.route('/spotify/disconnect', methods=['POST'])
@login_required
def spotify_disconnect():
    """Disconnect user's Spotify account."""
    _process_spotify_disconnect(current_user)
    return redirect(url_for('users.spotify_link')) # Redirect to spotify_link page

@users_bp.route('/update-bearer-token', methods=['POST'])
@login_required
def update_bearer_token():
    """Update the Spotify bearer token in the session"""
    # Check if clearing token was requested
    if request.form.get('clear_token'):
        session.pop('access_token', None)
        session.pop('token_source', None)
        session.pop('bearer_token_added', None)
        current_app.logger.info(f"User {current_user.id} cleared Spotify bearer token from session")
        flash('Spotify bearer token has been cleared', 'success')
        return redirect(url_for('users.profile'))
    
    # Get bearer token from form
    bearer_token = request.form.get('bearer_token', '').strip()
    if not bearer_token:
        flash('No bearer token provided', 'warning')
        return redirect(url_for('users.profile'))
    
    try:
        # Store the token in session with timestamp and mark as manual initially
        session['access_token'] = bearer_token
        session['token_source'] = 'manual' # Initial assumption
        session['bearer_token_added'] = datetime.now().timestamp()
        
        current_app.logger.info(f"User {current_user.id} added a manual bearer token to session. Validating: {bearer_token[:10]}...")
        
        try:
            resp_me = oauth.spotify.get('https://api.spotify.com/v1/me', token={'access_token': bearer_token, 'token_type': 'Bearer'})
            user_info = resp_me.json() if resp_me.ok else None

            if user_info and 'id' in user_info:
                # This is a user OAuth token
                username = user_info.get('display_name') or user_info.get('id')
                
                # Update token source to reflect it's a user token
                session['token_source'] = 'user_manual'
                current_app.logger.info(f"Token identified as user OAuth token for: {username} (ID: {user_info.get('id')})")
                flash(f'Successfully authenticated with Spotify as {username}', 'success')
                current_app.logger.info(f"Manual bearer token added for Spotify user: {username} (ID: {user_info.get('id')})")
            else:
                current_app.logger.warning(f"Manual token is not a valid user token. Response: {resp_me.status_code if not resp_me.ok else 'OK, but no ID or user_info was None'}")
                raise Exception("Not a user token or failed to fetch user info")

        except Exception as user_error:
            current_app.logger.warning(f"Validating as user token failed: {str(user_error)}. Checking if client credentials token...")
            try:
                resp_browse = oauth.spotify.get('https://api.spotify.com/v1/browse/new-releases', params={'limit':1}, token={'access_token': bearer_token, 'token_type': 'Bearer'})
                browse_results = resp_browse.json() if resp_browse.ok else None

                if browse_results and 'albums' in browse_results:
                    # This looks like a client credentials token
                    session['token_source'] = 'client_credentials_manual'
                    current_app.logger.info("Token identified as client credentials token (manual)")
                    flash('Token saved as client credentials token. This type of token cannot access user-specific data.', 'warning')
                else:
                    flash('Token saved but validation failed (cannot fetch new releases). The token may be invalid or expired.', 'warning')
                    current_app.logger.warning(f"Manual token validation as client_credentials failed. Response: {resp_browse.status_code if not resp_browse.ok else 'OK, but no albums or browse_results was None'}")
            except Exception as e_browse:
                current_app.logger.error(f"Error validating bearer token as client credentials: {e_browse}")
                flash(f'Token saved but error during client credentials validation: {str(e_browse)}', 'warning')
    except Exception as e_main:
        current_app.logger.error(f"Error processing bearer token: {e_main}")
        flash(f'Error processing token: {str(e_main)}', 'warning')
    
    return redirect(url_for('users.profile'))

@users_bp.route('/use-refresh-token', methods=['POST'])
@login_required
def use_refresh_token():
    # This will be reviewed and updated for authlib
    pass

@users_bp.route('/setup')
@login_required
def setup():
    """One-time setup route to promote the current user to admin"""
    if current_user.is_admin:
        flash('You are already an administrator.', 'info')
        return redirect(url_for('users.profile'))
    
    try:
        # Check if the admin role exists
        admin_role = Role.query.filter_by(name='admin').first()
        
        # Check if any admin users already exist
        admin_exists = False
        if admin_role:
            admin_exists = admin_role.users.count() > 0
            
        if admin_exists:
            flash('An administrator already exists in the system. Only the first user can be promoted to admin.', 'warning')
            return redirect(url_for('users.profile'))
        
        # Create the admin role if it doesn't exist
        if not admin_role:
            admin_role = Role(name='admin', description='Administrator role with full system access')
            db.session.add(admin_role)
            db.session.commit()
            current_app.logger.info(f"Created admin role with ID {admin_role.id}")
            
        # Assign the admin role to the current user
        if admin_role not in current_user.roles:
            current_user.roles.append(admin_role)
            db.session.commit()
            current_app.logger.info(f"User {current_user.username} promoted to admin")
            flash('You have been promoted to administrator!', 'success')
        else:
            flash('You already have the admin role, but it may not be working correctly.', 'warning')
        
        return redirect(url_for('users.profile'))
        
    except Exception as e:
        db.session.rollback()
        current_app.logger.error(f"Error promoting user to admin: {str(e)}")
        flash(f'Error setting up admin privileges: {str(e)}', 'danger')
        return redirect(url_for('users.profile'))

@users_bp.route('/audio-settings', methods=['GET', 'POST'])
@login_required
def audio_settings():
    """Manage custom audio settings including intro, outro, and replay MP3s"""
    from musicround.helpers.utils import save_user_mp3, generate_tts_mp3, get_available_voices
    
    if request.method == 'POST':
        action = request.form.get('action')
        mp3_type = request.form.get('mp3_type')
        
        if not mp3_type or mp3_type not in ['intro', 'outro', 'replay']:
            flash('Invalid audio type specified', 'danger')
            return redirect(url_for('users.audio_settings'))
        
        if action == 'upload':
            # Handle file upload
            if 'audio_file' not in request.files:
                flash('No file provided', 'danger')
                return redirect(request.url)
                
            file = request.files['audio_file']
            if file.filename == '':
                flash('No file selected', 'danger')
                return redirect(request.url)
                
            # Save the user's uploaded MP3
            file_path = save_user_mp3(file, current_user.username, mp3_type)
            if file_path:
                # Update user's MP3 setting
                setattr(current_user, f'{mp3_type}_mp3', file_path)
                db.session.commit()
                flash(f'Your {mp3_type} audio has been updated', 'success')
            else:
                flash('Invalid file format. Please upload an MP3 file.', 'danger')
                
        elif action == 'generate':
            # Handle text-to-speech generation
            text = request.form.get('tts_text')
            service = request.form.get('tts_service', 'polly')
            voice = request.form.get('tts_voice')
            
            # Process advanced options for each service
            model = None
            stability = None
            similarity = None
            
            if service == 'openai':
                model = request.form.get('openai_model', 'tts-1')
            elif service == 'elevenlabs':
                model = request.form.get('elevenlabs_model', 'eleven_monolingual_v1')
                # Convert string values to float
                try:
                    stability = float(request.form.get('stability', 0.5))
                    similarity = float(request.form.get('similarity_boost', 0.75))
                except (ValueError, TypeError):
                    stability = 0.5
                    similarity = 0.75
            
            if not text:
                flash('No text provided for speech generation', 'danger')
                return redirect(request.url)
                
            # Generate MP3 with the selected service and options
            file_path = generate_tts_mp3(
                text=text, 
                username=current_user.username, 
                mp3_type=mp3_type, 
                service=service,
                voice=voice,
                model=model,
                stability=stability,
                similarity=similarity
            )
            
            if file_path:
                # Update user's MP3 setting
                setattr(current_user, f'{mp3_type}_mp3', file_path)
                db.session.commit()
                flash(f'Your {mp3_type} audio has been generated', 'success')
            else:
                flash('Error generating audio. Please check your settings and try again.', 'danger')
                
        elif action == 'reset':
            # Reset to default MP3
            setattr(current_user, f'{mp3_type}_mp3', None)
            db.session.commit()
            flash(f'Your {mp3_type} audio has been reset to default', 'success')
            
        return redirect(url_for('users.audio_settings'))
    
    # Define default text templates for each type
    default_texts = {
        'intro': 'Welcome to the music quiz! Get ready to test your knowledge of songs and artists.',
        'outro': 'That concludes our music round. How many songs did you recognize?',
        'replay': 'Now we will play all the songs again. Listen carefully!'
    }
    
    # Check which TTS services are available
    tts_services = []
    
    # Check for AWS credentials
    has_aws = all([
        current_app.config.get('AWS_ACCESS_KEY_ID'),
        current_app.config.get('AWS_SECRET_ACCESS_KEY')
    ])
    if has_aws:
        tts_services.append({
            'id': 'polly',
            'name': 'Amazon Polly',
            'description': 'High-quality natural-sounding voices',
            'voices': get_available_voices('polly')
        })
    
    # Check for OpenAI credentials
    has_openai = bool(current_app.config.get('OPENAI_API_KEY'))
    if has_openai:
        tts_services.append({
            'id': 'openai',
            'name': 'OpenAI TTS',
            'description': 'Realistic speech synthesis from OpenAI',
            'voices': get_available_voices('openai'),
            'models': [
                {'id': 'tts-1', 'name': 'TTS-1 (Standard)', 'description': 'Standard quality voice'},
                {'id': 'tts-1-hd', 'name': 'TTS-1-HD (High Definition)', 'description': 'Higher quality voice'}
            ]
        })
    
    # Check for ElevenLabs credentials
    has_elevenlabs = bool(current_app.config.get('ELEVENLABS_API_KEY'))
    if has_elevenlabs:
        tts_services.append({
            'id': 'elevenlabs',
            'name': 'ElevenLabs',
            'description': 'Ultra-realistic AI voices with emotion',
            'voices': get_available_voices('elevenlabs'),
            'models': [
                {'id': 'eleven_monolingual_v1', 'name': 'Monolingual V1', 'description': 'English only, faster generation'},
                {'id': 'eleven_multilingual_v2', 'name': 'Multilingual V2', 'description': 'Supports multiple languages'}
            ],
            'settings': True  # Flag to indicate this service has additional settings
        })
    
    # Handle TTS service selection for the form
    selected_service = request.args.get('tts_service')
    mp3_type = request.args.get('mp3_type') or 'intro'

    # Find the selected service dict
    selected_service_dict = None
    for svc in tts_services:
        if svc['id'] == selected_service:
            selected_service_dict = svc
            break
    if not selected_service_dict and tts_services:
        selected_service_dict = tts_services[0]

    return render_template(
        'users/audio_settings.html',
        default_texts=default_texts,
        tts_services=tts_services,
        has_tts_services=bool(tts_services),
        selected_service=selected_service_dict,
        mp3_type=mp3_type
    )

@users_bp.route('/system-settings', methods=['GET', 'POST'])
@login_required
@admin_required
def system_settings():
    """Admin view to edit global/system settings."""
    from musicround.helpers.utils import get_available_voices
    
    # Define editable system settings keys and their labels
    editable_settings = [
        ('default_tts_service', 'Default TTS Service'),
        ('default_tts_voice', 'Default TTS Voice'),
        ('default_tts_model', 'Default TTS Model'),
        ('fallback_spotify_refresh_token', 'Fallback Spotify Refresh Token'),
        ('spotify_region', 'Default Spotify Region'),
        ('max_songs_per_round', 'Maximum Songs Per Round'),
        ('enable_public_rounds', 'Enable Public Rounds'),
        ('allow_signups', 'Allow New User Registrations'),
        # Add more keys/labels as needed
    ]
    
    # Define which settings are checkboxes (boolean values)
    checkbox_settings = ['enable_public_rounds', 'allow_signups']

    if request.method == 'POST':
        # Process all settings from the form
        for key, _ in editable_settings:
            # Handle checkboxes differently - they're only in the form if checked
            if key in checkbox_settings:
                # Set 'true' if checkbox is in form data, otherwise 'false'
                value = 'true' if key in request.form else 'false'
                current_app.logger.debug(f"Setting {key} to {value}")
                SystemSetting.set(key, value)
            else:
                # Normal text/select fields
                value = request.form.get(key, '')
                current_app.logger.debug(f"Setting {key} to {value}")
                SystemSetting.set(key, value)
                
        # Add a database commit to ensure changes are saved
        db.session.commit()
        
        flash('System settings updated.', 'success')
        return redirect(url_for('users.system_settings'))

    # Get all current settings
    settings = SystemSetting.all_settings()
    
    # Check which TTS services are available
    tts_services = []
    
    # Check for AWS credentials
    has_aws = all([
        current_app.config.get('AWS_ACCESS_KEY_ID'),
        current_app.config.get('AWS_SECRET_ACCESS_KEY')
    ])
    if has_aws:
        tts_services.append({
            'id': 'polly',
            'name': 'Amazon Polly',
            'description': 'High-quality natural-sounding voices',
            'voices': get_available_voices('polly')
        })
    
    # Check for OpenAI credentials
    has_openai = bool(current_app.config.get('OPENAI_API_KEY'))
    if has_openai:
        tts_services.append({
            'id': 'openai',
            'name': 'OpenAI TTS',
            'description': 'Realistic speech synthesis from OpenAI',
            'voices': get_available_voices('openai'),
            'models': [
                {'id': 'tts-1', 'name': 'TTS-1 (Standard)', 'description': 'Standard quality voice'},
                {'id': 'tts-1-hd', 'name': 'TTS-1-HD (High Definition)', 'description': 'Higher quality voice'}
            ]
        })
    
    # Check for ElevenLabs credentials
    has_elevenlabs = bool(current_app.config.get('ELEVENLABS_API_KEY'))
    if has_elevenlabs:
        tts_services.append({
            'id': 'elevenlabs',
            'name': 'ElevenLabs',
            'description': 'Ultra-realistic AI voices with emotion',
            'voices': get_available_voices('elevenlabs'),
            'models': [
                {'id': 'eleven_monolingual_v1', 'name': 'Monolingual V1', 'description': 'English only, faster generation'},
                {'id': 'eleven_multilingual_v2', 'name': 'Multilingual V2', 'description': 'Supports multiple languages'}
            ]
        })
    
    # List of available regions for Spotify
    spotify_regions = [
        {'code': 'US', 'name': 'United States'},
        {'code': 'GB', 'name': 'United Kingdom'},
        {'code': 'DE', 'name': 'Germany'},
        {'code': 'FR', 'name': 'France'},
        {'code': 'ES', 'name': 'Spain'},
        {'code': 'IT', 'name': 'Italy'},
        {'code': 'JP', 'name': 'Japan'},
        {'code': 'AU', 'name': 'Australia'},
        {'code': 'BR', 'name': 'Brazil'},
        {'code': 'CA', 'name': 'Canada'},
    ]
    
    return render_template(
        'admin/system_settings.html',
        settings=settings,
        editable_settings=editable_settings,
        tts_services=tts_services,
        spotify_regions=spotify_regions
    )

@users_bp.route('/backup-manager')
@login_required
@admin_required
def backup_manager():
    """Backup Manager interface for administrators"""
    from musicround.helpers.backup_helper import list_backups, get_backup_summary, generate_backup_config_suggestion
    
    # Get list of existing backups
    backups = list_backups()
    
    # Get backup system status and summary
    backup_summary = get_backup_summary()
    
    # Extract key information from summary
    backup_count = backup_summary.get('backup_count', 0)
    latest_backup = backup_summary.get('latest_backup')
    schedule_enabled = backup_summary.get('schedule_enabled', False)
    schedule_time = backup_summary.get('schedule_time')
    schedule_frequency = backup_summary.get('schedule_frequency', 'daily')
    next_backup = backup_summary.get('next_backup')
    backup_location = backup_summary.get('backup_location')
    retention_days = backup_summary.get('retention_days', 30)
    
    # Generate configuration suggestion instead of Docker Compose labels
    config_suggestion = generate_backup_config_suggestion(retention_days=retention_days)
    
    # Check if we should show the schedule or create forms
    show_schedule_form = request.args.get('show_schedule') == 'true'
    show_create_form = request.args.get('show_create') == 'true'
    
    # Check for notifications from other backup operations
    notification = None
    if 'backup_notification' in session:
        notification = session.pop('backup_notification')
    
    return render_template(
        'admin/backup_manager.html',
        backups=backups,
        backup_count=backup_count,
        latest_backup=latest_backup,
        schedule_enabled=schedule_enabled,
        schedule_time=schedule_time,
        schedule_frequency=schedule_frequency,
        next_backup=next_backup,
        backup_location=backup_location,
        retention_days=retention_days,
        show_schedule_form=show_schedule_form,
        show_create_form=show_create_form,
        notification=notification,
        config_suggestion=config_suggestion
    )

@users_bp.route('/create-backup', methods=['POST'])
@login_required
@admin_required
def create_backup():
    """Create a new backup"""
    from musicround.helpers.backup_helper import create_backup as create_backup_helper
    
    # Check for automation token for scheduled backups
    automation_token = request.headers.get('X-Automation-Token') or request.args.get('token')
    if automation_token == current_app.config.get('AUTOMATION_TOKEN'):
        # Allow the request without authentication for automation
        pass
    elif not current_user.is_authenticated or not current_user.is_admin:
        return jsonify({"status": "error", "message": "Unauthorized"}), 401
    
    # Get custom backup name if provided
    backup_name = request.form.get('backup_name', '').strip()
    
    # Get options for what to include
    include_mp3s = request.form.get('include_mp3s', 'true') == 'true'
    include_config = request.form.get('include_config', 'true') == 'true'
    
    # Create the backup
    result = create_backup_helper(
        backup_name=backup_name if backup_name else None,
        include_mp3s=include_mp3s,
        include_config=include_config
    )
    
    # If this is an API request, return JSON
    if request.headers.get('Accept') == 'application/json' or automation_token:
        return jsonify(result)
    
    # Otherwise, store the result for display in the UI and redirect
    session['backup_notification'] = {
        'status': result.get('status'),
        'message': result.get('message')
    }
    
    return redirect(url_for('users.backup_manager'))

@users_bp.route('/schedule-backup', methods=['POST'])
@login_required
@admin_required
def schedule_backup():
    """Schedule automatic backups"""
    from musicround.helpers.backup_helper import schedule_backup as schedule_backup_helper
    
    # Get schedule settings from form
    schedule_time = request.form.get('schedule_time', '03:00')
    frequency = request.form.get('frequency', 'daily')
    enabled = request.form.get('enabled') == 'true'
    
    # Get retention days from form
    retention_days = request.form.get('retention_days', '30')
    try:
        retention_days = int(retention_days)
    except ValueError:
        retention_days = 30  # Default to 30 days if invalid input
    
    # Save schedule settings
    result = schedule_backup_helper(schedule_time=schedule_time, frequency=frequency, retention_days=retention_days)
    
    # Update enabled status
    from musicround.models import SystemSetting
    SystemSetting.set('backup_schedule_enabled', 'true' if enabled else 'false')
    
    # Store the result for display
    session['backup_notification'] = {
        'status': result.get('status'),
        'message': result.get('message')
    }
    
    return redirect(url_for('users.backup_manager'))

@users_bp.route('/verify-backup/<filename>', methods=['GET', 'POST'])
@login_required
@admin_required
def verify_backup(filename):
    """Verify the integrity of a backup file"""
    from musicround.helpers.backup_helper import verify_backup as verify_backup_helper
    
    # Verify the backup
    result = verify_backup_helper(filename)
    
    # Store the result for display
    session['backup_notification'] = {
        'status': result.get('status'),
        'message': result.get('message')
    }
    
    return redirect(url_for('users.backup_manager'))

@users_bp.route('/restore-backup/<filename>', methods=['POST'])
@login_required
@admin_required
def restore_backup(filename):
    """Restore from a backup file"""
    from musicround.helpers.backup_helper import restore_backup as restore_backup_helper
    
    # Restore the backup
    result = restore_backup_helper(filename)
    
    # Store the result for display
    session['backup_notification'] = {
        'status': result.get('status'),
        'message': result.get('message')
    }
    
    return redirect(url_for('users.backup_manager'))

@users_bp.route('/delete-backup/<filename>', methods=['POST'])
@login_required
@admin_required
def delete_backup(filename):
    """Delete a backup file"""
    from musicround.helpers.backup_helper import delete_backup as delete_backup_helper
    
    # Delete the backup
    result = delete_backup_helper(filename)
    
    # Store the result for display
    session['backup_notification'] = {
        'status': result.get('status'),
        'message': result.get('message')
    }
    
    return redirect(url_for('users.backup_manager'))

@users_bp.route('/download-backup/<filename>', methods=['GET', 'POST'])
@login_required
@admin_required
def download_backup(filename):
    """Download a backup file"""
    import os
    from flask import send_file, abort
    
    # Security check: only allow .zip files
    if not filename.endswith('.zip'):
        abort(400, "Invalid file type")
    
    # Only allow downloading from the backup directory
    backup_dir = os.path.join('/data', 'backups')
    backup_path = os.path.join(backup_dir, filename)
    
    # Check if the file exists
    if not os.path.exists(backup_path):
        abort(404, "Backup file not found")
    
    # Send the file
    return send_file(
        backup_path,
        mimetype='application/zip',
        as_attachment=True,
        download_name=filename
    )

@users_bp.route('/upload-backup', methods=['POST'])
@login_required
@admin_required
def upload_backup():
    """Upload a backup file to the system"""
    from musicround.helpers.backup_helper import upload_backup as upload_backup_helper
    
    # Check if a file was uploaded
    if 'backup_file' not in request.files:
        flash('No file selected', 'danger')
        return redirect(url_for('users.backup_manager'))
    
    file = request.files['backup_file']
    
    # Check if the file has a name
    if file.filename == '':
        flash('No file selected', 'danger')
        return redirect(url_for('users.backup_manager'))
    
    # Upload the file
    result = upload_backup_helper(file)
    
    # Store the result for display
    session['backup_notification'] = {
        'status': result.get('status'),
        'message': result.get('message')
    }
    
    return redirect(url_for('users.backup_manager'))

@users_bp.route('/apply-retention-policy', methods=['POST'])
@login_required
@admin_required
def apply_retention_policy():
    """Apply retention policy to backups"""
    from musicround.helpers.backup_helper import apply_retention_policy as apply_retention_policy_helper
    from musicround.models import SystemSetting
    
    # Get retention days from form
    retention_days = request.form.get('retention_days', '30')
    try:
        retention_days = int(retention_days)
    except ValueError:
        retention_days = 30  # Default to 30 days if invalid input
    
    # Save the retention policy setting
    SystemSetting.set('backup_retention_days', str(retention_days))
    
    # Apply the retention policy
    result = apply_retention_policy_helper(retention_days)
    
    # Store the result for display
    session['backup_notification'] = {
        'status': result.get('status'),
        'message': result.get('message'),
        'details': {
            'deleted_count': result.get('deleted_count', 0),
            'deleted_backups': result.get('deleted_backups', [])
        }
    }
    
    return redirect(url_for('users.backup_manager'))

@users_bp.route('/update-scheduler', methods=['POST'])
@login_required
@admin_required
def update_scheduler():
    """Update the Ofelia scheduler configuration based on current system settings"""
    from musicround.helpers.backup_helper import update_ofelia_config
    
    # Get the current retention days setting
    from musicround.models import SystemSetting
    retention_days = int(SystemSetting.get('backup_retention_days', '30'))
    
    # Update the scheduler configuration
    result = update_ofelia_config(retention_days=retention_days)
    
    # Store result for display in the UI
    session['backup_notification'] = {
        'status': result.get('status'),
        'message': result.get('message'),
        'details': {
            'schedule': result.get('schedule'),
            'config_content': result.get('config_content'),
            'instructions': result.get('instructions')
        }
    }
    
    return redirect(url_for('users.backup_manager'))

@users_bp.route('/system-health')
@login_required
@admin_required
def system_health():
    """Display system health status"""
    import os
    import platform
    import sys
    import flask
    import sqlite3
    from datetime import datetime
    from musicround.models import Song, Round, User, db
    from musicround.version import VERSION_INFO
    
    # Check database status
    database_status = {"color": "green", "message": "Database is operational and accessible."}
    database_stats = {}
    
    try:
        # Count database records
        database_stats["song_count"] = Song.query.count()
        database_stats["round_count"] = Round.query.count()
        database_stats["user_count"] = User.query.count()
        
        # Get database file size
        db_path = current_app.config['SQLALCHEMY_DATABASE_URI'].replace('sqlite:///', '')
        if os.path.exists(db_path):
            size_bytes = os.path.getsize(db_path)
            size_mb = size_bytes / (1024 * 1024)
            database_stats["file_size"] = f"{size_mb:.2f} MB"
        else:
            database_stats["file_size"] = "Unknown"
            database_status = {"color": "yellow", "message": "Database file not found at expected location."}
    except Exception as e:
        current_app.logger.error(f"Error checking database status: {str(e)}")
        database_status = {"color": "red", "message": f"Database error: {str(e)}"}
    
    # Check storage status
    storage_status = {"color": "green", "message": "All storage locations are accessible and writable."}
    storage_stats = []
    
    # Check important directories
    dirs_to_check = [
        {"path": '/data', "name": "Data Directory"},
        {"path": '/data/backups', "name": "Backups Directory"},
        {"path": os.path.join(os.path.dirname(current_app.root_path), 'mp3'), "name": "MP3 Directory"},
        {"path": os.path.join(current_app.root_path, 'static'), "name": "Static Files"}
    ]
    
    for dir_info in dirs_to_check:
        dir_path = dir_info["path"]
        dir_name = dir_info["name"]
        dir_stat = {"name": dir_name, "path": dir_path}
        
        # Create directory if it doesn't exist (for backups)
        if dir_name == "Backups Directory" and not os.path.exists(dir_path):
            try:
                os.makedirs(dir_path, exist_ok=True)
            except Exception:
                pass
        
        if os.path.exists(dir_path):
            # Check if directory is writable
            dir_stat["writable"] = os.access(dir_path, os.W_OK)
            
            # Count files and calculate size
            try:
                files = [f for f in os.listdir(dir_path) if os.path.isfile(os.path.join(dir_path, f))]
                dir_stat["file_count"] = len(files)
                
                total_size = sum(os.path.getsize(os.path.join(dir_path, f)) for f in files)
                size_mb = total_size / (1024 * 1024)
                dir_stat["size"] = f"{size_mb:.2f} MB"
            except Exception:
                dir_stat["file_count"] = "Error"
                dir_stat["size"] = "Error"
        else:
            dir_stat["writable"] = False
            dir_stat["file_count"] = 0
            dir_stat["size"] = "0.00 MB"
            
            # Update overall status
            if storage_status["color"] != "red":
                storage_status = {"color": "yellow", "message": f"Directory not found: {dir_name}"}
        
        storage_stats.append(dir_stat)
    
    # Check API services status
    api_status = {"color": "green", "message": "All API services are available."}
    service_stats = []
    
    # Check Spotify API
    spotify_service = {
        "name": "Spotify API",
        "status": "ok",
        "message": "Connected and operational"
    }
    
    try:
        spotify_credentials = all([
            current_app.config.get('SPOTIFY_CLIENT_ID'),
            current_app.config.get('SPOTIFY_CLIENT_SECRET')
        ])
        
        if not spotify_credentials:
            spotify_service["status"] = "error"
            spotify_service["message"] = "Missing API credentials"
            if api_status["color"] == "green":
                api_status = {"color": "yellow", "message": "Some API services are unavailable."}
    except Exception as e:
        spotify_service["status"] = "error"
        spotify_service["message"] = f"Error: {str(e)}"
        api_status = {"color": "yellow", "message": "Some API services have errors."}
    
    service_stats.append(spotify_service)
    
    # Check Deezer API
    deezer_service = {
        "name": "Deezer API",
        "status": "ok",
        "message": "Connected and operational"
    }
    
    try:
        deezer_client = current_app.config.get('deezer')
        if not deezer_client:
            deezer_service["status"] = "warning"
            deezer_service["message"] = "Deezer client not initialized"
            if api_status["color"] == "green":
                api_status = {"color": "yellow", "message": "Some API services have warnings."}
    except Exception as e:
        deezer_service["status"] = "error"
        deezer_service["message"] = f"Error: {str(e)}"
        api_status = {"color": "yellow", "message": "Some API services have errors."}
    
    service_stats.append(deezer_service)
    
    # Check memory usage
    memory_status = {"color": "gray", "message": "Memory usage information not available."}
    
    try:
        import psutil
        process = psutil.Process(os.getpid())
        memory_info = process.memory_info()
        memory_mb = memory_info.rss / (1024 * 1024)
        
        if memory_mb > 500:  # More than 500MB
            memory_status = {"color": "yellow", "message": f"High memory usage: {memory_mb:.2f} MB"}
        else:
            memory_status = {"color": "green", "message": f"Memory usage: {memory_mb:.2f} MB"}
    except ImportError:
        # psutil module not installed
        memory_status = {"color": "gray", "message": "Memory usage information not available (psutil not installed)."}
    except Exception as e:
        # Other errors
        memory_status = {"color": "gray", "message": f"Error checking memory: {str(e)}"}
        current_app.logger.error(f"Error checking memory: {str(e)}")
    
    # System information
    system_info = {
        "python_version": f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}",
        "platform": platform.platform(),
        "flask_version": flask.__version__
    }
    
    # Get last backup timestamp
    from musicround.helpers.backup_helper import list_backups
    backups = list_backups()
    if backups:
        latest_backup = backups[0]
        timestamp = latest_backup.get('timestamp')
        if timestamp:
            try:
                backup_time = datetime.fromisoformat(timestamp)
                database_stats["last_backup"] = backup_time.strftime("%Y-%m-%d %H:%M:%S")
            except (ValueError, TypeError):
                database_stats["last_backup"] = "Unknown format"
        else:
            database_stats["last_backup"] = "Unknown"
    else:
        database_stats["last_backup"] = "Never"
    
    return render_template(
        'admin/system_health.html',
        database_status=database_status,
        storage_status=storage_status,
        api_status=api_status,
        memory_status=memory_status,
        database_stats=database_stats,
        storage_stats=storage_stats,
        service_stats=service_stats,
        system_info=system_info,
        version_info=VERSION_INFO
    )