"""
Flask routes for web dashboard
"""

import logging
import time
import json
import secrets
from datetime import datetime, date, timezone
from flask import Blueprint, render_template, request, jsonify, redirect, url_for, flash, session, g
from flask_login import login_required, current_user, login_user, logout_user
from flask_socketio import emit, join_room, leave_room
from werkzeug.security import check_password_hash
from werkzeug.utils import secure_filename
from sqlalchemy import text

from .auth import AuthenticatedUser
from ..core.message_bus import Message, MessageType
from ..utils.timezone_utils import get_venue_timezone, utc_to_venue_datetime, venue_to_utc_datetime


def conditional_auth_decorator(condition, auth_decorator, fallback_decorator=login_required):
    """Helper function for conditional auth decorators"""
    def decorator(func):
        if condition:
            return auth_decorator(func)
        else:
            return fallback_decorator(func)
    return decorator


def is_bet_detail_winning(detail, match, session):
    """
    Determine if a bet detail is winning based on direct result, extraction associations,
    winning outcomes array, and under/over results.

    Args:
        detail: BetDetailModel instance
        match: MatchModel instance
        session: Database session

    Returns:
        bool: True if the bet detail is winning
    """
    # Direct win check - this is the authoritative source set by _update_bet_results
    if detail.result in ['won', 'win']:
        return True

    # If result is not 'win', check other conditions only for non-UNDER/OVER bets
    # For UNDER/OVER bets, we rely solely on the direct result check above
    if match:
        # Check if this is an UNDER/OVER outcome
        is_under_over = detail.outcome and ('UNDER' in detail.outcome.upper() or 'OVER' in detail.outcome.upper())
        
        # For UNDER/OVER bets, we've already checked the direct result above
        # No need to check winning_outcomes or under_over_result as they could cause duplicate detection
        if is_under_over:
            return False
        
        # For non-UNDER/OVER bets, check other conditions
        # Check winning_outcomes array (contains all winning outcomes from extraction)
        if match.winning_outcomes:
            try:
                if isinstance(match.winning_outcomes, str):
                    winning_outcomes = json.loads(match.winning_outcomes)
                elif isinstance(match.winning_outcomes, list):
                    winning_outcomes = match.winning_outcomes
                else:
                    winning_outcomes = []

                if detail.outcome in winning_outcomes:
                    return True
            except (json.JSONDecodeError, TypeError):
                pass

        # Check extraction associations if match has extraction result
        if match.result:
            from ..database.models import ExtractionAssociationModel
            # Check if this outcome is associated with the extraction result
            association = session.query(ExtractionAssociationModel).filter_by(
                outcome_name=detail.outcome,
                extraction_result=match.result
            ).first()

            if association:
                return True

    return False


def is_settled_bet_detail_winning(detail, match, session):
    """Prefer settled row status for completed matches, fallback to inferred logic otherwise."""
    if detail.result == 'win':
        return True
    if detail.result in ['lost', 'cancelled']:
        return False
    if match and getattr(match, 'status', None) == 'done':
        return False
    return is_bet_detail_winning(detail, match, session)


def get_api_auth_decorator(require_admin=False):
    """Get API auth decorator that works with lazy initialization"""
    def decorator(func):
        from functools import wraps
        @wraps(func)
        def decorated_function(*args, **kwargs):
            from flask import request
            from flask_login import current_user

            # Get auth_manager from blueprint context (set during app initialization)
            auth_manager = getattr(api_bp, 'auth_manager', None)
            if auth_manager:
                # Check for Bearer token authentication first
                auth_header = request.headers.get('Authorization')
                if auth_header and auth_header.startswith('Bearer '):
                    # Use the auth manager's require_auth method for Bearer tokens
                    if require_admin:
                        # Check if user is admin after authentication
                        @auth_manager.require_auth
                        def admin_check(*args, **kwargs):
                            if not hasattr(request, 'current_user'):
                                return jsonify({'error': 'Authentication required'}), 401

                            user_role = request.current_user.get('role', 'normal')
                            is_admin = request.current_user.get('is_admin', False)

                            if user_role != 'admin' and not is_admin:
                                return jsonify({'error': 'Admin access required'}), 403

                            return func(*args, **kwargs)
                        return admin_check(*args, **kwargs)
                    else:
                        return auth_manager.require_auth(func)(*args, **kwargs)
                else:
                    # No Bearer token - check for web session authentication
                    if current_user.is_authenticated:
                        # Set current_user in request for consistency
                        request.current_user = {
                            'user_id': current_user.id,
                            'username': current_user.username,
                            'is_admin': current_user.is_admin,
                            'role': getattr(current_user, 'role', 'normal')
                        }
                        
                        # Set g.user_id for API endpoints that use it
                        from flask import g
                        g.user_id = current_user.id

                        # Check admin requirement for web session auth
                        if require_admin:
                            user_role = getattr(current_user, 'role', 'normal')
                            is_admin = getattr(current_user, 'is_admin', False)

                            if user_role != 'admin' and not is_admin:
                                return jsonify({'error': 'Admin access required'}), 403

                        return func(*args, **kwargs)
                    else:
                        return jsonify({'error': 'Authentication required'}), 401
            else:
                # Fallback to login_required if auth_manager not available
                return login_required(func)(*args, **kwargs)
        return decorated_function
    return decorator

logger = logging.getLogger(__name__)

# Blueprint definitions
main_bp = Blueprint('main', __name__)
auth_bp = Blueprint('auth', __name__)
api_bp = Blueprint('api', __name__)

# These will be set by the app.py when registering blueprints
main_bp.db_manager = None
main_bp.config_manager = None
main_bp.message_bus = None

auth_bp.auth_manager = None
auth_bp.db_manager = None

api_bp.api = None
api_bp.auth_manager = None
api_bp.db_manager = None
api_bp.config_manager = None
api_bp.message_bus = None
api_bp.settings = None


# HLS Streaming Routes (no authentication required)
# CORS headers are added to support cross-origin video streaming
# This allows video files to be served from a different hostname
@main_bp.route('/hls/<path:filename>')
def serve_hls_file(filename):
    """Serve HLS streaming files without authentication
    
    This route serves the HLS playlist and segment files generated
    by the RTSPStreamer component. No authentication is required
    to allow video players to access the stream.
    
    The stream is always available at /hls/stream.m3u8 regardless of
    which video is currently playing (INTRO.mp4, match video, etc.).
    
    CORS Support:
    - Access-Control-Allow-Origin: * (or configured origins)
    - Allows video streaming from different hostnames to avoid
      browser connection limits per origin
    """
    import os
    from flask import send_from_directory, jsonify, Response
    
    try:
        # Get the main application from Flask g context
        main_app = g.get('main_app')
        if not main_app:
            logger.warning("Main app not available for HLS streaming")
            return jsonify({"error": "Streaming not available"}), 503
        
        # Get the headless_player (RTSPStreamer) component
        headless_player = getattr(main_app, 'headless_player', None)
        if not headless_player:
            # Try thread_manager to get the component
            thread_manager = getattr(main_app, 'thread_manager', None)
            if thread_manager:
                headless_player = thread_manager.get_component('headless_player')
        
        if not headless_player:
            logger.warning("Headless player not available for HLS streaming")
            return jsonify({"error": "Streaming service not available"}), 503
        
        # Get the HLS directory from the streamer
        hls_dir = getattr(headless_player, 'hls_dir', None)
        if not hls_dir:
            logger.warning("HLS directory not available")
            return jsonify({"error": "No active stream"}), 404
        
        # Check if the directory exists
        if not os.path.exists(hls_dir):
            logger.warning(f"HLS directory does not exist: {hls_dir}")
            return jsonify({"error": "Stream directory not found"}), 404
        
        # Map URL filename to actual filename
        # /hls/stream.m3u8 -> mbetterc_stream.m3u8
        # /hls/stream_0001.m4s -> mbetterc_stream_0001.m4s (fMP4 segments)
        # /hls/stream_0001.ts -> mbetterc_stream_0001.ts (MPEG-TS segments, legacy)
        # /hls/stream_init.mp4 -> mbetterc_stream_init.mp4 (fMP4 init segment)
        stream_name = getattr(headless_player, 'stream_name', 'mbetterc_stream')
        if filename == 'stream.m3u8':
            actual_filename = f"{stream_name}.m3u8"
        elif filename == 'stream_init.mp4':
            # fMP4 init segment
            actual_filename = f"{stream_name}_init.mp4"
        elif filename.startswith('stream_') and (filename.endswith('.ts') or filename.endswith('.m4s')):
            # Segment files: stream_0001.ts -> mbetterc_stream_0001.ts
            # or stream_0001.m4s -> mbetterc_stream_0001.m4s
            actual_filename = filename.replace('stream_', f'{stream_name}_', 1)
        else:
            # For any other files, try to serve as-is
            actual_filename = filename
        
        # Serve the file
        logger.debug(f"Serving HLS file: {filename} -> {actual_filename} from {hls_dir}")
        response = send_from_directory(hls_dir, actual_filename)
        
        # Add CORS headers for cross-origin video streaming
        # This allows video to be served from a different hostname
        response.headers['Access-Control-Allow-Origin'] = '*'
        response.headers['Access-Control-Allow-Methods'] = 'GET, OPTIONS'
        response.headers['Access-Control-Allow-Headers'] = 'Range, Content-Type'
        response.headers['Access-Control-Expose-Headers'] = 'Content-Length, Content-Range, Content-Type'
        
        # Add caching headers for better performance
        # Playlist files should not be cached (live stream)
        if filename.endswith('.m3u8'):
            response.headers['Cache-Control'] = 'no-cache, no-store, must-revalidate'
            response.headers['Pragma'] = 'no-cache'
            response.headers['Expires'] = '0'
        else:
            # Segment files can be cached briefly
            response.headers['Cache-Control'] = 'public, max-age=10'
        
        return response
        
    except Exception as e:
        logger.error(f"Error serving HLS file {filename}: {e}")
        return jsonify({"error": "Failed to serve stream file"}), 500


# Handle OPTIONS requests for CORS preflight
@main_bp.route('/hls/<path:filename>', methods=['OPTIONS'])
def hls_options(filename):
    """Handle CORS preflight requests for HLS streaming"""
    response = Response()
    response.headers['Access-Control-Allow-Origin'] = '*'
    response.headers['Access-Control-Allow-Methods'] = 'GET, OPTIONS'
    response.headers['Access-Control-Allow-Headers'] = 'Range, Content-Type'
    response.headers['Access-Control-Max-Age'] = '86400'  # 24 hours
    return response


# Main routes
@main_bp.route('/')
@login_required
def index():
    """Dashboard home page - redirects cashier users to cashier dashboard"""
    try:
        # Check if user is cashier and redirect them to cashier dashboard
        if hasattr(current_user, 'role') and current_user.role == 'cashier':
            return redirect(url_for('main.cashier_dashboard'))
        elif hasattr(current_user, 'is_cashier_user') and current_user.is_cashier_user():
            return redirect(url_for('main.cashier_dashboard'))
        
        return render_template('dashboard/index.html',
                             user=current_user,
                             page_title="Dashboard")
    except Exception as e:
        logger.error(f"Dashboard index error: {e}")
        flash("Error loading dashboard", "error")
        return render_template('errors/500.html'), 500


@main_bp.route('/cashier')
@login_required
def cashier_dashboard():
    """Cashier-specific dashboard page"""
    try:
        # Verify user is cashier
        if not (hasattr(current_user, 'role') and current_user.role == 'cashier'):
            if not (hasattr(current_user, 'is_cashier_user') and current_user.is_cashier_user()):
                flash("Cashier access required", "error")
                return redirect(url_for('main.index'))
        
        return render_template('dashboard/cashier.html',
                             user=current_user,
                             page_title="Cashier Dashboard")
    except Exception as e:
        logger.error(f"Cashier dashboard error: {e}")
        flash("Error loading cashier dashboard", "error")
        return render_template('errors/500.html'), 500


# Admin/User Betting Routes (replacing video control)
@main_bp.route('/bets')
@login_required
def bets():
    """Admin/User betting management page"""
    try:
        # Get today's date for the date picker default
        from datetime import date
        today_date = date.today().isoformat()
        
        return render_template('dashboard/admin_bets.html',
                             user=current_user,
                             today_date=today_date,
                             page_title="Betting Management")
    except Exception as e:
        logger.error(f"Admin bets page error: {e}")
        flash("Error loading bets page", "error")
        return render_template('errors/500.html'), 500


@main_bp.route('/bets/new')
@login_required
def new_bet():
    """Admin/User new bet creation page"""
    try:
        # Get current date for the page
        from datetime import date
        current_date = date.today().strftime('%Y-%m-%d')
        
        return render_template('dashboard/admin_new_bet.html',
                             user=current_user,
                             current_date=current_date,
                             page_title="Create New Bet")
    except Exception as e:
        logger.error(f"Admin new bet page error: {e}")
        flash("Error loading new bet page", "error")
        return render_template('errors/500.html'), 500


@main_bp.route('/bets/<uuid:bet_id>')
@login_required
def bet_details(bet_id):
    """Admin/User bet details page"""
    try:
        # Convert UUID to string
        bet_uuid = str(bet_id)
        
        # Fetch bet details from database
        from ..database.models import BetModel, BetDetailModel, MatchModel
        
        session = main_bp.db_manager.get_session()
        try:
            # Get the bet
            bet = session.query(BetModel).filter_by(uuid=bet_uuid).first()
            if not bet:
                flash("Bet not found", "error")
                return redirect(url_for('main.bets'))
            
            # Get bet details with match information
            bet_details = session.query(BetDetailModel).filter_by(bet_id=bet_uuid).all()
            bet_details_data = []
            
            # Statistics counters
            results = {
                'pending': 0,
                'won': 0,
                'lost': 0,
                'cancelled': 0,
                'winnings': 0.0
            }
            
            total_amount = 0.0
            has_pending = False
            
            for detail in bet_details:
                # Get match information
                match = session.query(MatchModel).filter_by(id=detail.match_id).first()

                # Parse winning_outcomes JSON string if it exists
                winning_outcomes = []
                if match and match.winning_outcomes:
                    try:
                        if isinstance(match.winning_outcomes, str):
                            winning_outcomes = json.loads(match.winning_outcomes)
                        elif isinstance(match.winning_outcomes, list):
                            winning_outcomes = match.winning_outcomes
                    except (json.JSONDecodeError, TypeError):
                        winning_outcomes = []

                # Get odds for this outcome
                odds = 0.0
                if match:
                    outcomes_dict = match.get_outcomes_dict()
                    odds = outcomes_dict.get(detail.outcome, 0.0)

                detail_dict = {
                    'id': detail.id,
                    'match_id': detail.match_id,
                    'outcome': detail.outcome,
                    'amount': float(detail.amount),
                    'odds': float(odds),
                    'potential_winning': float(detail.amount) * float(odds),
                    'result': detail.result,
                    'match': {
                        'match_number': match.match_number if match else 'Unknown',
                        'fighter1_township': match.fighter1_township if match else 'Unknown',
                        'fighter2_township': match.fighter2_township if match else 'Unknown',
                        'venue_kampala_township': match.venue_kampala_township if match else 'Unknown',
                        'status': match.status if match else 'Unknown',
                        'winning_outcomes': winning_outcomes
                    } if match else None
                }
                
                bet_details_data.append(detail_dict)
                total_amount += float(detail.amount)
                
                # Update statistics
                if detail.result == 'pending':
                    results['pending'] += 1
                    has_pending = True
                elif is_settled_bet_detail_winning(detail, match, session):
                    results['won'] += 1
                    results['winnings'] += float(detail.win_amount or 0.0)
                elif detail.result == 'lost':
                    results['lost'] += 1
                elif detail.result == 'cancelled':
                    results['cancelled'] += 1
            
            # Create bet object for template
            bet_data = {
                'uuid': bet.uuid,
                'bet_datetime': bet.bet_datetime,
                'fixture_id': bet.fixture_id,
                'paid': bet.paid,
                'total_amount': total_amount,
                'bet_count': len(bet_details_data),
                'has_pending': has_pending,
                'barcode_standard': bet.barcode_standard,
                'barcode_data': bet.barcode_data,
                'bet_details': bet_details_data
            }
            
            return render_template('dashboard/admin_bet_details.html',
                                 user=current_user,
                                 bet=bet_data,
                                 results=results,
                                 page_title=f"Bet Details - {bet_uuid[:8]}...")
                                 
        finally:
            session.close()
        
    except Exception as e:
        logger.error(f"Admin bet details page error: {e}")
        flash("Error loading bet details", "error")
        return render_template('errors/500.html'), 500


@main_bp.route('/templates')
@login_required
def templates():
    """Template management page"""
    try:
        # Restrict cashier users from accessing templates page
        if hasattr(current_user, 'role') and current_user.role == 'cashier':
            flash("Access denied", "error")
            return redirect(url_for('main.cashier_dashboard'))
        elif hasattr(current_user, 'is_cashier_user') and current_user.is_cashier_user():
            flash("Access denied", "error")
            return redirect(url_for('main.cashier_dashboard'))
        
        return render_template('dashboard/templates.html',
                             user=current_user,
                             page_title="Templates")
    except Exception as e:
        logger.error(f"Templates page error: {e}")
        flash("Error loading templates", "error")
        return render_template('errors/500.html'), 500


@main_bp.route('/config')
@login_required
def configuration():
    """Configuration page"""
    try:
        if not current_user.is_admin:
            flash("Admin access required", "error")
            # Redirect cashier users to their dashboard
            if hasattr(current_user, 'role') and current_user.role == 'cashier':
                return redirect(url_for('main.cashier_dashboard'))
            elif hasattr(current_user, 'is_cashier_user') and current_user.is_cashier_user():
                return redirect(url_for('main.cashier_dashboard'))
            return redirect(url_for('main.index'))
        
        # Get current configuration values
        config_data = {}
        try:
            if main_bp.config_manager:
                # Load configuration values from database/config manager
                general_config = main_bp.config_manager.get_section_config("general") or {}
                video_config = main_bp.config_manager.get_section_config("video") or {}
                database_config = main_bp.config_manager.get_section_config("database") or {}
                api_config = main_bp.config_manager.get_section_config("api") or {}
                screen_cast_config = main_bp.config_manager.get_section_config("screen_cast") or {}
                streaming_config = main_bp.config_manager.get_section_config("streaming") or {}

                # Load barcode configuration from database
                barcode_enabled = main_bp.db_manager.get_config_value('barcode.enabled', False) if main_bp.db_manager else False
                barcode_standard = main_bp.db_manager.get_config_value('barcode.standard', 'none') if main_bp.db_manager else 'none'
                barcode_width = main_bp.db_manager.get_config_value('barcode.width', 200) if main_bp.db_manager else 200
                barcode_height = main_bp.db_manager.get_config_value('barcode.height', 100) if main_bp.db_manager else 100
                barcode_show_on_thermal = main_bp.db_manager.get_config_value('barcode.show_on_thermal', True) if main_bp.db_manager else True
                barcode_show_on_verification = main_bp.db_manager.get_config_value('barcode.show_on_verification', True) if main_bp.db_manager else True

                # Load QR code configuration from database
                qrcode_enabled = main_bp.db_manager.get_config_value('qrcode.enabled', False) if main_bp.db_manager else False
                qrcode_size = main_bp.db_manager.get_config_value('qrcode.size', 200) if main_bp.db_manager else 200
                qrcode_error_correction = main_bp.db_manager.get_config_value('qrcode.error_correction', 'M') if main_bp.db_manager else 'M'
                qrcode_show_on_thermal = main_bp.db_manager.get_config_value('qrcode.show_on_thermal', True) if main_bp.db_manager else True
                qrcode_show_on_verification = main_bp.db_manager.get_config_value('qrcode.show_on_verification', True) if main_bp.db_manager else True

                # Load signup configuration from database
                dashboard_signup_enabled = main_bp.db_manager.get_config_value('dashboard_signup.enabled', False) if main_bp.db_manager else False
                website_signup_enabled = main_bp.db_manager.get_config_value('website_signup.enabled', False) if main_bp.db_manager else False

                config_data.update({
                    # General settings
                    'app_name': general_config.get('app_name', 'MbetterClient'),
                    'log_level': general_config.get('log_level', 'INFO'),
                    'enable_qt': general_config.get('enable_qt', True),

                    # Video settings
                    'video_width': video_config.get('video_width', 1920),
                    'video_height': video_config.get('video_height', 1080),
                    'fullscreen': video_config.get('fullscreen', False),

                    # Database settings
                    'db_path': database_config.get('db_path', 'data/mbetterclient.db'),

                    # API settings
                    'fastapi_url': api_config.get('fastapi_url', 'https://mbetter.nexlab.net/'),
                    'api_token': api_config.get('api_token', 'gtW82Ejr8LySk2PFVRefn-HEWG5NrowsHaDCht2yPGo'),
                    'api_interval': api_config.get('api_interval', 600),
                    'api_timeout': api_config.get('api_timeout', 30),
                    'api_enabled': api_config.get('api_enabled', True),

                    # Screen cast settings
                    'screen_cast_enabled': screen_cast_config.get('enabled', True),
                    'screen_cast_port': screen_cast_config.get('stream_port', 8000),
                    'chromecast_name': screen_cast_config.get('chromecast_name', ''),
                    'screen_cast_resolution': screen_cast_config.get('resolution', '1280x720'),
                    'screen_cast_framerate': screen_cast_config.get('framerate', 15),
                    'screen_cast_auto_start_capture': screen_cast_config.get('auto_start_capture', False),
                    'screen_cast_auto_start_streaming': screen_cast_config.get('auto_start_streaming', False),

                    # Barcode settings
                    'barcode_enabled': barcode_enabled,
                    'barcode_standard': barcode_standard,
                    'barcode_width': barcode_width,
                    'barcode_height': barcode_height,
                    'barcode_show_on_thermal': barcode_show_on_thermal,
                    'barcode_show_on_verification': barcode_show_on_verification,

                    # QR code settings
                    'qrcode_enabled': qrcode_enabled,
                    'qrcode_size': qrcode_size,
                    'qrcode_error_correction': qrcode_error_correction,
                    'qrcode_show_on_thermal': qrcode_show_on_thermal,
                    'qrcode_show_on_verification': qrcode_show_on_verification,

                    # Signup settings
                    'dashboard_signup_enabled': dashboard_signup_enabled,
                    'website_signup_enabled': website_signup_enabled,

                    # Streaming settings
                    'streaming_port': streaming_config.get('port', 5884),
                    'streaming_framerate': streaming_config.get('framerate', 25),
                    'streaming_segment_duration': streaming_config.get('segment_duration', 2),
                    'streaming_video_bitrate_high': streaming_config.get('video_bitrate_high', 10000),
                    'video_hostname': streaming_config.get('video_hostname'),
                    'video_port': streaming_config.get('video_port'),
                    'video_use_ssl': streaming_config.get('video_use_ssl')
                })
        except Exception as e:
            logger.warning(f"Error loading configuration values: {e}")
            # Use defaults if config loading fails
            config_data = {
                'app_name': 'MbetterClient',
                'log_level': 'INFO',
                'enable_qt': True,
                'video_width': 1920,
                'video_height': 1080,
                'fullscreen': False,
                'db_path': 'data/mbetterclient.db',
                'fastapi_url': 'https://mbetter.nexlab.net/',
                'api_token': 'gtW82Ejr8LySk2PFVRefn-HEWG5NrowsHaDCht2yPGo',
                'api_interval': 600,
                'api_timeout': 30,
                'api_enabled': True,
                'screen_cast_enabled': True,
                'screen_cast_port': 8000,
                'chromecast_name': '',
                'screen_cast_resolution': '1280x720',
                'screen_cast_framerate': 15,
                'screen_cast_auto_start_capture': False,
                'screen_cast_auto_start_streaming': False,
                # Default barcode settings
                'barcode_enabled': False,
                'barcode_standard': 'none',
                'barcode_width': 200,
                'barcode_height': 100,
                'barcode_show_on_thermal': True,
                'barcode_show_on_verification': True,
                # Default signup settings
                'dashboard_signup_enabled': False,
                'website_signup_enabled': False,
                # Default streaming settings
                'streaming_port': 5884,
                'streaming_framerate': 25,
                'streaming_segment_duration': 2,
                'streaming_video_bitrate_high': 10000,
                'video_hostname': None,
                'video_port': None,
                'video_use_ssl': None
            }
        
        return render_template('dashboard/config.html',
                             user=current_user,
                             config=config_data,
                             page_title="Configuration")
    except Exception as e:
        logger.error(f"Configuration page error: {e}")
        flash("Error loading configuration", "error")
        return render_template('errors/500.html'), 500


@main_bp.route('/users')
@login_required
def users():
    """User management page"""
    try:
        if not current_user.is_admin:
            flash("Admin access required", "error")
            # Redirect cashier users to their dashboard
            if hasattr(current_user, 'role') and current_user.role == 'cashier':
                return redirect(url_for('main.cashier_dashboard'))
            elif hasattr(current_user, 'is_cashier_user') and current_user.is_cashier_user():
                return redirect(url_for('main.cashier_dashboard'))
            return redirect(url_for('main.index'))
        
        return render_template('dashboard/users.html',
                             user=current_user,
                             page_title="User Management")
    except Exception as e:
        logger.error(f"Users page error: {e}")
        flash("Error loading user management", "error")
        return render_template('errors/500.html'), 500


@main_bp.route('/tokens')
@login_required
def api_tokens():
    """API token management page"""
    try:
        # Restrict cashier users from accessing API tokens page
        if hasattr(current_user, 'role') and current_user.role == 'cashier':
            flash("Access denied", "error")
            return redirect(url_for('main.cashier_dashboard'))
        elif hasattr(current_user, 'is_cashier_user') and current_user.is_cashier_user():
            flash("Access denied", "error")
            return redirect(url_for('main.cashier_dashboard'))
        
        return render_template('dashboard/tokens.html',
                             user=current_user,
                             page_title="API Tokens")
    except Exception as e:
        logger.error(f"API tokens page error: {e}")
        flash("Error loading API tokens", "error")
        return render_template('errors/500.html'), 500


@main_bp.route('/oauth-providers')
@login_required
def oauth_providers():
    """OAuth providers management page"""
    try:
        if not current_user.is_admin:
            flash("Admin access required", "error")
            return redirect(url_for('main.index'))
        
        return render_template('dashboard/oauth_providers.html',
                             user=current_user,
                             page_title="OAuth Providers")
    except Exception as e:
        logger.error(f"OAuth providers page error: {e}")
        flash("Error loading OAuth providers", "error")
        return render_template('errors/500.html'), 500


@main_bp.route('/billing')
@login_required
def billing():
    """Billing management page"""
    try:
        if not current_user.is_admin:
            flash("Admin access required", "error")
            return redirect(url_for('main.index'))
        
        return render_template('dashboard/billing.html',
                             user=current_user,
                             page_title="Billing Management")
    except Exception as e:
        logger.error(f"Billing page error: {e}")
        flash("Error loading billing", "error")
        return render_template('errors/500.html'), 500


@main_bp.route('/billing/summary')
@login_required
def billing_summary():
    """Billing summary page"""
    try:
        if not current_user.is_admin:
            flash("Admin access required", "error")
            return redirect(url_for('main.index'))
        
        return render_template('dashboard/billing.html',
                             user=current_user,
                             page_title="Billing Summary",
                             active_tab='summary')
    except Exception as e:
        logger.error(f"Billing summary page error: {e}")
        flash("Error loading billing summary", "error")
        return render_template('errors/500.html'), 500


@main_bp.route('/billing/subscription')
@login_required
def billing_subscription():
    """Billing subscription page"""
    try:
        if not current_user.is_admin:
            flash("Admin access required", "error")
            return redirect(url_for('main.index'))
        
        return render_template('dashboard/billing.html',
                             user=current_user,
                             page_title="Subscription Management",
                             active_tab='subscription')
    except Exception as e:
        logger.error(f"Billing subscription page error: {e}")
        flash("Error loading subscription", "error")
        return render_template('errors/500.html'), 500


@main_bp.route('/billing/plans')
@login_required
def billing_plans():
    """Billing plans page"""
    try:
        if not current_user.is_admin:
            flash("Admin access required", "error")
            return redirect(url_for('main.index'))
        
        return render_template('dashboard/billing.html',
                             user=current_user,
                             page_title="Subscription Plans",
                             active_tab='plans')
    except Exception as e:
        logger.error(f"Billing plans page error: {e}")
        flash("Error loading plans", "error")
        return render_template('errors/500.html'), 500


@main_bp.route('/billing/currencies')
@login_required
def billing_currencies():
    """Billing currencies page"""
    try:
        if not current_user.is_admin:
            flash("Admin access required", "error")
            return redirect(url_for('main.index'))
        
        return render_template('dashboard/billing.html',
                             user=current_user,
                             page_title="Currency Management",
                             active_tab='currencies')
    except Exception as e:
        logger.error(f"Billing currencies page error: {e}")
        flash("Error loading currencies", "error")
        return render_template('errors/500.html'), 500


@main_bp.route('/billing/gateways')
@login_required
def billing_gateways():
    """Billing payment gateways page"""
    try:
        if not current_user.is_admin:
            flash("Admin access required", "error")
            return redirect(url_for('main.index'))
        
        return render_template('dashboard/billing.html',
                             user=current_user,
                             page_title="Payment Gateways",
                             active_tab='gateways')
    except Exception as e:
        logger.error(f"Billing gateways page error: {e}")
        flash("Error loading gateways", "error")
        return render_template('errors/500.html'), 500


@main_bp.route('/fixtures')
@login_required
def fixtures():
    """Fixtures management page"""
    try:
        return render_template('dashboard/fixtures.html',
                              user=current_user,
                              page_title="Fixtures")
    except Exception as e:
        logger.error(f"Fixtures page error: {e}")
        flash("Error loading fixtures", "error")
        return render_template('errors/500.html'), 500


@main_bp.route('/fixtures/<fixture_id>')
@login_required
def fixture_details(fixture_id):
    """Fixture details page showing all matches in the fixture"""
    try:
        return render_template('dashboard/fixture_details.html',
                                user=current_user,
                                fixture_id=fixture_id,
                                page_title=f"Fixture Details - Fixture #{fixture_id}")
    except Exception as e:
        logger.error(f"Fixture details page error: {e}")
        flash("Error loading fixture details", "error")
        return render_template('errors/500.html'), 500

@main_bp.route('/matches-templates/<int:template_id>')
@login_required
def matches_template_details(template_id):
    """Match template details page for editing odds"""
    try:
        # Restrict cashier users from accessing template details
        if hasattr(current_user, 'role') and current_user.role == 'cashier':
            flash("Access denied", "error")
            return redirect(url_for('main.cashier_dashboard'))
        elif hasattr(current_user, 'is_cashier_user') and current_user.is_cashier_user():
            flash("Access denied", "error")
            return redirect(url_for('main.cashier_dashboard'))
        
        return render_template('dashboard/matches_template_details.html',
                                user=current_user,
                                template_id=template_id,
                                page_title=f"Template Details - Template #{template_id}")
    except Exception as e:
        logger.error(f"Match template details page error: {e}")
        flash("Error loading template details", "error")
        return render_template('errors/500.html'), 500


@main_bp.route('/templates-details')
@login_required
def templates_details():
    """Templates details page listing all template matches"""
    try:
        # Restrict cashier users from accessing templates details
        if hasattr(current_user, 'role') and current_user.role == 'cashier':
            flash("Access denied", "error")
            return redirect(url_for('main.cashier_dashboard'))
        elif hasattr(current_user, 'is_cashier_user') and current_user.is_cashier_user():
            flash("Access denied", "error")
            return redirect(url_for('main.cashier_dashboard'))
        
        return render_template('dashboard/templates_details.html',
                                user=current_user,
                                page_title="Fixture Template Details")
    except Exception as e:
        logger.error(f"Templates details page error: {e}")
        flash("Error loading templates details", "error")
        return render_template('errors/500.html'), 500



@main_bp.route('/matches/<int:match_id>/<fixture_id>')
@login_required
def match_details(match_id, fixture_id):
    """Match details page showing match information and outcomes"""
    try:
        return render_template('dashboard/match_details.html',
                                user=current_user,
                                match_id=match_id,
                                fixture_id=fixture_id,
                                page_title=f"Match Details - Match #{match_id}")
    except Exception as e:
        logger.error(f"Match details page error: {e}")
        flash("Error loading match details", "error")
        return render_template('errors/500.html'), 500


@main_bp.route('/logs')
@login_required
def logs():
    """Application logs page"""
    try:
        if not current_user.is_admin:
            flash("Admin access required", "error")
            # Redirect cashier users to their dashboard
            if hasattr(current_user, 'role') and current_user.role == 'cashier':
                return redirect(url_for('main.cashier_dashboard'))
            elif hasattr(current_user, 'is_cashier_user') and current_user.is_cashier_user():
                return redirect(url_for('main.cashier_dashboard'))
            return redirect(url_for('main.index'))

        return render_template('dashboard/logs.html',
                              user=current_user,
                              page_title="Application Logs")
    except Exception as e:
        logger.error(f"Logs page error: {e}")
        flash("Error loading logs", "error")
        return render_template('errors/500.html'), 500


@main_bp.route('/extraction')
@login_required
def extraction():
    """Extraction management page"""
    try:
        # Restrict cashier users from accessing extraction page
        if hasattr(current_user, 'role') and current_user.role == 'cashier':
            flash("Access denied", "error")
            return redirect(url_for('main.cashier_dashboard'))
        elif hasattr(current_user, 'is_cashier_user') and current_user.is_cashier_user():
            flash("Access denied", "error")
            return redirect(url_for('main.cashier_dashboard'))

        return render_template('dashboard/extraction.html',
                              user=current_user,
                              page_title="Extraction Management")
    except Exception as e:
        logger.error(f"Extraction page error: {e}")
        flash("Error loading extraction page", "error")
        return render_template('errors/500.html'), 500


# Cashier Betting Routes
@main_bp.route('/cashier/bets')
@login_required
def cashier_bets():
    """Cashier betting management page"""
    try:
        # Verify user is cashier
        if not (hasattr(current_user, 'role') and current_user.role == 'cashier'):
            if not (hasattr(current_user, 'is_cashier_user') and current_user.is_cashier_user()):
                flash("Cashier access required", "error")
                return redirect(url_for('main.index'))
        
        # Get today's date for the date picker default
        from datetime import date
        today_date = date.today().isoformat()
        
        return render_template('dashboard/bets.html',
                             user=current_user,
                             today_date=today_date,
                             page_title="Betting Management")
    except Exception as e:
        logger.error(f"Cashier bets page error: {e}")
        flash("Error loading bets page", "error")
        return render_template('errors/500.html'), 500


@main_bp.route('/cashier/bets/new')
@login_required
def cashier_new_bet():
    """Cashier new bet creation page"""
    try:
        # Verify user is cashier
        if not (hasattr(current_user, 'role') and current_user.role == 'cashier'):
            if not (hasattr(current_user, 'is_cashier_user') and current_user.is_cashier_user()):
                flash("Cashier access required", "error")
                return redirect(url_for('main.index'))
        
        # Get current date for the page
        from datetime import date
        current_date = date.today().strftime('%Y-%m-%d')
        
        return render_template('dashboard/new_bet.html',
                             user=current_user,
                             current_date=current_date,
                             page_title="Create New Bet")
    except Exception as e:
        logger.error(f"Cashier new bet page error: {e}")
        flash("Error loading new bet page", "error")
        return render_template('errors/500.html'), 500


@main_bp.route('/cashier/bets/<uuid:bet_id>')
@login_required
def cashier_bet_details(bet_id):
    """Cashier bet details page"""
    try:
        # Verify user is cashier
        if not (hasattr(current_user, 'role') and current_user.role == 'cashier'):
            if not (hasattr(current_user, 'is_cashier_user') and current_user.is_cashier_user()):
                flash("Cashier access required", "error")
                return redirect(url_for('main.index'))
        
        # Convert UUID to string
        bet_uuid = str(bet_id)
        
        # Fetch bet details from database
        from ..database.models import BetModel, BetDetailModel, MatchModel
        
        session = main_bp.db_manager.get_session()
        try:
            # Get the bet
            bet = session.query(BetModel).filter_by(uuid=bet_uuid).first()
            if not bet:
                flash("Bet not found", "error")
                return redirect(url_for('main.cashier_bets'))
            
            # Get bet details with match information
            bet_details = session.query(BetDetailModel).filter_by(bet_id=bet_uuid).all()
            bet_details_data = []
            
            # Statistics counters
            results = {
                'pending': 0,
                'won': 0,
                'lost': 0,
                'cancelled': 0,
                'winnings': 0.0
            }
            
            total_amount = 0.0
            has_pending = False
            
            for detail in bet_details:
                # Get match information
                match = session.query(MatchModel).filter_by(id=detail.match_id).first()
                
                # Parse winning_outcomes JSON string if it exists
                winning_outcomes = []
                if match and match.winning_outcomes:
                    try:
                        if isinstance(match.winning_outcomes, str):
                            winning_outcomes = json.loads(match.winning_outcomes)
                        elif isinstance(match.winning_outcomes, list):
                            winning_outcomes = match.winning_outcomes
                    except (json.JSONDecodeError, TypeError):
                        winning_outcomes = []

                # Get odds for this outcome
                odds = 0.0
                if match:
                    outcomes_dict = match.get_outcomes_dict()
                    odds = outcomes_dict.get(detail.outcome, 0.0)

                detail_dict = {
                    'id': detail.id,
                    'match_id': detail.match_id,
                    'outcome': detail.outcome,
                    'amount': float(detail.amount),
                    'odds': float(odds),
                    'potential_winning': float(detail.amount) * float(odds),
                    'result': detail.result,
                    'match': {
                        'match_number': match.match_number if match else 'Unknown',
                        'fighter1_township': match.fighter1_township if match else 'Unknown',
                        'fighter2_township': match.fighter2_township if match else 'Unknown',
                        'venue_kampala_township': match.venue_kampala_township if match else 'Unknown',
                        'status': match.status if match else 'Unknown',
                        'winning_outcomes': winning_outcomes
                    } if match else None
                }
                
                bet_details_data.append(detail_dict)
                total_amount += float(detail.amount)
                
                # Update statistics
                if detail.result == 'pending':
                    results['pending'] += 1
                    has_pending = True
                elif is_settled_bet_detail_winning(detail, match, session):
                    results['won'] += 1
                    results['winnings'] += float(detail.win_amount or 0)  # Use actual win amount
                elif detail.result == 'lost':
                    results['lost'] += 1
                elif detail.result == 'cancelled':
                    results['cancelled'] += 1
            
            # Create bet object for template
            bet_data = {
                'uuid': bet.uuid,
                'bet_datetime': bet.bet_datetime,
                'fixture_id': bet.fixture_id,
                'paid': bet.paid,
                'total_amount': total_amount,
                'bet_count': len(bet_details_data),
                'has_pending': has_pending,
                'barcode_standard': bet.barcode_standard,
                'barcode_data': bet.barcode_data,
                'bet_details': bet_details_data
            }
            
            return render_template('dashboard/bet_details.html',
                                 user=current_user,
                                 bet=bet_data,
                                 results=results,
                                 page_title=f"Bet Details - {bet_uuid[:8]}...")
                                 
        finally:
            session.close()
        
    except Exception as e:
        logger.error(f"Cashier bet details page error: {e}")
        flash("Error loading bet details", "error")
        return render_template('errors/500.html'), 500


# Auth routes
@auth_bp.route('/login', methods=['GET', 'POST'])
def login():
    """Login page"""
    if current_user.is_authenticated:
        return redirect(url_for('main.index'))
    
    if request.method == 'POST':
        try:
            username = request.form.get('username', '').strip()
            password = request.form.get('password', '')
            remember_me = request.form.get('remember_me', False)
            
            if not username or not password:
                flash("Username and password are required", "error")
                return render_template('auth/login.html')
            
            # Authenticate user
            authenticated_user = auth_bp.auth_manager.authenticate_user(username, password)
            
            if authenticated_user:
                login_user(authenticated_user, remember=remember_me)
                logger.info(f"User logged in: {username}")
                
                # Redirect to next page or appropriate dashboard
                next_page = request.args.get('next')
                if next_page:
                    return redirect(next_page)
                else:
                    # Redirect cashier users to their specific dashboard
                    if hasattr(authenticated_user, 'role') and authenticated_user.role == 'cashier':
                        return redirect(url_for('main.cashier_dashboard'))
                    elif hasattr(authenticated_user, 'is_cashier_user') and authenticated_user.is_cashier_user():
                        return redirect(url_for('main.cashier_dashboard'))
                    else:
                        return redirect(url_for('main.index'))
            else:
                flash("Invalid username or password", "error")
                return render_template('auth/login.html')
                
        except Exception as e:
            logger.error(f"Login error: {e}")
            flash("Login failed. Please try again.", "error")
            return render_template('auth/login.html')
    
    return render_template('auth/login.html')
    
    
@main_bp.route('/video_test')
@login_required
def video_test():
    """Video upload test page"""
    try:
        # Restrict cashier users from accessing video test page
        if hasattr(current_user, 'role') and current_user.role == 'cashier':
            flash("Access denied", "error")
            return redirect(url_for('main.cashier_dashboard'))
        elif hasattr(current_user, 'is_cashier_user') and current_user.is_cashier_user():
            flash("Access denied", "error")
            return redirect(url_for('main.cashier_dashboard'))
        
        return render_template('dashboard/video_test.html',
                             user=current_user,
                             page_title="Video Upload Test")
    except Exception as e:
        logger.error(f"Video test page error: {e}")
        flash("Error loading video test page", "error")
        return render_template('errors/500.html'), 500


@auth_bp.route('/logout')
@login_required
def logout():
    """Logout user"""
    try:
        username = current_user.username
        logout_user()
        logger.info(f"User logged out: {username}")
        flash("You have been logged out", "info")
    except Exception as e:
        logger.error(f"Logout error: {e}")
    
    return redirect(url_for('auth.login'))


@auth_bp.route('/change-password', methods=['GET', 'POST'])
@login_required
def change_password():
    """Change password page"""
    if request.method == 'POST':
        try:
            current_password = request.form.get('current_password', '')
            new_password = request.form.get('new_password', '')
            confirm_password = request.form.get('confirm_password', '')

            if not all([current_password, new_password, confirm_password]):
                flash("All fields are required", "error")
                return render_template('auth/change_password.html')

            if new_password != confirm_password:
                flash("New passwords do not match", "error")
                return render_template('auth/change_password.html')

            if len(new_password) < 6:
                flash("Password must be at least 6 characters", "error")
                return render_template('auth/change_password.html')

            # Change password
            success = auth_bp.auth_manager.change_password(
                current_user.id, current_password, new_password
            )

            if success:
                flash("Password changed successfully", "success")
                return redirect(url_for('main.index'))
            else:
                flash("Current password is incorrect", "error")
                return render_template('auth/change_password.html')

        except Exception as e:
            logger.error(f"Change password error: {e}")
            flash("Failed to change password", "error")
            return render_template('auth/change_password.html')

    return render_template('auth/change_password.html')


@main_bp.route('/reports')
@login_required
def reports():
    """Reports page for admin/user"""
    try:
        # Get today's date for the date picker default
        from datetime import date
        today_date = date.today().isoformat()

        return render_template('dashboard/reports.html',
                              user=current_user,
                              today_date=today_date,
                              page_title="Reports")
    except Exception as e:
        logger.error(f"Reports page error: {e}")
        flash("Error loading reports page", "error")
        return render_template('errors/500.html'), 500


@main_bp.route('/cashier/reports')
@login_required
def cashier_reports():
    """Reports page for cashier"""
    try:
        # Verify user is cashier
        if not (hasattr(current_user, 'role') and current_user.role == 'cashier'):
            if not (hasattr(current_user, 'is_cashier_user') and current_user.is_cashier_user()):
                flash("Cashier access required", "error")
                return redirect(url_for('main.index'))

        # Get today's date for the date picker default
        from datetime import date
        today_date = date.today().isoformat()

        return render_template('dashboard/reports.html',
                              user=current_user,
                              today_date=today_date,
                              page_title="Reports")
    except Exception as e:
        logger.error(f"Cashier reports page error: {e}")
        flash("Error loading reports page", "error")
        return render_template('errors/500.html'), 500


@main_bp.route('/statistics')
@login_required
def statistics():
    """Statistics dashboard page"""
    try:
        return render_template('dashboard/statistics.html',
                              user=current_user,
                              page_title="Statistics")
    except Exception as e:
        logger.error(f"Statistics page error: {e}")
        flash("Error loading statistics", "error")
        return render_template('errors/500.html'), 500


# OAuth Configuration (stored in database or config)
OAUTH_PROVIDERS = {
    'google': {
        'enabled': False,  # Set to True when client_id is configured
        'client_id': '',
        'client_secret': '',
        'auth_url': 'https://accounts.google.com/o/oauth2/v2/auth',
        'token_url': 'https://oauth2.googleapis.com/token',
        'userinfo_url': 'https://www.googleapis.com/oauth2/v2/userinfo',
        'scope': 'email profile'
    },
    'facebook': {
        'enabled': False,
        'client_id': '',
        'client_secret': '',
        'auth_url': 'https://www.facebook.com/v18.0/dialog/oauth',
        'token_url': 'https://graph.facebook.com/v18.0/oauth/access_token',
        'userinfo_url': 'https://graph.facebook.com/me?fields=id,name,email',
        'scope': 'email,public_profile'
    },
    'discord': {
        'enabled': False,
        'client_id': '',
        'client_secret': '',
        'auth_url': 'https://discord.com/api/oauth2/authorize',
        'token_url': 'https://discord.com/api/oauth2/token',
        'userinfo_url': 'https://discord.com/api/users/@me',
        'scope': 'identify email'
    },
    'github': {
        'enabled': False,
        'client_id': '',
        'client_secret': '',
        'auth_url': 'https://github.com/login/oauth/authorize',
        'token_url': 'https://github.com/login/oauth/access_token',
        'userinfo_url': 'https://api.github.com/user',
        'scope': 'user:email'
    }
}


def get_oauth_config(provider):
    """Get OAuth configuration from database or fallback to defaults"""
    try:
        if api_bp.db_manager:
            session = api_bp.db_manager.get_session()
            try:
                from ..database.models import ConfigurationModel
                
                # Try to get OAuth config from database
                config_key = f'oauth_{provider}'
                config = session.query(ConfigurationModel).filter_by(key=config_key).first()
                
                if config:
                    config_data = config.get_typed_value()
                    if isinstance(config_data, dict):
                        # Start with hardcoded defaults, then override with database values
                        # This ensures database values take precedence
                        base_config = OAUTH_PROVIDERS.get(provider, {}).copy()
                        base_config.update({
                            'enabled': config_data.get('enabled', False),
                            'client_id': config_data.get('client_id', ''),
                            'client_secret': config_data.get('client_secret', ''),
                        })
                        logger.debug(f"OAuth config for {provider}: enabled={base_config.get('enabled')}, client_id={base_config.get('client_id')[:10] if base_config.get('client_id') else 'None'}...")
                        return base_config
            finally:
                session.close()
    except Exception as e:
        logger.warning(f"Failed to load OAuth config from database: {e}")
    
    # Fallback to hardcoded config
    return OAUTH_PROVIDERS.get(provider)


# OAuth Configuration Endpoint
@api_bp.route('/oauth/config/<provider>')
def get_oauth_provider_config(provider):
    """Get OAuth configuration for a provider (public endpoint)"""
    try:
        if provider not in OAUTH_PROVIDERS:
            return jsonify({"error": "Unknown provider"}), 404
        
        config = get_oauth_config(provider)
        
        # Only return safe public information
        return jsonify({
            "provider": provider,
            "enabled": config.get('enabled', False),
            "clientId": config.get('client_id', '') if config.get('enabled') else ''
        })
        
    except Exception as e:
        logger.error(f"OAuth config error: {e}")
        return jsonify({"error": str(e)}), 500


# OAuth Callback Endpoint
@api_bp.route('/oauth/callback/<provider>', methods=['GET', 'POST'])
def oauth_callback(provider):
    """Handle OAuth callback and create/login user
    
    GET: Handles redirect from OAuth provider (Google, Facebook, Discord)
    POST: Handles AJAX callback from frontend
    """
    try:
        import requests
        import base64
        
        if provider not in OAUTH_PROVIDERS:
            if request.method == 'GET':
                return f"""
                <html><body>
                <script>
                window.opener.postMessage({{error: 'Unknown provider'}}, '*');
                window.close();
                </script>
                </body></html>
                """
            return jsonify({"error": "Unknown provider"}), 404
        
        # Get code and state from either GET params or POST body
        if request.method == 'GET':
            code = request.args.get('code')
            encoded_state = request.args.get('state')
            # Default values
            state = encoded_state
            role = 'player'
            form_type = 'login'
            
            # Try to decode state parameter (may contain form_type and role)
            if encoded_state:
                try:
                    # State is base64 encoded JSON
                    state_json = base64.b64decode(encoded_state).decode('utf-8')
                    state_data = json.loads(state_json)
                    state = state_data.get('state', encoded_state)
                    form_type = state_data.get('form_type', 'login')
                    role = state_data.get('role', 'player')
                    logger.info(f"OAuth GET callback - decoded state: form_type={form_type}, role={role}")
                except Exception as e:
                    # If decoding fails, use defaults (legacy format)
                    logger.warning(f"OAuth GET callback - failed to decode state: {e}, using defaults")
        else:
            data = request.get_json() or {}
            code = data.get('code')
            state = data.get('state')
            role = data.get('role', 'player')
            form_type = data.get('form_type', 'login')
        
        if not code:
            if request.method == 'GET':
                return f"""
                <html><body>
                <script>
                window.opener.postMessage({{error: 'Authorization code is required'}}, '*');
                window.close();
                </script>
                </body></html>
                """
            return jsonify({"error": "Authorization code is required"}), 400
        
        config = get_oauth_config(provider)
        
        if not config.get('enabled') or not config.get('client_id'):
            error_msg = f"{provider} login is not enabled"
            if request.method == 'GET':
                return f"""
                <html><body>
                <script>
                window.opener.postMessage({{error: '{error_msg}'}}, '*');
                window.close();
                </script>
                </body></html>
                """
            return jsonify({"error": error_msg}), 400
        
        # Exchange code for access token
        redirect_uri = f"{request.host_url.rstrip('/')}/api/oauth/callback/{provider}"
        
        token_data = {
            'client_id': config['client_id'],
            'client_secret': config['client_secret'],
            'code': code,
            'redirect_uri': redirect_uri,
            'grant_type': 'authorization_code'
        }
        
        headers = {'Accept': 'application/json'}
        
        token_response = requests.post(config['token_url'], data=token_data, headers=headers, timeout=10)
        
        if not token_response.ok:
            logger.error(f"OAuth token exchange failed for {provider}: {token_response.text}")
            error_msg = "Failed to exchange authorization code"
            if request.method == 'GET':
                return f"""
                <html><body>
                <script>
                window.opener.postMessage({{error: '{error_msg}'}}, '*');
                window.close();
                </script>
                </body></html>
                """
            return jsonify({"error": error_msg}), 400
        
        token_json = token_response.json()
        access_token = token_json.get('access_token')
        
        if not access_token:
            logger.error(f"No access token in response from {provider}")
            error_msg = "Failed to obtain access token"
            if request.method == 'GET':
                return f"""
                <html><body>
                <script>
                window.opener.postMessage({{error: '{error_msg}'}}, '*');
                window.close();
                </script>
                </body></html>
                """
            return jsonify({"error": error_msg}), 400
        
        # Get user info from provider
        userinfo_headers = {'Authorization': f'Bearer {access_token}'}
        
        if provider == 'facebook':
            userinfo_headers = {'Authorization': f'Bearer {access_token}'}
        
        userinfo_response = requests.get(config['userinfo_url'], headers=userinfo_headers, timeout=10)
        
        if not userinfo_response.ok:
            logger.error(f"Failed to get user info from {provider}: {userinfo_response.text}")
            error_msg = "Failed to get user information"
            if request.method == 'GET':
                return f"""
                <html><body>
                <script>
                window.opener.postMessage({{error: '{error_msg}'}}, '*');
                window.close();
                </script>
                </body></html>
                """
            return jsonify({"error": error_msg}), 400
        
        userinfo = userinfo_response.json()
        
        # Extract user details based on provider
        if provider == 'google':
            provider_user_id = userinfo.get('id')
            email = userinfo.get('email')
            name = userinfo.get('name', '')
            username = email.split('@')[0] if email else f"google_{provider_user_id}"
            # Google provides picture URL
            avatar_url = userinfo.get('picture', '')
        elif provider == 'facebook':
            provider_user_id = userinfo.get('id')
            email = userinfo.get('email')
            name = userinfo.get('name', '')
            username = email.split('@')[0] if email else f"facebook_{provider_user_id}"
            # Facebook requires separate call for picture or use ID-based URL
            avatar_url = f"https://graph.facebook.com/{provider_user_id}/picture?type=large" if provider_user_id else ''
        elif provider == 'discord':
            provider_user_id = userinfo.get('id')
            email = userinfo.get('email')
            name = userinfo.get('global_name') or userinfo.get('username', '')
            username = userinfo.get('username') or (email.split('@')[0] if email else f"discord_{provider_user_id}")
            # Discord provides avatar hash, construct URL
            avatar_hash = userinfo.get('avatar')
            if avatar_hash and provider_user_id:
                avatar_url = f"https://cdn.discordapp.com/avatars/{provider_user_id}/{avatar_hash}.png?size=256"
            else:
                # Discord default avatar based on discriminator
                discriminator = userinfo.get('discriminator', '0')
                default_avatar_id = int(discriminator) % 5 if discriminator.isdigit() else 0
                avatar_url = f"https://cdn.discordapp.com/embed/avatars/{default_avatar_id}.png"
        elif provider == 'github':
            provider_user_id = str(userinfo.get('id'))
            # GitHub may not return email in user info, need to fetch separately
            email = userinfo.get('email')
            if not email:
                # Try to get primary email from GitHub emails API
                try:
                    emails_response = requests.get(
                        'https://api.github.com/user/emails',
                        headers={'Authorization': f'Bearer {access_token}'},
                        timeout=10
                    )
                    if emails_response.ok:
                        emails = emails_response.json()
                        # Find primary email
                        for email_info in emails:
                            if email_info.get('primary') and email_info.get('verified'):
                                email = email_info.get('email')
                                break
                except Exception as e:
                    logger.warning(f"Failed to fetch GitHub emails: {e}")
            name = userinfo.get('name') or userinfo.get('login', '')
            username = userinfo.get('login') or (email.split('@')[0] if email else f"github_{provider_user_id}")
            # GitHub provides avatar_url directly
            avatar_url = userinfo.get('avatar_url', '')
        else:
            error_msg = "Unsupported provider"
            if request.method == 'GET':
                return f"""
                <html><body>
                <script>
                window.opener.postMessage({{error: '{error_msg}'}}, '*');
                window.close();
                </script>
                </body></html>
                """
            return jsonify({"error": error_msg}), 400
        
        if not email:
            error_msg = "Email is required for signup"
            if request.method == 'GET':
                return f"""
                <html><body>
                <script>
                window.opener.postMessage({{error: '{error_msg}'}}, '*');
                window.close();
                </script>
                </body></html>
                """
            return jsonify({"error": error_msg}), 400
        
        # Check if user exists by email
        session = api_bp.db_manager.get_session()
        try:
            from ..database.models import UserModel
            from ..database.manager import DatabaseManager
            
            user = session.query(UserModel).filter_by(email=email).first()
            
            if user:
                # User exists
                if form_type == 'signup':
                    # Signup form - user already exists, return error
                    error_msg = "An account with this email already exists. Please login instead."
                    if request.method == 'GET':
                        return f"""
                        <html><body>
                        <script>
                        window.opener.postMessage({{error: '{error_msg}'}}, '*');
                        window.close();
                        </script>
                        </body></html>
                        """
                    return jsonify({"error": error_msg}), 409
                else:
                    # Login form - log them in
                    authenticated_user = AuthenticatedUser(
                        user_id=user.id,
                        username=user.username,
                        email=user.email,
                        is_admin=user.is_admin,
                        role=getattr(user, 'role', 'normal')
                    )
            else:
                # User doesn't exist
                # Only create new user if this is a signup form
                if form_type != 'signup':
                    error_msg = "No account found with this email. Please sign up first."
                    if request.method == 'GET':
                        return f"""
                        <html><body>
                        <script>
                        window.opener.postMessage({{error: '{error_msg}'}}, '*');
                        window.close();
                        </script>
                        </body></html>
                        """
                    return jsonify({"error": error_msg}), 404
                
                # Create new user (signup form)
                # Generate username from real name if available
                # Format: "Silvio Antani" -> "silvio.antani"
                if name:
                    # Sanitize: lowercase, replace spaces with dots, remove special chars
                    import re
                    base_username = re.sub(r'[^a-z0-9.]', '', name.lower().replace(' ', '.'))
                    # Remove consecutive dots and leading/trailing dots
                    base_username = re.sub(r'\.+', '.', base_username).strip('.')
                    if not base_username:
                        base_username = username  # Fallback to original username
                else:
                    base_username = username
                
                username = base_username
                counter = 1
                while session.query(UserModel).filter_by(username=username).first():
                    username = f"{base_username}{counter}"
                    counter += 1
                
                # Create user with OAuth info
                # Generate a random password (user won't need it for OAuth login)
                random_password = secrets.token_urlsafe(32)
                password_hash = auth_bp.auth_manager.hash_password(random_password)
                
                # Generate Gravatar URL as fallback for avatar
                import hashlib
                gravatar_hash = hashlib.md5(email.lower().strip().encode()).hexdigest()
                gravatar_url = f"https://www.gravatar.com/avatar/{gravatar_hash}?d=identicon&s=256"
                
                # Use OAuth avatar if available, otherwise use Gravatar
                final_avatar_url = avatar_url if avatar_url else gravatar_url
                
                new_user = UserModel(
                    username=username,
                    email=email,
                    password_hash=password_hash,
                    real_name=name,  # Store the real name from OAuth
                    avatar_url=final_avatar_url,  # Store avatar URL
                    is_admin=False,
                    role=role,
                    email_verified=True,  # OAuth emails are verified
                    created_at=datetime.utcnow()
                )
                session.add(new_user)
                session.commit()
                
                authenticated_user = AuthenticatedUser(
                    user_id=new_user.id,
                    username=new_user.username,
                    email=new_user.email,
                    is_admin=new_user.is_admin,
                    role=getattr(new_user, 'role', 'player')
                )
                
                logger.info(f"Created new user via {provider} OAuth: {username}")
            
            # Update user's real_name and avatar if they don't have one (for existing users)
            if user and not getattr(user, 'real_name', None):
                user.real_name = name
            if user and not getattr(user, 'avatar_url', None):
                import hashlib
                gravatar_hash = hashlib.md5(email.lower().strip().encode()).hexdigest()
                gravatar_url = f"https://www.gravatar.com/avatar/{gravatar_hash}?d=identicon&s=256"
                user.avatar_url = avatar_url if avatar_url else gravatar_url
            if user:
                session.commit()
            
            # Create JWT token with refresh token for persistent sessions
            token_data = auth_bp.auth_manager.create_jwt_token(authenticated_user.id, include_refresh=True)
            
            # Get the full user data including real_name and avatar_url
            user_data = {
                'id': authenticated_user.id,
                'username': authenticated_user.username,
                'email': authenticated_user.email,
                'role': authenticated_user.role,
                'is_admin': authenticated_user.is_admin,
                'real_name': name or authenticated_user.username,
                'avatar_url': final_avatar_url if not user else (getattr(user, 'avatar_url', None) or avatar_url or f"https://www.gravatar.com/avatar/{hashlib.md5(email.lower().strip().encode()).hexdigest()}?d=identicon&s=256")
            }
            
            # Return response based on request method
            if request.method == 'GET':
                # For GET requests, return HTML that posts message to parent window and closes
                user_dict = user_data
                return f"""
                <html>
                <head><title>OAuth Callback</title></head>
                <body>
                <p>Authentication successful! You can close this window.</p>
                <script>
                (function() {{
                    const data = {{
                        success: true,
                        message: 'Login successful',
                        token: '{token_data["access_token"]}',
                        user: {json.dumps(user_dict)}
                    }};
                    // Try to post to opener
                    if (window.opener) {{
                        window.opener.postMessage(data, '*');
                        window.close();
                    }} else {{
                        // If no opener, redirect to main page with token
                        localStorage.setItem('auth_token', '{token_data["access_token"]}');
                        localStorage.setItem('user', JSON.stringify({json.dumps(user_dict)}));
                        window.location.href = '/';
                    }}
                }})();
                </script>
                </body>
                </html>
                """
            return jsonify({
                "success": True,
                "message": "Login successful",
                "access_token": token_data['access_token'],
                "expires_in": token_data['expires_in'],
                "token_type": token_data['token_type'],
                "refresh_token": token_data['refresh_token'],
                "user": user_data
            })
            
        finally:
            session.close()
        
    except Exception as e:
        logger.error(f"OAuth callback error: {e}")
        import traceback
        traceback.print_exc()
        if request.method == 'GET':
            return f"""
            <html><body>
            <script>
            window.opener.postMessage({{error: '{str(e).replace("'", "\\'")}'}}, '*');
            window.close();
            </script>
            </body></html>
            """
        return jsonify({"error": str(e)}), 500


# OAuth Settings Management Endpoints (Admin Only)
@api_bp.route('/oauth-settings')
@get_api_auth_decorator(require_admin=True)
def get_oauth_settings():
    """Get OAuth settings for all providers (admin only)"""
    try:
        settings = {}
        
        for provider in ['google', 'facebook', 'discord']:
            config = get_oauth_config(provider)
            settings[provider] = {
                'enabled': config.get('enabled', False),
                'client_id': config.get('client_id', ''),
                'client_secret': config.get('client_secret', '')
            }
        
        return jsonify({
            "success": True,
            "settings": settings
        })
        
    except Exception as e:
        logger.error(f"Get OAuth settings error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/oauth-settings', methods=['POST'])
@get_api_auth_decorator(require_admin=True)
def save_oauth_settings():
    """Save OAuth settings for all providers (admin only)"""
    try:
        from ..database.models import ConfigurationModel
        
        data = request.get_json() or {}
        
        if not data:
            return jsonify({"error": "No settings provided"}), 400
        
        session = api_bp.db_manager.get_session()
        try:
            for provider in ['google', 'facebook', 'discord']:
                if provider in data:
                    provider_config = data[provider]
                    
                    config_key = f'oauth_{provider}'
                    config_value = {
                        'enabled': provider_config.get('enabled', False),
                        'client_id': provider_config.get('client_id', ''),
                        'client_secret': provider_config.get('client_secret', '')
                    }
                    
                    # Find existing config or create new
                    config = session.query(ConfigurationModel).filter_by(key=config_key).first()
                    
                    if config:
                        config.value = json.dumps(config_value)
                        config.updated_at = datetime.utcnow()
                    else:
                        config = ConfigurationModel(
                            key=config_key,
                            value=json.dumps(config_value),
                            value_type='json',
                            description=f'OAuth configuration for {provider}',
                            is_system=True
                        )
                        session.add(config)
            
            session.commit()
            logger.info("OAuth settings saved successfully")
            
            return jsonify({
                "success": True,
                "message": "OAuth settings saved successfully"
            })
            
        finally:
            session.close()
        
    except Exception as e:
        logger.error(f"Save OAuth settings error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/admin/oauth/providers/<provider_name>', methods=['PUT'])
@get_api_auth_decorator(require_admin=True)
def save_oauth_provider_settings(provider_name):
    """Save OAuth settings for a specific provider (admin only)"""
    try:
        from ..database.models import ConfigurationModel
        
        if provider_name not in ['google', 'facebook', 'discord', 'github']:
            return jsonify({"error": "Invalid provider name"}), 400
        
        data = request.get_json() or {}
        
        if not data:
            return jsonify({"error": "No settings provided"}), 400
        
        session = api_bp.db_manager.get_session()
        try:
            config_key = f'oauth_{provider_name}'
            
            # Get existing config to preserve client_secret if not provided
            existing_config = session.query(ConfigurationModel).filter_by(key=config_key).first()
            existing_secret = ''
            if existing_config:
                existing_data = existing_config.get_typed_value()
                if isinstance(existing_data, dict):
                    existing_secret = existing_data.get('client_secret', '')
            
            # Use is_active from form (template sends is_active, not enabled)
            enabled = data.get('is_active', data.get('enabled', False))
            
            # Only update client_secret if a new one is provided
            client_secret = data.get('client_secret', '')
            if not client_secret and existing_secret:
                # Keep existing secret if no new one provided
                client_secret = existing_secret
            
            config_value = {
                'enabled': enabled,
                'client_id': data.get('client_id', ''),
                'client_secret': client_secret
            }
            
            # Find existing config or create new
            config = session.query(ConfigurationModel).filter_by(key=config_key).first()
            
            if config:
                config.value = json.dumps(config_value)
                config.updated_at = datetime.utcnow()
            else:
                config = ConfigurationModel(
                    key=config_key,
                    value=json.dumps(config_value),
                    value_type='json',
                    description=f'OAuth configuration for {provider_name}',
                    is_system=True
                )
                session.add(config)
        
            session.commit()
            logger.info(f"OAuth settings saved for {provider_name}: enabled={enabled}")
            
            return jsonify({
                "success": True,
                "message": f"{provider_name.capitalize()} OAuth settings saved successfully",
                "provider_name": provider_name
            })
            
        finally:
            session.close()
        
    except Exception as e:
        logger.error(f"Save OAuth provider settings error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/oauth/providers')
@get_api_auth_decorator(require_admin=True)
def get_oauth_providers():
    """Get all OAuth provider configurations (admin only)"""
    try:
        from ..database.models import ConfigurationModel
        
        # Return as array for template compatibility
        providers = []
        provider_names = ['google', 'facebook', 'discord', 'github']
        
        if api_bp.db_manager:
            session = api_bp.db_manager.get_session()
            try:
                for provider_name in provider_names:
                    config_key = f'oauth_{provider_name}'
                    db_config = session.query(ConfigurationModel).filter_by(key=config_key).first()
                    
                    provider_data = {
                        'name': provider_name,
                        'client_id': '',
                        'has_client_secret': False,
                        'is_configured': False,
                        'enabled': False
                    }
                    
                    if db_config:
                        config_data = db_config.get_typed_value()
                        if isinstance(config_data, dict):
                            provider_data['enabled'] = config_data.get('enabled', False)
                            provider_data['client_id'] = config_data.get('client_id', '')
                            provider_data['has_client_secret'] = bool(config_data.get('client_secret'))
                            provider_data['is_configured'] = bool(config_data.get('client_id') and config_data.get('client_secret'))
                    
                    providers.append(provider_data)
            finally:
                session.close()
        else:
            # Return default empty config if no db_manager
            for provider_name in provider_names:
                providers.append({
                    'name': provider_name,
                    'client_id': '',
                    'has_client_secret': False,
                    'is_configured': False,
                    'enabled': False
                })
        
        return jsonify({
            'success': True,
            'providers': providers
        })
        
    except Exception as e:
        logger.error(f"Get OAuth providers error: {e}")
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500


# Public OAuth Configuration Endpoint (No Auth Required)
@api_bp.route('/oauth-config')
def get_oauth_config_public():
    """Get OAuth configuration for frontend (public endpoint)
    
    Returns enabled status and clientId for each OAuth provider.
    The clientId is safe to expose publicly as it's only used to identify the app.
    """
    try:
        from ..database.models import ConfigurationModel
        
        config = {
            'google': {'enabled': False},
            'facebook': {'enabled': False},
            'discord': {'enabled': False},
            'github': {'enabled': False}
        }
        
        if api_bp.db_manager:
            session = api_bp.db_manager.get_session()
            try:
                # Try to get OAuth config from database for each provider
                for provider in ['google', 'facebook', 'discord', 'github']:
                    config_key = f'oauth_{provider}'
                    db_config = session.query(ConfigurationModel).filter_by(key=config_key).first()
                    
                    if db_config:
                        config_data = db_config.get_typed_value()
                        if isinstance(config_data, dict):
                            enabled = config_data.get('enabled', False)
                            client_id = config_data.get('client_id', '')
                            config[provider] = {
                                'enabled': enabled,
                                'clientId': client_id if enabled else ''  # Only expose clientId if enabled
                            }
            finally:
                session.close()
        
        return jsonify(config)
        
    except Exception as e:
        logger.error(f"Get OAuth config public error: {e}")
        # Return default config on error
        return jsonify({
            'google': {'enabled': False},
            'facebook': {'enabled': False},
            'discord': {'enabled': False},
            'github': {'enabled': False}
        })


# Email Settings Management Endpoints (Admin Only)
@api_bp.route('/email-settings')
@get_api_auth_decorator(require_admin=True)
def get_email_settings():
    """Get email/SMTP settings (admin only)"""
    try:
        from ..database.models import ConfigurationModel
        
        settings = {
            'enabled': False,
            'smtp_host': 'smtp.gmail.com',
            'smtp_port': 587,
            'smtp_username': '',
            'smtp_password': '',
            'smtp_from_email': '',
            'smtp_from_name': 'MbetterClient',
            'app_domain': '',
            'smtp_use_tls': 'tls'
        }
        
        if api_bp.db_manager:
            session = api_bp.db_manager.get_session()
            try:
                # Try to get email config from database
                config = session.query(ConfigurationModel).filter_by(key='email_settings').first()
                
                if config:
                    config_data = config.get_typed_value()
                    if isinstance(config_data, dict):
                        settings.update(config_data)
            finally:
                session.close()
        
        return jsonify({
            "success": True,
            "settings": settings
        })
        
    except Exception as e:
        logger.error(f"Get email settings error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/email-settings', methods=['POST'])
@get_api_auth_decorator(require_admin=True)
def save_email_settings():
    """Save email/SMTP settings (admin only)"""
    try:
        from ..database.models import ConfigurationModel
        
        data = request.get_json() or {}
        
        if not data:
            return jsonify({"error": "No settings provided"}), 400
        
        session = api_bp.db_manager.get_session()
        try:
            config_key = 'email_settings'
            config_value = {
                'enabled': data.get('enabled', False),
                'smtp_host': data.get('smtp_host', 'smtp.gmail.com'),
                'smtp_port': data.get('smtp_port', 587),
                'smtp_username': data.get('smtp_username', ''),
                'smtp_password': data.get('smtp_password', ''),
                'smtp_from_email': data.get('smtp_from_email', ''),
                'smtp_from_name': data.get('smtp_from_name', 'MbetterClient'),
                'app_domain': data.get('app_domain', ''),
                'smtp_use_tls': data.get('smtp_use_tls', 'tls')
            }
            
            # Find existing config or create new
            config = session.query(ConfigurationModel).filter_by(key=config_key).first()
            
            if config:
                config.value = json.dumps(config_value)
                config.updated_at = datetime.utcnow()
            else:
                config = ConfigurationModel(
                    key=config_key,
                    value=json.dumps(config_value),
                    value_type='json',
                    description='Email/SMTP configuration',
                    is_system=True
                )
                session.add(config)
            
            session.commit()
            logger.info("Email settings saved successfully")
            
            return jsonify({
                "success": True,
                "message": "Email settings saved successfully"
            })
            
        finally:
            session.close()
        
    except Exception as e:
        logger.error(f"Save email settings error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/email-settings/test', methods=['POST'])
@get_api_auth_decorator(require_admin=True)
def test_email_settings():
    """Send a test email to verify SMTP configuration (admin only)"""
    try:
        import smtplib
        from email.mime.text import MIMEText
        from email.mime.multipart import MIMEMultipart
        
        # Get email settings
        settings_response = get_email_settings()
        if settings_response[1] != 200:
            return jsonify({"error": "Failed to load email settings"}), 500
        
        settings_data = settings_response[0].get_json()
        settings = settings_data.get('settings', {})
        
        if not settings.get('enabled'):
            return jsonify({"error": "Email services are not enabled"}), 400
        
        # Get current admin user's email
        if not current_user.is_authenticated:
            return jsonify({"error": "Not authenticated"}), 401
        
        to_email = getattr(current_user, 'email', None)
        if not to_email:
            return jsonify({"error": "No email address associated with your account"}), 400
        
        # Create test email
        msg = MIMEMultipart('alternative')
        msg['Subject'] = 'MbetterClient Email Test'
        msg['From'] = f"{settings.get('smtp_from_name', 'MbetterClient')} <{settings.get('smtp_from_email')}>"
        msg['To'] = to_email
        
        html_content = f"""
        <html>
        <body>
            <h2>Email Test Successful</h2>
            <p>This is a test email from MbetterClient.</p>
            <p>If you received this email, your SMTP configuration is working correctly.</p>
            <hr>
            <p><small>Sent at: {datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S UTC')}</small></p>
        </body>
        </html>
        """
        
        msg.attach(MIMEText(html_content, 'html'))
        
        # Send email
        smtp_host = settings.get('smtp_host')
        smtp_port = settings.get('smtp_port', 587)
        smtp_username = settings.get('smtp_username')
        smtp_password = settings.get('smtp_password')
        smtp_use_tls = settings.get('smtp_use_tls', 'tls')
        
        if smtp_use_tls == 'ssl':
            server = smtplib.SMTP_SSL(smtp_host, smtp_port, timeout=10)
        else:
            server = smtplib.SMTP(smtp_host, smtp_port, timeout=10)
            if smtp_use_tls == 'tls':
                server.starttls()
        
        server.login(smtp_username, smtp_password)
        server.send_message(msg)
        server.quit()
        
        logger.info(f"Test email sent successfully to {to_email}")
        
        return jsonify({
            "success": True,
            "message": f"Test email sent successfully to {to_email}"
        })
        
    except smtplib.SMTPAuthenticationError as e:
        logger.error(f"SMTP authentication error: {e}")
        return jsonify({"error": "SMTP authentication failed. Check your username and password."}), 400
    except smtplib.SMTPException as e:
        logger.error(f"SMTP error: {e}")
        return jsonify({"error": f"SMTP error: {str(e)}"}), 400
    except Exception as e:
        logger.error(f"Test email error: {e}")
        return jsonify({"error": str(e)}), 500


# Email Verification and Password Reset Endpoints (Public)
@api_bp.route('/auth/send-verification', methods=['POST'])
def send_verification_email():
    """Send email verification link to user"""
    try:
        import smtplib
        from email.mime.text import MIMEText
        from email.mime.multipart import MIMEMultipart
        import secrets
        
        data = request.get_json() or {}
        email = data.get('email', '').strip().lower()
        
        if not email:
            return jsonify({"error": "Email is required"}), 400
        
        # Get email settings
        settings_response = get_email_settings()
        if settings_response[1] != 200:
            return jsonify({"error": "Email services not configured"}), 500
        
        settings_data = settings_response[0].get_json()
        settings = settings_data.get('settings', {})
        
        if not settings.get('enabled'):
            return jsonify({"error": "Email services are not enabled"}), 400
        
        # Check if user exists
        session = api_bp.db_manager.get_session()
        try:
            from ..database.models import UserModel, EmailVerificationModel
            
            user = session.query(UserModel).filter_by(email=email).first()
            
            if not user:
                return jsonify({"error": "User not found"}), 404
            
            # Generate verification token
            token = secrets.token_urlsafe(32)
            expires_at = datetime.utcnow() + timedelta(hours=24)
            
            # Store verification token
            verification = EmailVerificationModel(
                user_id=user.id,
                email=email,
                token=token,
                token_type='email_verification',
                expires_at=expires_at
            )
            session.add(verification)
            session.commit()
            
            # Create verification link
            app_domain = settings.get('app_domain', '').rstrip('/')
            verification_link = f"{app_domain}/verify-email?token={token}"
            
            # Create email
            msg = MIMEMultipart('alternative')
            msg['Subject'] = 'Verify Your Email Address'
            msg['From'] = f"{settings.get('smtp_from_name', 'MbetterClient')} <{settings.get('smtp_from_email')}>"
            msg['To'] = email
            
            html_content = f"""
            <html>
            <body style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
                <h2 style="color: #333;">Verify Your Email Address</h2>
                <p>Hi {user.username},</p>
                <p>Thank you for signing up for MbetterClient. Please verify your email address by clicking the button below:</p>
                <p style="text-align: center; margin: 30px 0;">
                    <a href="{verification_link}" style="background-color: #007bff; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px; display: inline-block;">Verify Email</a>
                </p>
                <p>Or copy and paste this link into your browser:</p>
                <p style="word-break: break-all; color: #666;">{verification_link}</p>
                <p style="color: #666; font-size: 12px;">This link will expire in 24 hours.</p>
                <hr style="margin: 30px 0; border: none; border-top: 1px solid #eee;">
                <p style="color: #999; font-size: 12px;">If you didn't create an account, please ignore this email.</p>
            </body>
            </html>
            """
            
            msg.attach(MIMEText(html_content, 'html'))
            
            # Send email
            smtp_host = settings.get('smtp_host')
            smtp_port = settings.get('smtp_port', 587)
            smtp_username = settings.get('smtp_username')
            smtp_password = settings.get('smtp_password')
            smtp_use_tls = settings.get('smtp_use_tls', 'tls')
            
            if smtp_use_tls == 'ssl':
                server = smtplib.SMTP_SSL(smtp_host, smtp_port, timeout=10)
            else:
                server = smtplib.SMTP(smtp_host, smtp_port, timeout=10)
                if smtp_use_tls == 'tls':
                    server.starttls()
            
            server.login(smtp_username, smtp_password)
            server.send_message(msg)
            server.quit()
            
            logger.info(f"Verification email sent to {email}")
            
            return jsonify({
                "success": True,
                "message": "Verification email sent successfully"
            })
            
        finally:
            session.close()
        
    except Exception as e:
        logger.error(f"Send verification email error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/auth/verify-email', methods=['POST'])
def verify_email():
    """Verify email using token"""
    try:
        data = request.get_json() or {}
        token = data.get('token', '')
        
        if not token:
            return jsonify({"error": "Token is required"}), 400
        
        session = api_bp.db_manager.get_session()
        try:
            from ..database.models import EmailVerificationModel, UserModel
            
            # Find verification token
            verification = session.query(EmailVerificationModel).filter_by(
                token=token,
                token_type='email_verification'
            ).first()
            
            if not verification:
                return jsonify({"error": "Invalid or expired token"}), 400
            
            # Check if token is expired
            if verification.expires_at < datetime.utcnow():
                session.delete(verification)
                session.commit()
                return jsonify({"error": "Token has expired"}), 400
            
            # Get user
            user = session.query(UserModel).filter_by(id=verification.user_id).first()
            if not user:
                return jsonify({"error": "User not found"}), 404
            
            # Mark user as verified
            user.email_verified = True
            user.email_verified_at = datetime.utcnow()
            
            # Delete used token
            session.delete(verification)
            session.commit()
            
            logger.info(f"Email verified for user {user.username}")
            
            return jsonify({
                "success": True,
                "message": "Email verified successfully"
            })
            
        finally:
            session.close()
        
    except Exception as e:
        logger.error(f"Verify email error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/auth/forgot-password', methods=['POST'])
def forgot_password():
    """Send password reset link to user"""
    try:
        import smtplib
        from email.mime.text import MIMEText
        from email.mime.multipart import MIMEMultipart
        import secrets
        
        data = request.get_json() or {}
        email = data.get('email', '').strip().lower()
        
        if not email:
            return jsonify({"error": "Email is required"}), 400
        
        # Get email settings
        settings_response = get_email_settings()
        if settings_response[1] != 200:
            return jsonify({"error": "Email services not configured"}), 500
        
        settings_data = settings_response[0].get_json()
        settings = settings_data.get('settings', {})
        
        if not settings.get('enabled'):
            return jsonify({"error": "Email services are not enabled"}), 400
        
        # Check if user exists
        session = api_bp.db_manager.get_session()
        try:
            from ..database.models import UserModel, EmailVerificationModel
            
            user = session.query(UserModel).filter_by(email=email).first()
            
            if not user:
                # Don't reveal if user exists or not for security
                return jsonify({
                    "success": True,
                    "message": "If an account exists with this email, a password reset link has been sent"
                })
            
            # Generate reset token
            token = secrets.token_urlsafe(32)
            expires_at = datetime.utcnow() + timedelta(hours=1)
            
            # Store reset token
            reset = EmailVerificationModel(
                user_id=user.id,
                email=email,
                token=token,
                token_type='password_reset',
                expires_at=expires_at
            )
            session.add(reset)
            session.commit()
            
            # Create reset link
            app_domain = settings.get('app_domain', '').rstrip('/')
            reset_link = f"{app_domain}/reset-password?token={token}"
            
            # Create email
            msg = MIMEMultipart('alternative')
            msg['Subject'] = 'Reset Your Password'
            msg['From'] = f"{settings.get('smtp_from_name', 'MbetterClient')} <{settings.get('smtp_from_email')}>"
            msg['To'] = email
            
            html_content = f"""
            <html>
            <body style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
                <h2 style="color: #333;">Reset Your Password</h2>
                <p>Hi {user.username},</p>
                <p>We received a request to reset your password. Click the button below to create a new password:</p>
                <p style="text-align: center; margin: 30px 0;">
                    <a href="{reset_link}" style="background-color: #007bff; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px; display: inline-block;">Reset Password</a>
                </p>
                <p>Or copy and paste this link into your browser:</p>
                <p style="word-break: break-all; color: #666;">{reset_link}</p>
                <p style="color: #666; font-size: 12px;">This link will expire in 1 hour.</p>
                <hr style="margin: 30px 0; border: none; border-top: 1px solid #eee;">
                <p style="color: #999; font-size: 12px;">If you didn't request a password reset, please ignore this email.</p>
            </body>
            </html>
            """
            
            msg.attach(MIMEText(html_content, 'html'))
            
            # Send email
            smtp_host = settings.get('smtp_host')
            smtp_port = settings.get('smtp_port', 587)
            smtp_username = settings.get('smtp_username')
            smtp_password = settings.get('smtp_password')
            smtp_use_tls = settings.get('smtp_use_tls', 'tls')
            
            if smtp_use_tls == 'ssl':
                server = smtplib.SMTP_SSL(smtp_host, smtp_port, timeout=10)
            else:
                server = smtplib.SMTP(smtp_host, smtp_port, timeout=10)
                if smtp_use_tls == 'tls':
                    server.starttls()
            
            server.login(smtp_username, smtp_password)
            server.send_message(msg)
            server.quit()
            
            logger.info(f"Password reset email sent to {email}")
            
            return jsonify({
                "success": True,
                "message": "If an account exists with this email, a password reset link has been sent"
            })
            
        finally:
            session.close()
        
    except Exception as e:
        logger.error(f"Forgot password error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/auth/reset-password', methods=['POST'])
def reset_password():
    """Reset password using token"""
    try:
        data = request.get_json() or {}
        token = data.get('token', '')
        new_password = data.get('new_password', '')
        
        if not token:
            return jsonify({"error": "Token is required"}), 400
        
        if not new_password or len(new_password) < 6:
            return jsonify({"error": "Password must be at least 6 characters"}), 400
        
        session = api_bp.db_manager.get_session()
        try:
            from ..database.models import EmailVerificationModel, UserModel
            
            # Find reset token
            reset = session.query(EmailVerificationModel).filter_by(
                token=token,
                token_type='password_reset'
            ).first()
            
            if not reset:
                return jsonify({"error": "Invalid or expired token"}), 400
            
            # Check if token is expired
            if reset.expires_at < datetime.utcnow():
                session.delete(reset)
                session.commit()
                return jsonify({"error": "Token has expired"}), 400
            
            # Get user
            user = session.query(UserModel).filter_by(id=reset.user_id).first()
            if not user:
                return jsonify({"error": "User not found"}), 404
            
            # Update password
            password_hash = auth_bp.auth_manager.hash_password(new_password)
            user.password_hash = password_hash
            
            # Delete used token
            session.delete(reset)
            session.commit()
            
            logger.info(f"Password reset for user {user.username}")
            
            return jsonify({
                "success": True,
                "message": "Password reset successfully"
            })
            
        finally:
            session.close()
        
    except Exception as e:
        logger.error(f"Reset password error: {e}")
        return jsonify({"error": str(e)}), 500


# API routes
@api_bp.route('/status')
@get_api_auth_decorator()
def system_status():
    """Get system status"""
    try:
        status = api_bp.api.get_system_status()
        return jsonify(status)
    except Exception as e:
        logger.error(f"API status error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/settings')
@get_api_auth_decorator()
def get_app_settings():
    """Get application settings including edit_odds flag"""
    try:
        settings = getattr(api_bp, 'settings', None)
        if not settings:
            return jsonify({"error": "Settings not available"}), 500
        
        return jsonify({
            "edit_odds": getattr(settings, 'edit_odds', False)
        })
    except Exception as e:
        logger.error(f"API settings error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/debug/match-status')
@get_api_auth_decorator(require_admin=True)
def debug_match_status():
    """Get debug information about match statuses"""
    try:
        fixture_id = request.args.get('fixture_id')
        debug_data = api_bp.api.get_debug_match_status(fixture_id)
        return jsonify(debug_data)
    except Exception as e:
        logger.error(f"API debug match status error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/video/status')
@get_api_auth_decorator()
def video_status():
    """Get video player status"""
    try:
        status = api_bp.api.get_video_status()
        return jsonify(status)
    except Exception as e:
        logger.error(f"API video status error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/video/control', methods=['POST'])
@get_api_auth_decorator()
def video_control():
    """Control video player with enhanced looping support"""
    try:
        data = request.get_json() or {}
        action = data.get('action')
        
        if not action:
            return jsonify({"error": "Action is required"}), 400
        
        # Enhanced actions for looping functionality
        if action == 'play_loop':
            # Start looping playback with specified video and template
            filename = data.get('filename')
            file_path = data.get('file_path')  # Alternative parameter name
            template = data.get('template', 'text')
            
            # Use file_path if filename is not provided
            if not filename and file_path:
                filename = file_path
            
            if not filename:
                return jsonify({"error": "Filename or file_path is required for play_loop action"}), 400
            
            # Send loop play message to Qt player with explicit looping parameters
            control_data = {
                'file_path': filename,
                'template': template,
                'loop': True,
                'repeat': True,
                'loop_count': -1  # Infinite loop
            }
            result = api_bp.api.control_video('play_loop', **control_data)
            
            if result.get('success'):
                logger.info(f"Started infinite looping playback: {filename} with template {template}")
                return jsonify({
                    "success": True,
                    "message": f"Started infinite looping playback of {filename}",
                    "filename": filename,
                    "template": template,
                    "loop": True,
                    "loop_count": -1
                })
            else:
                return jsonify({
                    "success": False,
                    "error": result.get('error', 'Failed to start looping playback')
                }), 500
                
        elif action == 'stop_loop':
            # Stop looping playback
            result = api_bp.api.control_video('stop_loop')
            
            if result.get('success'):
                logger.info("Stopped looping playback")
                return jsonify({
                    "success": True,
                    "message": "Stopped looping playback"
                })
            else:
                return jsonify({
                    "success": False,
                    "error": result.get('error', 'Failed to stop playback')
                }), 500
                
        elif action == 'change_template':
            # Change template while video is playing (for real-time switching)
            template = data.get('template')
            
            if not template:
                return jsonify({"error": "Template is required for change_template action"}), 400
            
            # Send template change to Qt player via control_video
            control_data = {
                'template': template,
                'reload_template': True,
                'load_specific_template': template
            }
            result = api_bp.api.control_video('template_change', **control_data)
            
            # Also update overlay directly as fallback
            overlay_result = api_bp.api.update_overlay(template, {})
            
            if result.get('success') or overlay_result.get('success'):
                logger.info(f"Changed template to {template} during playback")
                return jsonify({
                    "success": True,
                    "message": f"Template changed to {template}",
                    "template": template
                })
            else:
                return jsonify({
                    "success": False,
                    "error": result.get('error', 'Failed to change template')
                }), 500
                
        else:
            # Handle existing video control actions (play, pause, stop, etc.)
            control_data = {k: v for k, v in data.items() if k != 'action'}
            result = api_bp.api.control_video(action, **control_data)
            return jsonify(result)
        
    except Exception as e:
        logger.error(f"API video control error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/overlay', methods=['POST'])
@get_api_auth_decorator()
def update_overlay():
    """Update video overlay"""
    try:
        data = request.get_json() or {}
        template = data.get('template')
        overlay_data = data.get('data', {})
        
        if not template:
            return jsonify({"error": "Template is required"}), 400
        
        result = api_bp.api.update_overlay(template, overlay_data)
        return jsonify(result)
        
    except Exception as e:
        logger.error(f"API overlay update error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/templates')
@get_api_auth_decorator()
def get_templates():
    """Get available templates"""
    try:
        templates = api_bp.api.get_templates()
        return jsonify(templates)
    except Exception as e:
        logger.error(f"API get templates error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/config')
@get_api_auth_decorator()
def get_configuration():
    """Get configuration"""
    try:
        section = request.args.get('section')
        config = api_bp.api.get_configuration(section)
        return jsonify(config)
    except Exception as e:
        logger.error(f"API get configuration error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/config/<section>')
@get_api_auth_decorator()
def get_config_section(section):
    """Get configuration section"""
    try:
        config = api_bp.api.get_configuration(section)
        return jsonify(config)
    except Exception as e:
        logger.error(f"API get config section error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/config/<section>', methods=['POST'])
@get_api_auth_decorator()
@get_api_auth_decorator(require_admin=True)
def update_config_section(section):
    """Update configuration section"""
    try:
        data = request.get_json() or {}
        result = api_bp.api.update_configuration(section, data)
        
        # If updating API configuration, notify API client to reload
        if section == "api" and result.get("success", False):
            try:
                from ..core.message_bus import MessageBuilder, MessageType
                if api_bp.message_bus:
                    config_update_message = MessageBuilder.config_update(
                        sender="web_dashboard",
                        config_section="api",
                        config_data=data
                    )
                    api_bp.message_bus.publish(config_update_message)
                    logger.info("API configuration update message sent to message bus")
            except Exception as msg_e:
                logger.warning(f"Failed to send config update message: {msg_e}")
        
        return jsonify(result)
    except Exception as e:
        logger.error(f"API update config section error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/config', methods=['POST'])
@get_api_auth_decorator()
@get_api_auth_decorator(require_admin=True)
def update_configuration():
    """Update configuration"""
    try:
        data = request.get_json() or {}
        section = data.get('section')
        config_data = data.get('config')
        
        if not section or not config_data:
            return jsonify({"error": "Section and config data are required"}), 400
        
        result = api_bp.api.update_configuration(section, config_data)
        return jsonify(result)
        
    except Exception as e:
        logger.error(f"API update configuration error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/config/match-interval')
@get_api_auth_decorator()
def get_match_interval():
    """Get match interval configuration"""
    try:
        if api_bp.config_manager:
            general_config = api_bp.config_manager.get_section_config("general") or {}
            match_interval = general_config.get('match_interval', 5)  # Default 5 minutes
        else:
            match_interval = 5  # Default fallback

        return jsonify({
            "success": True,
            "match_interval": match_interval
        })
    except Exception as e:
        logger.error(f"API get match interval error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/config/match-interval', methods=['POST'])
@get_api_auth_decorator()
def set_match_interval():
    """Set match interval configuration"""
    try:
        data = request.get_json() or {}
        match_interval = data.get('match_interval')

        if match_interval is None:
            return jsonify({"error": "match_interval is required"}), 400

        # Validate range
        if not isinstance(match_interval, int) or match_interval < 1 or match_interval > 60:
            return jsonify({"error": "match_interval must be an integer between 1 and 60"}), 400

        if api_bp.config_manager:
            # Update configuration
            result = api_bp.config_manager.update_section("general", {
                "match_interval": match_interval
            })

            if result:
                logger.info(f"Match interval updated to {match_interval} minutes")

                # Send configuration update message to message bus
                try:
                    from ..core.message_bus import MessageBuilder, MessageType
                    if api_bp.message_bus:
                        config_update_message = MessageBuilder.config_update(
                            sender="web_dashboard",
                            config_section="general",
                            config_data={"match_interval": match_interval}
                        )
                        api_bp.message_bus.publish(config_update_message)
                        logger.info("Match interval configuration update message sent to message bus")
                except Exception as msg_e:
                    logger.warning(f"Failed to send match interval config update message: {msg_e}")

                return jsonify({
                    "success": True,
                    "message": f"Match interval set to {match_interval} minutes"
                })
            else:
                return jsonify({"error": "Failed to update configuration"}), 500
        else:
            return jsonify({"error": "Configuration manager not available"}), 500

    except Exception as e:
        logger.error(f"API set match interval error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/config/license-text')
@get_api_auth_decorator()
def get_license_text():
    """Get license text configuration"""
    try:
        if api_bp.db_manager:
            session = api_bp.db_manager.get_session()
            try:
                from ..database.models import ConfigurationModel
                config = session.query(ConfigurationModel).filter_by(key='license_text').first()

                if config:
                    license_text = config.get_typed_value()
                else:
                    # Return default if not set
                    license_text = 'LICENSED BY THE NATIONAL LOTTERIES AND GAMING REGULATORY BOARD - LICENSE NUMBER NLGBR-GB-82-006. Gambling can be addictive and psychologically harmful not allowed for minors'

                return jsonify({
                    "success": True,
                    "license_text": license_text
                })
            finally:
                session.close()
        else:
            return jsonify({"error": "Database manager not available"}), 500

    except Exception as e:
        logger.error(f"API get license text error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/config/license-text', methods=['POST'])
@get_api_auth_decorator()
def set_license_text():
    """Set license text configuration"""
    try:
        data = request.get_json() or {}
        license_text = data.get('license_text', '').strip()

        if not license_text:
            return jsonify({"error": "license_text is required"}), 400

        # Validate length
        if len(license_text) > 500:
            return jsonify({"error": "License text must be 500 characters or less"}), 400

        if api_bp.db_manager:
            session = api_bp.db_manager.get_session()
            try:
                from ..database.models import ConfigurationModel
                from datetime import datetime

                # Check if config exists
                config = session.query(ConfigurationModel).filter_by(key='license_text').first()

                if config:
                    # Update existing config
                    config.set_typed_value(license_text)
                    config.updated_at = datetime.utcnow()
                else:
                    # Create new config
                    config = ConfigurationModel(
                        key='license_text',
                        config_value=license_text,
                        value_type='string',
                        description='License text displayed on overlay templates',
                        is_system=False
                    )
                    session.add(config)

                session.commit()

                logger.info(f"License text updated: {len(license_text)} characters")

                return jsonify({
                    "success": True,
                    "message": "License text updated successfully",
                    "license_text": license_text
                })
            finally:
                session.close()
        else:
            return jsonify({"error": "Database manager not available"}), 500

    except Exception as e:
        logger.error(f"API set license text error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/config/test-connection', methods=['POST'])
@get_api_auth_decorator()
@get_api_auth_decorator(require_admin=True)
def test_api_connection():
    """Test connection to FastAPI server using request data or configured values"""
    try:
        data = request.get_json() or {}
        
        # Get URL and token from request first, fallback to configuration
        url = data.get('url', '').strip()
        token = data.get('token', '').strip()
        
        # If no URL/token in request, load from configuration
        if not url or not token:
            try:
                if api_bp.config_manager:
                    api_config = api_bp.config_manager.get_section_config("api") or {}
                    if not url:
                        url = api_config.get('fastapi_url', '').strip()
                    if not token:
                        token = api_config.get('api_token', '').strip()
                else:
                    # Fallback to default values if config manager not available
                    if not url:
                        url = 'https://mbetter.nexlab.net/api/updates'
            except Exception as e:
                logger.warning(f"Failed to load API configuration for test: {e}")
                # Fallback to default values
                if not url:
                    url = 'https://mbetter.nexlab.net/api/updates'
        
        if not url:
            return jsonify({"error": "URL is required"}), 400
        
        # Test the connection
        import requests
        from urllib.parse import urljoin
        
        # Normalize URL
        if not url.startswith(('http://', 'https://')):
            url = 'https://' + url
        
        # Call the exact configured URL without any modifications
        session = requests.Session()
        session.timeout = 10
        
        # Prepare request data
        request_data = {}
        if token:
            # Add authentication in header and request body
            session.headers.update({
                'Authorization': f'Bearer {token}'
            })
            request_data['token'] = token
        
        try:
            # Call the exact URL as configured with POST request
            response = session.post(url, json=request_data, timeout=10)
            
            auth_status = "with authentication" if token else "without authentication"
            return jsonify({
                "success": True,
                "message": f"Connection successful {auth_status}! Server responded with status {response.status_code}",
                "url": url,
                "status_code": response.status_code,
                "authenticated": bool(token)
            })
                
        except Exception as e:
            auth_info = " (with authentication)" if token else " (without authentication)"
            return jsonify({
                "success": False,
                "error": f"Connection failed{auth_info}. Error: {str(e)}"
            }), 400
        
    except Exception as e:
        logger.error(f"API connection test error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/users')
@get_api_auth_decorator()
@get_api_auth_decorator(require_admin=True)
def get_users():
    """Get all users"""
    try:
        users = api_bp.api.get_users()
        return jsonify(users)
    except Exception as e:
        logger.error(f"API get users error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/users', methods=['POST'])
@get_api_auth_decorator()
@get_api_auth_decorator(require_admin=True)
def create_user():
    """Create new user"""
    try:
        data = request.get_json() or {}
        username = data.get('username', '').strip()
        email = data.get('email', '').strip()
        password = data.get('password', '')
        is_admin = data.get('is_admin', False)
        role = data.get('role', 'normal')
        
        if not all([username, email, password]):
            return jsonify({"error": "Username, email, and password are required"}), 400
        
        # Validate role
        if role not in ['admin', 'normal', 'cashier']:
            return jsonify({"error": "Invalid role. Must be admin, normal, or cashier"}), 400
        
        result = api_bp.api.create_user(username, email, password, is_admin, role)
        return jsonify(result)
        
    except Exception as e:
        logger.error(f"API create user error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/users/<int:user_id>', methods=['PUT'])
@get_api_auth_decorator()
@get_api_auth_decorator(require_admin=True)
def update_user(user_id):
    """Update user"""
    try:
        data = request.get_json() or {}
        username = data.get('username')
        email = data.get('email')
        password = data.get('password')
        is_admin = data.get('is_admin')
        role = data.get('role')
        email_verified = data.get('email_verified')
        
        # Validate role if provided
        if role and role not in ['admin', 'normal', 'cashier', 'broker', 'player']:
            return jsonify({"error": "Invalid role. Must be admin, normal, cashier, broker, or player"}), 400
        
        result = api_bp.api.update_user(user_id, username, email, password, is_admin, role, email_verified)
        return jsonify(result)
        
    except Exception as e:
        logger.error(f"API update user error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/send-verification-email', methods=['POST'])
@get_api_auth_decorator()
@get_api_auth_decorator(require_admin=True)
def admin_send_verification_email():
    """Send verification email to a user (admin only)"""
    try:
        data = request.get_json() or {}
        email = data.get('email', '').strip()
        
        if not email:
            return jsonify({"error": "Email is required"}), 400
        
        # Get email settings
        settings_response = get_email_settings()
        if settings_response[1] != 200:
            return jsonify({"error": "Email services are not configured"}), 400
        
        settings_data = settings_response[0].get_json()
        settings = settings_data.get('settings', {})
        
        if not settings.get('enabled'):
            return jsonify({"error": "Email services are not enabled"}), 400
        
        # Get user by email
        session = api_bp.db_manager.get_session()
        try:
            from ..database.models import UserModel, EmailVerificationModel
            
            user = session.query(UserModel).filter_by(email=email).first()
            
            if not user:
                return jsonify({"error": "User not found"}), 404
            
            # Generate verification token
            import secrets
            token = secrets.token_urlsafe(32)
            expires_at = datetime.utcnow() + timedelta(hours=24)
            
            # Store verification token
            verification = EmailVerificationModel(
                user_id=user.id,
                email=email,
                token=token,
                token_type='email_verification',
                expires_at=expires_at
            )
            session.add(verification)
            session.commit()
            
            # Create verification link
            app_domain = settings.get('app_domain', '').rstrip('/')
            verification_link = f"{app_domain}/verify-email?token={token}"
            
            # Create email
            import smtplib
            from email.mime.text import MIMEText
            from email.mime.multipart import MIMEMultipart
            
            msg = MIMEMultipart('alternative')
            msg['Subject'] = 'Verify Your Email Address'
            msg['From'] = f"{settings.get('smtp_from_name', 'MbetterClient')} <{settings.get('smtp_from_email')}>"
            msg['To'] = email
            
            html_content = f"""
            <html>
            <body style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
                <h2 style="color: #333;">Verify Your Email Address</h2>
                <p>Hi {user.username},</p>
                <p>Please verify your email address by clicking the button below:</p>
                <p style="text-align: center; margin: 30px 0;">
                    <a href="{verification_link}" style="background-color: #007bff; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px; display: inline-block;">Verify Email</a>
                </p>
                <p>Or copy and paste this link into your browser:</p>
                <p style="word-break: break-all; color: #666;">{verification_link}</p>
                <p style="color: #666; font-size: 12px;">This link will expire in 24 hours.</p>
                <hr style="margin: 30px 0; border: none; border-top: 1px solid #eee;">
                <p style="color: #999; font-size: 12px;">If you didn't request this email, please ignore it.</p>
            </body>
            </html>
            """
            
            msg.attach(MIMEText(html_content, 'html'))
            
            # Send email
            smtp_host = settings.get('smtp_host')
            smtp_port = settings.get('smtp_port', 587)
            smtp_username = settings.get('smtp_username')
            smtp_password = settings.get('smtp_password')
            smtp_use_tls = settings.get('smtp_use_tls', 'tls')
            
            if smtp_use_tls == 'ssl':
                server = smtplib.SMTP_SSL(smtp_host, smtp_port, timeout=10)
            else:
                server = smtplib.SMTP(smtp_host, smtp_port, timeout=10)
                if smtp_use_tls == 'tls':
                    server.starttls()
            
            server.login(smtp_username, smtp_password)
            server.send_message(msg)
            server.quit()
            
            logger.info(f"Verification email sent to {email} by admin")
            
            return jsonify({
                "success": True,
                "message": f"Verification email sent to {email}"
            })
            
        finally:
            session.close()
            
    except Exception as e:
        logger.error(f"Send verification email error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/users/<int:user_id>', methods=['DELETE'])
@get_api_auth_decorator()
@get_api_auth_decorator(require_admin=True)
def delete_user(user_id):
    """Delete user"""
    try:
        result = api_bp.api.delete_user(user_id)
        return jsonify(result)
    except Exception as e:
        logger.error(f"API delete user error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/tokens')
@get_api_auth_decorator()
def get_api_tokens():
    """Get API tokens for current user"""
    try:
        user_id = getattr(current_user, 'id', None) or getattr(request, 'current_user', {}).get('user_id')
        
        if not user_id:
            return jsonify({"error": "User not authenticated"}), 401
        
        tokens = api_bp.api.get_api_tokens(user_id)
        return jsonify(tokens)
        
    except Exception as e:
        logger.error(f"API get tokens error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/tokens', methods=['POST'])
@get_api_auth_decorator()
def create_api_token():
    """Create API token"""
    try:
        data = request.get_json() or {}
        token_name = data.get('name', '').strip()
        expires_hours = data.get('expires_hours', 8760)  # 1 year default
        
        if not token_name:
            return jsonify({"error": "Token name is required"}), 400
        
        user_id = getattr(current_user, 'id', None) or getattr(request, 'current_user', {}).get('user_id')
        
        if not user_id:
            return jsonify({"error": "User not authenticated"}), 401
        
        result = api_bp.api.create_api_token(user_id, token_name, expires_hours)
        return jsonify(result)
        
    except Exception as e:
        logger.error(f"API create token error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/tokens/<int:token_id>', methods=['DELETE'])
@get_api_auth_decorator()
def revoke_api_token(token_id):
    """Revoke API token"""
    try:
        user_id = getattr(current_user, 'id', None) or getattr(request, 'current_user', {}).get('user_id')
        
        if not user_id:
            return jsonify({"error": "User not authenticated"}), 401
        
        result = api_bp.api.revoke_api_token(user_id, token_id)
        return jsonify(result)
        
    except Exception as e:
        logger.error(f"API revoke token error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/logs')
@get_api_auth_decorator()
@get_api_auth_decorator(require_admin=True)
def get_logs():
    """Get application logs"""
    try:
        level = request.args.get('level', 'INFO')
        limit = int(request.args.get('limit', 100))
        
        logs = api_bp.api.get_logs(level, limit)
        return jsonify(logs)
        
    except Exception as e:
        logger.error(f"API get logs error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/test-message', methods=['POST'])
@get_api_auth_decorator()
@get_api_auth_decorator(require_admin=True)
def send_test_message():
    """Send test message to component"""
    try:
        data = request.get_json() or {}
        recipient = data.get('recipient')
        message_type = data.get('message_type')
        message_data = data.get('data', {})
        
        if not recipient or not message_type:
            return jsonify({"error": "Recipient and message type are required"}), 400
        
        result = api_bp.api.send_test_message(recipient, message_type, message_data)
        return jsonify(result)
        
    except Exception as e:
        logger.error(f"API test message error: {e}")
        return jsonify({"error": str(e)}), 500


# Video upload and delete routes
@api_bp.route('/video/upload', methods=['POST'])
@get_api_auth_decorator()
def upload_video():
    """Upload video file"""
    try:
        logger.info("Video upload route called")
        logger.info(f"Request files: {list(request.files.keys())}")
        logger.info(f"Request form: {dict(request.form)}")
        
        if 'video' not in request.files:
            logger.error("No video file in request.files")
            return jsonify({"error": "No video file provided"}), 400
        
        file = request.files['video']
        logger.info(f"File received: {file.filename}, size: {file.content_length if hasattr(file, 'content_length') else 'unknown'}")
        
        if file.filename == '':
            logger.error("Empty filename")
            return jsonify({"error": "No file selected"}), 400
        
        template = request.form.get('template', 'news_template')
        logger.info(f"Template: {template}")
        
        # Check if API instance is available
        if not hasattr(api_bp, 'api') or api_bp.api is None:
            logger.error("API instance not available on blueprint")
            return jsonify({"error": "API service unavailable"}), 500
            
        logger.info("Calling api_bp.api.upload_video...")
        result = api_bp.api.upload_video(file, template)
        logger.info(f"Upload result: {result}")
        return jsonify(result)
        
    except Exception as e:
        logger.error(f"API video upload error: {e}", exc_info=True)
        return jsonify({"error": str(e)}), 500


@api_bp.route('/video/delete', methods=['POST'])
@get_api_auth_decorator()
def delete_video():
    """Delete uploaded video"""
    try:
        data = request.get_json() or {}
        filename = data.get('filename')
        
        if not filename:
            return jsonify({"error": "Filename is required"}), 400
        
        result = api_bp.api.delete_video(filename)
        return jsonify(result)
        
    except Exception as e:
        logger.error(f"API video delete error: {e}")
        return jsonify({"error": str(e)}), 500


# Auth token endpoint for JWT creation
@auth_bp.route('/token', methods=['POST'])
def create_auth_token():
    """Create JWT authentication token"""
    try:
        data = request.get_json() or {}
        username = data.get('username', '').strip()
        password = data.get('password', '')
        
        if not username or not password:
            return jsonify({"error": "Username and password are required"}), 400
        
        # Authenticate user
        authenticated_user = auth_bp.auth_manager.authenticate_user(username, password)
        
        if authenticated_user:
            # Create JWT token
            token = auth_bp.auth_manager.create_jwt_token(authenticated_user.id)
            
            if token:
                return jsonify({
                    "access_token": token,
                    "token_type": "bearer",
                    "user": authenticated_user.to_dict()
                })
            else:
                return jsonify({"error": "Failed to create token"}), 500
        else:
            return jsonify({"error": "Invalid credentials"}), 401
            
    except Exception as e:
        logger.error(f"Token creation error: {e}")
        return jsonify({"error": str(e)}), 500
@api_bp.route('/system/shutdown', methods=['POST'])
@get_api_auth_decorator()
@get_api_auth_decorator(require_admin=True)
def shutdown_application():
    """Shutdown the application (admin only)"""
    try:
        logger.info(f"Application shutdown requested by admin user")
        
        # Return success response immediately
        response = jsonify({"success": True, "message": "Shutdown initiated"})
        
        # Schedule immediate force exit in a separate thread to avoid circular dependencies
        import threading
        import time
        import os
        
        def force_shutdown():
            time.sleep(0.5)  # Give time for HTTP response to be sent
            logger.info("Web dashboard initiated force shutdown - terminating application")
            os._exit(0)
        
        shutdown_thread = threading.Thread(target=force_shutdown, daemon=True)
        shutdown_thread.start()
        
        return response
        
    except Exception as e:
        logger.error(f"API shutdown error: {e}")
        return jsonify({"error": str(e)}), 500
    
@api_bp.route('/templates/upload', methods=['POST'])
@get_api_auth_decorator()
@get_api_auth_decorator(require_admin=True)
def upload_template():
    """Upload template file"""
    try:
        if 'template' not in request.files:
            return jsonify({"error": "No template file provided"}), 400
        
        file = request.files['template']
        if file.filename == '':
            return jsonify({"error": "No file selected"}), 400
        
        # Get template name from form or use filename
        template_name = request.form.get('template_name', '')
        
        result = api_bp.api.upload_template(file, template_name)
        return jsonify(result)
        
    except Exception as e:
        logger.error(f"Template upload error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/templates/<template_name>', methods=['GET'])
@get_api_auth_decorator()
def get_template_content(template_name):
    """Get template content for preview"""
    try:
        from pathlib import Path
        
        # Add .html extension if not present
        if not template_name.endswith('.html'):
            template_name += '.html'
        
        # Get persistent uploaded templates directory
        uploaded_templates_dir = api_bp.api._get_persistent_templates_dir()
        
        # Get built-in templates directory
        builtin_templates_dir = Path(__file__).parent.parent / "qt_player" / "templates"
        
        # First try uploaded templates (user uploads take priority)
        template_path = uploaded_templates_dir / template_name
        
        # If not found in uploaded, try built-in templates
        if not template_path.exists():
            template_path = builtin_templates_dir / template_name
        
        if template_path.exists():
            with open(template_path, 'r', encoding='utf-8') as f:
                content = f.read()
            return content, 200, {'Content-Type': 'text/html'}
        else:
            return jsonify({"error": "Template not found"}), 404
            
    except Exception as e:
        logger.error(f"Template content fetch error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/templates/<template_name>', methods=['DELETE'])
@get_api_auth_decorator()
@get_api_auth_decorator(require_admin=True)
def delete_template(template_name):
    """Delete uploaded template"""
    try:
        result = api_bp.api.delete_template(template_name)
        return jsonify(result)
        
    except Exception as e:
        logger.error(f"Template deletion error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/outcome-assignments')
@get_api_auth_decorator()
def get_outcome_assignments():
    """Get outcome template assignments"""
    try:
        from ..database.models import GameConfigModel
        import json

        session = api_bp.db_manager.get_session()
        try:
            # Get outcome assignments from config
            assignments_config = session.query(GameConfigModel).filter_by(
                config_key='outcome_template_assignments'
            ).first()

            assignments = {}
            if assignments_config:
                try:
                    assignments = json.loads(assignments_config.config_value)
                except (json.JSONDecodeError, TypeError):
                    assignments = {}

            return jsonify({
                "success": True,
                "assignments": assignments
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API get outcome assignments error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/outcome-assignments', methods=['POST'])
@get_api_auth_decorator()
@get_api_auth_decorator(require_admin=True)
def save_outcome_assignments():
    """Save outcome template assignments (admin only)"""
    try:
        from ..database.models import GameConfigModel
        import json
        from datetime import datetime

        data = request.get_json() or {}
        assignments = data.get('assignments', {})

        session = api_bp.db_manager.get_session()
        try:
            # Save assignments to config
            assignments_config = session.query(GameConfigModel).filter_by(
                config_key='outcome_template_assignments'
            ).first()
            
            if assignments_config:
                assignments_config.config_value = json.dumps(assignments)
                assignments_config.updated_at = datetime.utcnow()
            else:
                assignments_config = GameConfigModel(
                    config_key='outcome_template_assignments',
                    config_value=json.dumps(assignments),
                    value_type='json',
                    description='Template assignments for specific match outcomes',
                    is_system=False
                )
                session.add(assignments_config)

            session.commit()

            logger.info(f"Saved outcome template assignments: {len(assignments)} assignments")

            return jsonify({
                "success": True,
                "message": f"Saved {len(assignments)} outcome assignments",
                "assignments": assignments
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API save outcome assignments error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/send-custom-message', methods=['POST'])
@get_api_auth_decorator()
def send_custom_message():
    """Send custom message to overlay with template selection and display time support"""
    try:
        data = request.get_json() or {}
        title = data.get('title', '').strip()
        message = data.get('message', '').strip()
        icon = data.get('icon', '📢').strip()
        template = data.get('template', 'text').strip()
        display_time = data.get('display_time', 10)

        if not title:
            return jsonify({"error": "Message title is required"}), 400

        if not message:
            return jsonify({"error": "Message content is required"}), 400

        # Validate template parameter
        if not template:
            template = 'text'  # Fallback to default
        
        # Validate display time parameter
        try:
            display_time = int(display_time)
            if display_time < 1 or display_time > 300:
                return jsonify({"error": "Display time must be between 1 and 300 seconds"}), 400
        except (ValueError, TypeError):
            return jsonify({"error": "Display time must be a valid number"}), 400

        # Validate that the template exists
        try:
            available_templates = api_bp.api.get_templates()
            if available_templates.get('success') and available_templates.get('templates'):
                template_names = [t.get('name', '') for t in available_templates['templates']]
                # Remove .html extension for comparison
                clean_template_names = [name.replace('.html', '') for name in template_names]
                
                if template not in clean_template_names and f"{template}.html" not in template_names:
                    logger.warning(f"Template '{template}' not found, falling back to 'text' template")
                    template = 'text'  # Fallback to safe default
            else:
                logger.warning("Could not retrieve template list, using 'text' template")
                template = 'text'
        except Exception as e:
            logger.warning(f"Error validating template '{template}': {e}, falling back to 'text'")
            template = 'text'

        # Prepare overlay data based on template type
        overlay_data = {}
        
        if template == 'text':
            # Standard text template data
            overlay_data = {
                'title': title,
                'message': message,
                'icon': icon,
                'display_time': display_time
            }
        elif template == 'fixtures':
            # Fixtures template - map message fields to fixture data structure
            overlay_data = {
                'title': title,
                'message': message,
                'custom_message': True,  # Flag to indicate this is a custom message
                'display_time': display_time
            }
        elif template == 'results':
            # Results template - map message fields to results data structure
            overlay_data = {
                'title': title,
                'message': message,
                'custom_message': True,  # Flag to indicate this is a custom message
                'display_time': display_time
            }
        else:
            # Generic template - use basic data structure
            overlay_data = {
                'title': title,
                'message': message,
                'icon': icon,
                'custom_message': True,
                'display_time': display_time
            }

        # Send the message to the specified template overlay
        result = api_bp.api.update_overlay(template, overlay_data)
        
        if result.get('success'):
            logger.info(f"Custom message sent to {template} template: {title} (display: {display_time}s)")
            return jsonify({
                "success": True,
                "message": f"Custom message sent successfully to {template} template",
                "data": {
                    "title": title,
                    "message": message,
                    "icon": icon,
                    "template": template,
                    "display_time": display_time
                }
            })
        else:
            return jsonify({
                "success": False,
                "error": result.get('error', f'Failed to send message to {template} template')
            }), 500

    except Exception as e:
        logger.error(f"API send custom message error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/intro-templates', methods=['GET'])
@get_api_auth_decorator()
def get_intro_templates():
    """Get intro templates configuration"""
    try:
        from ..database.models import GameConfigModel
        import json

        session = api_bp.db_manager.get_session()
        try:
            # Get intro templates configuration from database
            intro_config = session.query(GameConfigModel).filter_by(config_key='intro_templates_config').first()

            # Default configuration
            default_config = {
                'templates': [
                    {'name': 'fixtures', 'show_time': '00:15'},
                    {'name': 'match', 'show_time': '00:15'}
                ],
                'default_show_time': '00:15',
                'rotating_time': '00:15'
            }

            if intro_config:
                try:
                    config = json.loads(intro_config.config_value)
                    # Merge with defaults to ensure all fields are present
                    for key, value in default_config.items():
                        if key not in config:
                            config[key] = value
                    return jsonify(config)
                except (json.JSONDecodeError, TypeError):
                    logger.warning("Failed to parse intro templates config from database, using defaults")
                    return jsonify(default_config)
            else:
                return jsonify(default_config)

        finally:
            session.close()

    except Exception as e:
        logger.error(f"Error loading intro templates: {str(e)}")
        return jsonify({'templates': [], 'default_show_time': '00:30', 'rotating_time': '00:15'})


@api_bp.route('/intro-templates', methods=['POST'])
@get_api_auth_decorator()
def save_intro_templates():
    """Save intro templates configuration"""
    try:
        from ..database.models import GameConfigModel
        import json
        import re

        data = request.get_json()
        if not data:
            return jsonify({'error': 'No configuration data provided'}), 400

        # Validate data structure
        templates = data.get('templates', [])
        default_show_time = data.get('default_show_time', '00:30')
        rotating_time = data.get('rotating_time', '00:15')

        logger.info(f"WebDashboard: Saving intro templates - received {len(templates)} templates")
        logger.debug(f"WebDashboard: Templates data: {templates}")
        logger.debug(f"WebDashboard: Default show time: {default_show_time}, Rotating time: {rotating_time}")

        # Validate time format (MM:SS)
        time_pattern = re.compile(r'^[0-9]{1,2}:[0-5][0-9]$')

        if not time_pattern.match(default_show_time):
            return jsonify({'error': 'Invalid default show time format. Use MM:SS format.'}), 400

        if not time_pattern.match(rotating_time):
            return jsonify({'error': 'Invalid rotating time format. Use MM:SS format.'}), 400

        # Validate templates
        if not isinstance(templates, list):
            return jsonify({'error': 'Templates must be a list'}), 400

        for i, template in enumerate(templates):
            if not isinstance(template, dict):
                return jsonify({'error': f'Template {i+1} must be an object'}), 400

            if 'name' not in template or 'show_time' not in template:
                return jsonify({'error': f'Template {i+1} must have name and show_time fields'}), 400

            if not time_pattern.match(template['show_time']):
                return jsonify({'error': f'Template {i+1} has invalid show_time format. Use MM:SS format.'}), 400

        # Save configuration to database
        config = {
            'templates': templates,
            'default_show_time': default_show_time,
            'rotating_time': rotating_time,
            'updated_at': datetime.now().isoformat()
        }

        config_json = json.dumps(config)
        logger.debug(f"WebDashboard: Config JSON length: {len(config_json)}")

        session = api_bp.db_manager.get_session()
        try:
            # Check if config already exists
            existing_config = session.query(GameConfigModel).filter_by(config_key='intro_templates_config').first()

            if existing_config:
                logger.debug("WebDashboard: Updating existing config")
                # Update existing config
                existing_config.config_value = config_json
                existing_config.updated_at = datetime.utcnow()
            else:
                logger.debug("WebDashboard: Creating new config")
                # Create new config
                new_config = GameConfigModel(
                    config_key='intro_templates_config',
                    config_value=config_json,
                    value_type='json',
                    description='Intro templates configuration for video player',
                    is_system=False
                )
                session.add(new_config)

            session.commit()

            logger.info(f"WebDashboard: Successfully saved intro templates configuration: {len(templates)} templates")

            return jsonify({
                'success': True,
                'message': f'Intro templates configuration saved successfully with {len(templates)} templates',
                'templates': templates,
                'default_show_time': default_show_time,
                'rotating_time': rotating_time
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"WebDashboard: Error saving intro templates: {str(e)}")
        return jsonify({'error': 'Internal server error'}), 500


@api_bp.route('/fixtures')
@get_api_auth_decorator()
def get_fixtures():
    """Get all fixtures/matches grouped by fixture_id with calculated status"""
    try:
        from ..database.models import MatchModel
        from datetime import datetime, date
        from collections import defaultdict
        from ..utils.timezone_utils import get_today_venue_date, utc_to_venue_datetime

        session = api_bp.db_manager.get_session()
        try:
            matches = session.query(MatchModel).order_by(MatchModel.created_at.desc()).all()

            # Group matches by fixture_id
            fixtures_by_id = defaultdict(list)
            for match in matches:
                fixtures_by_id[match.fixture_id].append(match)

            fixtures_data = []
            # Use venue timezone for date comparison
            today = get_today_venue_date(api_bp.db_manager)

            for fixture_id, fixture_matches in fixtures_by_id.items():
                # Sort matches by match_number for consistent ordering
                fixture_matches.sort(key=lambda m: m.match_number)

                # Calculate fixture-level status with venue timezone awareness
                fixture_status = calculate_fixture_status(fixture_matches, today, api_bp.db_manager)

                # Use the first match as the representative for the fixture
                first_match = fixture_matches[0]

                # Determine fixture date in venue timezone for proper filtering
                fixture_venue_date = None
                is_today = False
                is_past = False
                if first_match.start_time and api_bp.db_manager:
                    venue_start_time = utc_to_venue_datetime(first_match.start_time, api_bp.db_manager)
                    fixture_venue_date = venue_start_time.date()
                    is_today = (fixture_venue_date == today)
                    is_past = (fixture_venue_date < today)
                
                # Create fixture data structure
                fixture_data = {
                    'id': first_match.id,
                    'fixture_id': fixture_id,
                    'match_number': first_match.match_number,
                    'fighter1_township': first_match.fighter1_township,
                    'fighter2_township': first_match.fighter2_township,
                    'venue_kampala_township': first_match.venue_kampala_township,
                    'start_time': first_match.start_time.isoformat() if first_match.start_time else None,
                    'created_at': first_match.created_at.isoformat(),
                    'fixture_status': fixture_status,
                    'match_count': len(fixture_matches),
                    'matches': [match.to_dict() for match in fixture_matches],
                    'is_today': is_today,
                    'is_past': is_past,
                    'fixture_venue_date': fixture_venue_date.isoformat() if fixture_venue_date else None
                }

                fixtures_data.append(fixture_data)

            # Sort fixtures by creation date (most recent first)
            fixtures_data.sort(key=lambda f: f['created_at'], reverse=True)

            return jsonify({
                "success": True,
                "fixtures": fixtures_data,
                "total": len(fixtures_data)
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API get fixtures error: {e}")
        return jsonify({"error": str(e)}), 500


def calculate_fixture_status(matches, today, db_manager=None):
    """
    Calculate fixture status based on match statuses and timing.

    Args:
        matches: List of MatchModel objects for this fixture
        today: Today's date in venue timezone
        db_manager: Database manager for timezone conversion (optional for backward compatibility)

    Returns:
        str: Fixture status ('pending', 'running', 'scheduled', 'bet', 'ingame', 'end')
    """
    if not matches:
        return 'pending'

    # Get all match statuses
    match_statuses = [match.status for match in matches]

    # Check if all matches are pending
    if all(status == 'pending' for status in match_statuses):
        return 'pending'

    # Check if start time of first match is today (in venue timezone) and at least one match is pending
    first_match = matches[0]
    if first_match.start_time and db_manager:
        # Convert UTC start_time to venue timezone for proper date comparison
        from ..utils.timezone_utils import utc_to_venue_datetime
        venue_start_time = utc_to_venue_datetime(first_match.start_time, db_manager)
        if (venue_start_time.date() == today and
            any(status == 'pending' for status in match_statuses)):
            return 'running'
    elif (first_match.start_time and
        first_match.start_time.date() == today and
        any(status == 'pending' for status in match_statuses)):
        # Fallback for backward compatibility (no db_manager)
        return 'running'

    # Otherwise, determine status based on the most advanced match status
    status_priority = {
        'pending': 0,
        'scheduled': 1,
        'bet': 2,
        'ingame': 3,
        'end': 4,
        'cancelled': 5,
        'failed': 5,
        'paused': 5
    }

    # Find the highest priority status
    highest_status = max(match_statuses, key=lambda s: status_priority.get(s, 0))

    # Map to fixture status
    if highest_status in ['cancelled', 'failed', 'paused']:
        return 'end'  # Treat cancelled/failed/paused as end state
    else:
        return highest_status


@api_bp.route('/cashier/pending-matches')
@get_api_auth_decorator()
def get_cashier_pending_matches():
    """Get matches from the fixture for cashier dashboard - supports date filtering"""
    try:
        # Allow access from localhost without authentication
        if request.remote_addr == '127.0.0.1':
            pass  # Skip authentication
        # Check if user is authenticated via Flask-Login (web interface)
        elif current_user.is_authenticated:
            pass  # User is logged in via web interface
        elif hasattr(api_bp, 'auth_manager') and api_bp.auth_manager:
            # Apply API token authentication manually
            auth_header = request.headers.get('Authorization')
            if auth_header and auth_header.startswith('Bearer '):
                token = auth_header.split(' ', 1)[1]
                payload = api_bp.auth_manager.verify_jwt_token(token)
                if payload:
                    request.current_user = payload
                else:
                    api_data = api_bp.auth_manager.verify_api_token(token)
                    if api_data:
                        request.current_user = api_data
                    else:
                        return {'error': 'Authentication required'}, 401
            else:
                return {'error': 'Authentication required'}, 401
        from ..database.models import MatchModel
        from datetime import datetime, date, timedelta
        from ..utils.timezone_utils import get_today_venue_date, venue_to_utc_datetime

        session = api_bp.db_manager.get_session()
        try:
            # Check for date parameter in query string
            date_param = request.args.get('date')
            
            # Get today's date in VENUE timezone (not server timezone)
            today = get_today_venue_date(api_bp.db_manager)
            yesterday = today - timedelta(days=1)

            # First, auto-fail old fixtures with pending/scheduled/bet results
            auto_fail_old_fixtures(session, yesterday)

            # Determine the target date for fixture selection
            if date_param:
                # Parse the date parameter (format: YYYY-MM-DD)
                # This date is in venue timezone (user's local date selection)
                try:
                    target_date = datetime.strptime(date_param, '%Y-%m-%d').date()
                except ValueError:
                    return jsonify({
                        "success": False,
                        "error": f"Invalid date format: {date_param}. Use YYYY-MM-DD format."
                    }), 400
            else:
                target_date = today

            # Convert venue date range to UTC for database query
            # The database stores start_time in UTC
            venue_start = datetime.combine(target_date, datetime.min.time())
            venue_end = datetime.combine(target_date, datetime.max.time())
            utc_start = venue_to_utc_datetime(venue_start, api_bp.db_manager)
            utc_end = venue_to_utc_datetime(venue_end, api_bp.db_manager)

            logger.info(f"DEBUG: Date filter - target_date={target_date}, venue_start={venue_start}, utc_start={utc_start}, utc_end={utc_end}")

            # Find fixtures with start_time on the target date (get the LAST one)
            # Use UTC times for database comparison since start_time is stored in UTC
            fixtures_with_target_start = session.query(MatchModel.fixture_id)\
                .filter(MatchModel.start_time.isnot(None))\
                .filter(MatchModel.start_time >= utc_start)\
                .filter(MatchModel.start_time <= utc_end)\
                .order_by(MatchModel.created_at.desc())\
                .first()

            selected_fixture_id = None

            if fixtures_with_target_start:
                # Use the LAST fixture where matches start on target date
                selected_fixture_id = fixtures_with_target_start.fixture_id
                logger.info(f"Selected fixture {selected_fixture_id} - last fixture with matches starting on {target_date}")
            else:
                # Fallback: find the first fixture where ALL matches are in pending status (not scheduled/bet)
                all_fixtures = session.query(MatchModel.fixture_id).distinct().order_by(MatchModel.fixture_id.asc()).all()

                for fixture_row in all_fixtures:
                    fixture_id = fixture_row.fixture_id

                    # Check if all matches in this fixture are pending (strict pending only)
                    fixture_matches = session.query(MatchModel).filter_by(fixture_id=fixture_id).all()

                    if fixture_matches and all(match.status == 'pending' for match in fixture_matches):
                        selected_fixture_id = fixture_id
                        logger.info(f"Selected fixture {selected_fixture_id} - first fixture with all matches in pending status")
                        break

            if not selected_fixture_id:
                return jsonify({
                    "success": True,
                    "matches": [],
                    "total": 0,
                    "fixture_id": None,
                    "date": target_date.isoformat() if target_date else None
                })

            # Get all matches from the selected fixture
            fixture_matches = session.query(MatchModel)\
                .filter_by(fixture_id=selected_fixture_id)\
                .order_by(MatchModel.match_number.asc())\
                .all()

            matches_data = []
            for match in fixture_matches:
                match_data = match.to_dict()
                matches_data.append(match_data)

            return jsonify({
                "success": True,
                "matches": matches_data,
                "total": len(matches_data),
                "fixture_id": selected_fixture_id,
                "date": target_date.isoformat() if target_date else None
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API get cashier pending matches error: {e}")
        return jsonify({"error": str(e)}), 500


def auto_fail_old_fixtures(session, cutoff_date):
    """Auto-fail old fixtures with pending/scheduled/bet results"""
    try:
        from ..database.models import MatchModel
        from datetime import datetime, timedelta

        # Find matches from fixtures older than cutoff_date
        old_matches = session.query(MatchModel)\
            .filter(MatchModel.start_time.isnot(None))\
            .filter(MatchModel.start_time < datetime.combine(cutoff_date, datetime.max.time()))\
            .filter(MatchModel.status.in_(['pending', 'scheduled', 'bet']))\
            .all()

        failed_count = 0
        for match in old_matches:
            match.status = 'failed'
            failed_count += 1

        if failed_count > 0:
            session.commit()
            logger.info(f"Auto-failed {failed_count} old matches with pending/scheduled/bet status")

    except Exception as e:
        logger.warning(f"Error in auto_fail_old_fixtures: {e}")


@api_bp.route('/cashier/start-games', methods=['POST'])
@get_api_auth_decorator()
def start_games():
    """Start games for the first fixture - send START_GAME message to message bus"""
    try:
        from ..core.message_bus import MessageBuilder, MessageType

        # Send START_GAME message to the message bus
        # The games thread will handle finding and activating the appropriate matches
        start_game_message = MessageBuilder.start_game(
            sender="web_dashboard",
            fixture_id=None  # Let the games thread find the first fixture with pending matches
        )

        # Publish the message to the message bus
        if api_bp.message_bus:
            success = api_bp.message_bus.publish(start_game_message)

            if success:
                logger.info("START_GAME message sent to message bus")

                return jsonify({
                    "success": True,
                    "message": "Start games request sent to games thread",
                    "fixture_id": None  # Will be determined by games thread
                })
            else:
                logger.error("Failed to publish START_GAME message to message bus")
                return jsonify({
                    "success": False,
                    "error": "Failed to send start games request"
                }), 500
        else:
            logger.error("Message bus not available")
            return jsonify({
                "success": False,
                "error": "Message bus not available"
            }), 500

    except Exception as e:
        logger.error(f"API start games error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/fixtures/<fixture_id>')
@get_api_auth_decorator()
def get_fixture_details(fixture_id):
    """Get all matches in a fixture by fixture_id"""
    try:
        # Allow access from localhost without authentication
        if request.remote_addr == '127.0.0.1':
            pass  # Skip authentication
        # Check if user is authenticated via Flask-Login (web interface)
        elif current_user.is_authenticated:
            pass  # User is logged in via web interface
        elif hasattr(api_bp, 'auth_manager') and api_bp.auth_manager:
            # Apply auth check manually for conditional auth
            auth_header = request.headers.get('Authorization')
            if auth_header and auth_header.startswith('Bearer '):
                token = auth_header.split(' ', 1)[1]
                payload = api_bp.auth_manager.verify_jwt_token(token)
                if payload:
                    request.current_user = payload
                else:
                    api_data = api_bp.auth_manager.verify_api_token(token)
                    if api_data:
                        request.current_user = api_data
                    else:
                        return {'error': 'Authentication required'}, 401
            else:
                return {'error': 'Authentication required'}, 401
        from ..database.models import MatchModel, MatchOutcomeModel
        from datetime import datetime, date

        session = api_bp.db_manager.get_session()
        try:
            # Get all matches for this fixture
            matches = session.query(MatchModel).filter_by(fixture_id=fixture_id).order_by(MatchModel.match_number.asc()).all()

            if not matches:
                return jsonify({"error": "Fixture not found"}), 404

            # Calculate fixture-level status
            today = date.today()
            fixture_status = calculate_fixture_status(matches, today)

            # Get fixture data from first match
            first_match = matches[0]
            fixture_data = {
                'id': first_match.id,
                'fixture_id': fixture_id,
                'match_number': first_match.match_number,
                'fighter1_township': first_match.fighter1_township,
                'fighter2_township': first_match.fighter2_township,
                'venue_kampala_township': first_match.venue_kampala_township,
                'start_time': first_match.start_time.isoformat() if first_match.start_time else None,
                'created_at': first_match.created_at.isoformat(),
                'fixture_status': fixture_status,
                'match_count': len(matches)
            }

            # Get all matches with their outcomes
            matches_data = []
            for match in matches:
                match_data = match.to_dict()

                # Get outcomes for this match
                outcomes = session.query(MatchOutcomeModel).filter_by(match_id=match.id).all()
                match_data['outcomes'] = [outcome.to_dict() for outcome in outcomes]
                match_data['outcome_count'] = len(outcomes)

                matches_data.append(match_data)

            return jsonify({
                "success": True,
                "fixture": fixture_data,
                "matches": matches_data,
                "total_matches": len(matches_data)
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API get fixture details error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/fixtures/reset', methods=['POST'])
@get_api_auth_decorator()
@get_api_auth_decorator(require_admin=True)
def reset_fixtures():
    """Reset all fixtures data (admin only) - clear matches, match_outcomes, matches_templates, match_outcomes_templates, bets, extraction_stats, and ZIP files"""
    try:
        from ..database.models import MatchModel, MatchOutcomeModel, MatchTemplateModel, MatchOutcomeTemplateModel, BetModel, BetDetailModel, ExtractionStatsModel
        from ..config.settings import get_user_data_dir
        from pathlib import Path
        import shutil

        session = api_bp.db_manager.get_session()
        try:
            # Count existing data before reset
            matches_count = session.query(MatchModel).count()
            outcomes_count = session.query(MatchOutcomeModel).count()
            templates_count = session.query(MatchTemplateModel).count()
            template_outcomes_count = session.query(MatchOutcomeTemplateModel).count()
            bets_count = session.query(BetModel).count()
            bet_details_count = session.query(BetDetailModel).count()
            extraction_stats_count = session.query(ExtractionStatsModel).count()

            # Delete in correct order to handle foreign key constraints
            # Use raw SQL to ensure proper deletion with MySQL foreign key constraints
            
            # 1. Delete extraction_stats first (references matches)
            deleted_extraction_stats = session.execute(text("DELETE FROM extraction_stats"))
            session.commit()

            # 2. Delete bet_details first (references both bets and matches)
            deleted_bet_details = session.execute(text("DELETE FROM bets_details"))
            session.commit()

            # 3. Delete bets
            deleted_bets = session.execute(text("DELETE FROM bets"))
            session.commit()

            # 4. Delete match_outcomes (references matches)
            deleted_outcomes = session.execute(text("DELETE FROM match_outcomes"))
            session.commit()

            # 5. Delete matches
            deleted_matches = session.execute(text("DELETE FROM matches"))
            session.commit()

            # 6. Delete match_outcomes_templates (references matches_templates)
            deleted_template_outcomes = session.execute(text("DELETE FROM match_outcomes_templates"))
            session.commit()

            # 7. Delete match templates
            deleted_templates = session.execute(text("DELETE FROM matches_templates"))
            session.commit()

            # Clear ZIP files from persistent storage
            zip_storage_dir = Path(get_user_data_dir()) / "zip_files"
            zip_files_removed = 0

            if zip_storage_dir.exists():
                zip_files = list(zip_storage_dir.glob("*.zip"))
                zip_files_removed = len(zip_files)

                # Remove all ZIP files
                for zip_file in zip_files:
                    try:
                        zip_file.unlink()
                    except Exception as e:
                        logger.warning(f"Failed to remove ZIP file {zip_file}: {e}")

                logger.info(f"Removed {zip_files_removed} ZIP files from {zip_storage_dir}")

            logger.info(f"Fixtures reset completed - Removed {matches_count} matches, {outcomes_count} outcomes, {templates_count} templates, {template_outcomes_count} template outcomes, {bets_count} bets, {bet_details_count} bet details, {extraction_stats_count} extraction stats, {zip_files_removed} ZIP files")

            return jsonify({
                "success": True,
                "message": "Fixtures data reset successfully",
                "removed": {
                    "matches": matches_count,
                    "outcomes": outcomes_count,
                    "templates": templates_count,
                    "template_outcomes": template_outcomes_count,
                    "bets": bets_count,
                    "bet_details": bet_details_count,
                    "extraction_stats": extraction_stats_count,
                    "zip_files": zip_files_removed
                }
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API fixtures reset error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/matches-templates')
@get_api_auth_decorator()
def get_matches_templates():
    """Get matches_templates as a single fixture template"""
    try:
        from ..database.models import MatchTemplateModel, MatchOutcomeTemplateModel
        from collections import defaultdict

        session = api_bp.db_manager.get_session()
        try:
            # Get all match templates
            templates = session.query(MatchTemplateModel).order_by(MatchTemplateModel.match_number.asc()).all()

            if not templates:
                return jsonify({
                    "success": True,
                    "templates": [],
                    "total": 0
                })

            # Use the first template to get fixture-level info
            first_template = templates[0]

            # Create a single fixture template that contains all matches
            fixture_template_data = {
                'id': first_template.id,  # Use first template's ID for compatibility
                'fixture_id': 'template_fixture',  # Fixed fixture ID for templates
                'match_number': 1,  # Single fixture representation
                'fighter1_township': first_template.fighter1_township,  # Use first match's fighter
                'fighter2_township': first_template.fighter2_township,  # Use first match's fighter
                'venue_kampala_township': first_template.venue_kampala_township,  # Use first match's venue
                'start_time': None,  # No start time for templates
                'created_at': first_template.created_at.isoformat(),
                'fixture_status': 'template',  # Special status for templates
                'match_count': len(templates),
                'matches': [template.to_dict() for template in templates]  # Include all matches
            }

            return jsonify({
                "success": True,
                "templates": [fixture_template_data],
                "total": 1
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API get matches templates error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/matches-templates/<int:template_id>')
@get_api_auth_decorator()
def get_matches_template_details(template_id):
    """Get details for a specific match template with its outcomes"""
    try:
        from ..database.models import MatchTemplateModel, MatchOutcomeTemplateModel

        session = api_bp.db_manager.get_session()
        try:
            # Get the template
            template = session.query(MatchTemplateModel).filter_by(id=template_id).first()

            if not template:
                return jsonify({"error": "Template not found"}), 404

            # Get outcomes for this template
            outcomes = session.query(MatchOutcomeTemplateModel).filter_by(match_id=template_id).all()

            template_data = template.to_dict()
            
            # Map outcomes to include 'odds' field (from float_value)
            outcomes_data = []
            for outcome in outcomes:
                outcome_dict = outcome.to_dict()
                # Add 'odds' field for frontend compatibility
                outcome_dict['odds'] = outcome_dict.get('float_value', 0.0)
                outcomes_data.append(outcome_dict)
            
            template_data['outcomes'] = outcomes_data
            template_data['outcome_count'] = len(outcomes)

            return jsonify({
                "success": True,
                "template": template_data
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API get matches template details error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/matches-templates/<int:template_id>/odds', methods=['POST'])
@get_api_auth_decorator()
def update_matches_template_odds(template_id):
    """Update odds for a match template"""
    # Check if edit_odds is enabled (disabled by default for security)
    # Only allow if explicitly enabled via command line --edit-odds flag
    settings = getattr(api_bp, 'settings', None)
    edit_odds_enabled = getattr(settings, 'edit_odds', False) if settings else False
    
    # Check if user is admin (required for editing odds)
    from flask import request
    is_admin = False
    if hasattr(request, 'current_user') and request.current_user:
        is_admin = request.current_user.get('is_admin', False)
    
    if not edit_odds_enabled:
        return jsonify({
            "error": "Manual odds editing is disabled. Start with --edit-odds flag to enable admin-only odds editing.",
            "edit_odds_enabled": False
        }), 403
    
    if not is_admin:
        return jsonify({"error": "Admin access required to modify odds"}), 403
    
    try:
        from ..database.models import MatchTemplateModel, MatchOutcomeTemplateModel

        data = request.get_json() or {}
        odds_updates = data.get('odds', [])

        if not odds_updates:
            return jsonify({"error": "Odds updates are required"}), 400

        session = api_bp.db_manager.get_session()
        try:
            # Verify template exists
            template = session.query(MatchTemplateModel).filter_by(id=template_id).first()
            if not template:
                return jsonify({"error": "Template not found"}), 404

            # Update odds for each outcome
            updated_count = 0
            for update in odds_updates:
                outcome_id = update.get('outcome_id')
                new_odds = update.get('odds')

                if outcome_id is None or new_odds is None:
                    continue

                # Find and update the outcome
                outcome = session.query(MatchOutcomeTemplateModel).filter_by(id=outcome_id).first()
                if outcome and outcome.match_id == template_id:
                    outcome.odds = float(new_odds)
                    updated_count += 1

            session.commit()

            logger.info(f"Updated {updated_count} odds for template {template_id}")

            return jsonify({
                "success": True,
                "message": f"Updated {updated_count} odds successfully",
                "updated_count": updated_count
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API update matches template odds error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/api-client/status')
@get_api_auth_decorator()
def get_api_client_status():
    """Get API client status and endpoint information"""
    try:
        # Check if we can get API client component status via message bus
        status_data = {
            "api_client_running": False,
            "fastapi_endpoint": {},
            "message_bus_available": bool(api_bp.message_bus),
            "config_manager_available": bool(api_bp.config_manager)
        }
        
        # Try to get current API configuration
        if api_bp.config_manager:
            try:
                api_config = api_bp.config_manager.get_section_config("api") or {}
                status_data["api_config"] = {
                    "fastapi_url": api_config.get("fastapi_url", ""),
                    "api_token_set": bool(api_config.get("api_token", "").strip()),
                    "api_interval": api_config.get("api_interval", 600),
                    "api_enabled": api_config.get("api_enabled", True)
                }
            except Exception as e:
                status_data["config_error"] = str(e)
        
        # Try to get API endpoints configuration
        if api_bp.config_manager:
            try:
                endpoints_config = api_bp.config_manager.get_section_config("api_endpoints") or {}
                fastapi_endpoint = endpoints_config.get("fastapi_main", {})
                if fastapi_endpoint:
                    status_data["fastapi_endpoint"] = {
                        "url": fastapi_endpoint.get("url", ""),
                        "enabled": fastapi_endpoint.get("enabled", False),
                        "interval": fastapi_endpoint.get("interval", 0),
                        "auth_configured": bool(fastapi_endpoint.get("auth", {}).get("token", "")),
                        "last_request": fastapi_endpoint.get("last_request"),
                        "last_success": fastapi_endpoint.get("last_success"),
                        "consecutive_failures": fastapi_endpoint.get("consecutive_failures", 0),
                        "total_requests": fastapi_endpoint.get("total_requests", 0)
                    }
            except Exception as e:
                status_data["endpoints_error"] = str(e)
        
        return jsonify({
            "success": True,
            "status": status_data
        })
        
    except Exception as e:
        logger.error(f"API client status error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/api-client/trigger', methods=['POST'])
@get_api_auth_decorator()
@get_api_auth_decorator(require_admin=True)
def trigger_api_request():
    """Manually trigger an API request for testing"""
    try:
        data = request.get_json() or {}
        endpoint_name = data.get('endpoint', 'fastapi_main')

        # Send manual API request message
        if api_bp.message_bus:
            from ..core.message_bus import MessageBuilder, MessageType

            api_request_message = MessageBuilder.api_request(
                sender="web_dashboard",
                endpoint=endpoint_name
            )
            api_bp.message_bus.publish(api_request_message)

            logger.info(f"Manual API request triggered for endpoint: {endpoint_name}")

            return jsonify({
                "success": True,
                "message": f"API request triggered for endpoint: {endpoint_name}"
            })
        else:
            return jsonify({
                "success": False,
                "error": "Message bus not available"
            }), 500

    except Exception as e:
        logger.error(f"API request trigger error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/match-timer/config')
@get_api_auth_decorator()
def get_match_timer_config():
    """Get match timer configuration"""
    try:
        if api_bp.config_manager:
            general_config = api_bp.config_manager.get_section_config("general") or {}
            match_interval = general_config.get('match_interval', 5)  # Default 5 minutes
        else:
            match_interval = 5  # Default fallback

        return jsonify({
            "success": True,
            "match_interval": match_interval
        })
    except Exception as e:
        logger.error(f"API get match timer config error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/match-timer/start-match', methods=['POST'])
@get_api_auth_decorator()
def start_next_match():
    """Start the next match by sending MATCH_START message"""
    try:
        from ..database.models import MatchModel
        from datetime import datetime, date
        from ..core.message_bus import MessageBuilder, MessageType

        session = api_bp.db_manager.get_session()
        try:
            # Get today's date
            today = date.today()

            # Find the first fixture with matches starting today
            fixtures_with_today_start = session.query(MatchModel.fixture_id)\
                .filter(MatchModel.start_time.isnot(None))\
                .filter(MatchModel.start_time >= datetime.combine(today, datetime.min.time()))\
                .filter(MatchModel.start_time < datetime.combine(today, datetime.max.time()))\
                .order_by(MatchModel.created_at.asc())\
                .first()

            selected_fixture_id = None

            if fixtures_with_today_start:
                selected_fixture_id = fixtures_with_today_start.fixture_id
                logger.info(f"Selected fixture {selected_fixture_id} - has matches starting today")
            else:
                # Fallback: find fixtures where all matches are in pending status
                all_fixtures = session.query(MatchModel.fixture_id).distinct().all()

                for fixture_row in all_fixtures:
                    fixture_id = fixture_row.fixture_id

                    # Check if all matches in this fixture are pending, scheduled, or bet
                    fixture_matches = session.query(MatchModel).filter_by(fixture_id=fixture_id).all()

                    if fixture_matches and all(match.status in ['pending', 'scheduled', 'bet'] for match in fixture_matches):
                        selected_fixture_id = fixture_id
                        logger.info(f"Selected fixture {selected_fixture_id} - all matches are in playable status (pending/scheduled/bet)")
                        break

                # If no fixture with all pending matches found, use the first fixture by creation date
                if not selected_fixture_id:
                    first_fixture = session.query(MatchModel.fixture_id)\
                        .order_by(MatchModel.created_at.asc())\
                        .first()
                    if first_fixture:
                        selected_fixture_id = first_fixture.fixture_id
                        logger.info(f"Selected first fixture {selected_fixture_id} - fallback")

            if not selected_fixture_id:
                return jsonify({
                    "success": False,
                    "error": "No suitable fixture found"
                }), 404

            # Get all matches from the selected fixture
            fixture_matches = session.query(MatchModel)\
                .filter_by(fixture_id=selected_fixture_id)\
                .order_by(MatchModel.match_number.asc())\
                .all()

            if not fixture_matches:
                return jsonify({
                    "success": False,
                    "error": "No matches found in fixture"
                }), 404

            # Find the first match that needs to be started
            target_match = None

            # Priority 1: First match in "bet" status
            for match in fixture_matches:
                if match.status == 'bet':
                    target_match = match
                    break

            # Priority 2: First match in "scheduled" status
            if not target_match:
                for match in fixture_matches:
                    if match.status == 'scheduled':
                        target_match = match
                        break

            # Priority 3: First match in "pending" status
            if not target_match:
                for match in fixture_matches:
                    if match.status == 'pending':
                        target_match = match
                        break

            if not target_match:
                return jsonify({
                    "success": False,
                    "error": "No match available to start"
                }), 404

            # Send MATCH_START message to message bus
            if api_bp.message_bus:
                match_start_message = MessageBuilder.match_start(
                    sender="web_dashboard",
                    fixture_id=selected_fixture_id,
                    match_id=target_match.id
                )

                success = api_bp.message_bus.publish(match_start_message)

                if success:
                    logger.info(f"MATCH_START message sent for match {target_match.id} in fixture {selected_fixture_id}")

                    return jsonify({
                        "success": True,
                        "message": f"Match {target_match.match_number} started",
                        "fixture_id": selected_fixture_id,
                        "match_id": target_match.id,
                        "match_number": target_match.match_number
                    })
                else:
                    logger.error("Failed to publish MATCH_START message to message bus")
                    return jsonify({
                        "success": False,
                        "error": "Failed to send match start request"
                    }), 500
            else:
                logger.error("Message bus not available")
                return jsonify({
                    "success": False,
                    "error": "Message bus not available"
                }), 500

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API start next match error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/server-time')
@get_api_auth_decorator()
def get_server_time():
    """Get current server time"""
    try:
        from datetime import datetime
        # Use simple datetime.now() since it reports the correct local time
        server_time = datetime.now()

        return jsonify({
            "success": True,
            "server_time": server_time.isoformat(),
            "timestamp": int(server_time.timestamp() * 1000),  # milliseconds since epoch
            "formatted_time": server_time.strftime("%H:%M:%S")  # Pre-formatted time string
        })

    except Exception as e:
        logger.error(f"Server time API error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/notifications')
@get_api_auth_decorator()
def notifications():
    """Long polling endpoint for real-time notifications"""
    try:
        import time
        import threading
        import ssl
        import socket

        # Get timeout from query parameter (default 30 seconds)
        timeout = int(request.args.get('timeout', 30))
        if timeout > 60:  # Max 60 seconds
            timeout = 60

        # Create an event for timeout handling
        notification_received = threading.Event()

        # Check for pending notifications from the web dashboard
        if hasattr(g, 'main_app') and g.main_app and hasattr(g.main_app, 'web_dashboard'):
            # Register this event with the web dashboard so it gets notified when notifications arrive
            g.main_app.web_dashboard.register_waiting_client(notification_received)

            try:
                # First check for any pending notifications
                pending_notifications = g.main_app.web_dashboard.get_pending_notifications()
                if pending_notifications:
                    # Return pending notifications immediately
                    logger.debug(f"Returning {len(pending_notifications)} pending notifications to client")
                    response_data = {
                        "success": True,
                        "notifications": pending_notifications
                    }
                else:
                    # No pending notifications, wait for new ones or timeout
                    logger.debug(f"Waiting for notifications with {timeout}s timeout")
                    notification_received.wait(timeout=timeout)
                    # After waiting, check again for any notifications that arrived
                    pending_notifications = g.main_app.web_dashboard.get_pending_notifications()
                    if pending_notifications:
                        logger.debug(f"Returning {len(pending_notifications)} notifications after wait")
                        response_data = {
                            "success": True,
                            "notifications": pending_notifications
                        }
                    else:
                        # Timeout - return empty response
                        logger.debug("Notification wait timed out, returning empty response")
                        response_data = {
                            "success": True,
                            "notifications": []
                        }
            finally:
                # Always unregister the event when done
                g.main_app.web_dashboard.unregister_waiting_client(notification_received)
        else:
            # Web dashboard not available, return empty response
            response_data = {
                "success": True,
                "notifications": []
            }

        # Response data is already prepared above

        # Handle SSL/connection errors gracefully when sending response
        try:
            return jsonify(response_data)
        except (BrokenPipeError, ConnectionResetError):
            # Client disconnected during long-polling - this is expected
            logger.debug("Client disconnected during long-polling (BrokenPipeError/ConnectionResetError)")
            return "", 200  # Return empty response, connection already closed
        except ssl.SSLError as ssl_err:
            if "UNEXPECTED_EOF_WHILE_READING" in str(ssl_err) or "unexpected eof while reading" in str(ssl_err).lower():
                # SSL connection closed by client during long-polling - this is expected
                logger.debug("Client closed SSL connection during long-polling")
                return "", 200  # Return empty response, connection already closed
            else:
                # Unexpected SSL error
                logger.warning(f"Unexpected SSL error in notifications endpoint: {ssl_err}")
                raise
        except socket.error as sock_err:
            if hasattr(sock_err, 'errno') and sock_err.errno in (32, 104):  # SIGPIPE, ECONNRESET
                # Client connection reset during long-polling - this is expected
                logger.debug("Client connection reset during long-polling")
                return "", 200  # Return empty response, connection already closed
            else:
                # Unexpected socket error
                logger.warning(f"Unexpected socket error in notifications endpoint: {sock_err}")
                raise

    except Exception as e:
        logger.error(f"Notifications API error: {e}")
        # Even error responses can trigger SSL errors, so handle them too
        try:
            return jsonify({"error": str(e)}), 500
        except (BrokenPipeError, ConnectionResetError, ssl.SSLError):
            logger.debug("Client disconnected before error response could be sent")
            return "", 500


# Extraction API routes
@api_bp.route('/extraction/outcomes')
@get_api_auth_decorator()
def get_extraction_outcomes():
    """Get all available outcomes for extraction"""
    try:
        from ..database.models import MatchOutcomeModel

        session = api_bp.db_manager.get_session()
        try:
            # Get distinct outcome names from match outcomes
            outcomes_query = session.query(MatchOutcomeModel.column_name).distinct()
            outcomes = [row[0] for row in outcomes_query.all()]

            # Add UNDER and OVER outcomes if not present
            if 'UNDER' not in outcomes:
                outcomes.append('UNDER')
            if 'OVER' not in outcomes:
                outcomes.append('OVER')

            # Sort outcomes for consistent display
            outcomes.sort()

            return jsonify({
                "success": True,
                "outcomes": outcomes,
                "total": len(outcomes)
            })

        finally:
            logger.debug(f"DEBUG: Session being closed. Session is active: {session.is_active}")
            # Check if session has pending changes when closing
            if session.is_active and (session.new or session.dirty or session.deleted):
                logger.warning(f"DEBUG: Session has pending changes when closing: new={len(session.new)}, dirty={len(session.dirty)}, deleted={len(session.deleted)}")
                # Don't rollback here as it might interfere with successful commits
            session.close()
            logger.debug("DEBUG: Session closed")

    except Exception as e:
        logger.error(f"API get extraction outcomes error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/extraction/associations')
@get_api_auth_decorator()
def get_extraction_associations():
    """Get current extraction associations with defaults"""
    try:
        from ..database.models import ExtractionAssociationModel, MatchOutcomeModel

        session = api_bp.db_manager.get_session()
        try:
            associations = session.query(ExtractionAssociationModel).all()
            associations_data = [assoc.to_dict() for assoc in associations]

            return jsonify({
                "success": True,
                "associations": associations_data,
                "total": len(associations_data)
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API get extraction associations error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/extraction/associations', methods=['POST'])
@get_api_auth_decorator()
def save_extraction_associations():
    """Save extraction associations - unlimited associations per outcome"""
    try:
        from ..database.models import ExtractionAssociationModel

        data = request.get_json() or {}
        associations_data = data.get('associations', [])

        if not associations_data:
            return jsonify({"error": "No associations provided"}), 400

        # Log bulk association save attempt
        user_id = getattr(current_user, 'id', 'unknown') if current_user else 'unauthenticated'
        user_role = getattr(current_user, 'role', 'unknown') if current_user else 'unauthenticated'
        logger.info(f"EXTRACTION_ASSOCIATION_BULK_SAVE_ATTEMPT: user_id={user_id}, user_role={user_role}, "
                   f"association_count={len(associations_data)}, source=web_dashboard_api, "
                   f"method=bulk_save_associations")

        session = api_bp.db_manager.get_session()
        try:
            # Get existing associations before clearing for logging
            existing_associations = session.query(ExtractionAssociationModel).all()
            existing_count = len(existing_associations)

            # Clear existing associations
            session.query(ExtractionAssociationModel).delete()

            # Add new associations - no limit on number per outcome
            created_associations = []
            for assoc_data in associations_data:
                association = ExtractionAssociationModel(
                    outcome_name=assoc_data['outcome_name'],
                    extraction_result=assoc_data['extraction_result'],
                    is_default=False  # User-created associations are not default
                )
                session.add(association)
                created_associations.append(association)

            session.commit()

            # Log details of the bulk save operation
            logger.info(f"EXTRACTION_ASSOCIATION_BULK_SAVE_COMPLETED: user_id={user_id}, user_role={user_role}, "
                       f"cleared_count={existing_count}, saved_count={len(associations_data)}, "
                       f"source=web_dashboard_api, method=bulk_save_associations")

            # Log individual associations created
            for assoc in created_associations:
                logger.info(f"EXTRACTION_ASSOCIATION_CREATED: user_id={user_id}, "
                           f"outcome_name={assoc.outcome_name}, extraction_result={assoc.extraction_result}, "
                           f"association_id={assoc.id}, source=web_dashboard_api, method=bulk_save_associations")

            return jsonify({
                "success": True,
                "message": f"Saved {len(associations_data)} associations",
                "saved_count": len(associations_data)
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API save extraction associations error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/extraction/associations/add', methods=['POST'])
@get_api_auth_decorator()
def add_extraction_association():
    """Add a single extraction association"""
    try:
        from ..database.models import ExtractionAssociationModel, MatchOutcomeModel

        data = request.get_json() or {}
        outcome_name = data.get('outcome_name')
        extraction_result = data.get('extraction_result')

        if not outcome_name or not extraction_result:
            return jsonify({"error": "outcome_name and extraction_result are required"}), 400

        # Log association creation attempt with source information
        user_id = getattr(current_user, 'id', 'unknown') if current_user else 'unauthenticated'
        user_role = getattr(current_user, 'role', 'unknown') if current_user else 'unauthenticated'
        logger.info(f"EXTRACTION_ASSOCIATION_CREATION_ATTEMPT: user_id={user_id}, user_role={user_role}, "
                   f"outcome_name={outcome_name}, extraction_result={extraction_result}, "
                   f"source=web_dashboard_api, method=add_single_association")

        # Get valid extraction results from available outcomes
        session = api_bp.db_manager.get_session()
        try:
            # Get distinct outcome names from match outcomes
            outcomes_query = session.query(MatchOutcomeModel.column_name).distinct()
            valid_results = [row[0] for row in outcomes_query.all()]

            # Add UNDER and OVER outcomes if not present
            if 'UNDER' not in valid_results:
                valid_results.append('UNDER')
            if 'OVER' not in valid_results:
                valid_results.append('OVER')

            # Validate extraction_result values
            if extraction_result not in valid_results:
                logger.warning(f"EXTRACTION_ASSOCIATION_INVALID_RESULT: user_id={user_id}, "
                              f"outcome_name={outcome_name}, extraction_result={extraction_result}, "
                              f"valid_results={valid_results}")
                return jsonify({"error": f"extraction_result must be one of available outcomes: {', '.join(sorted(valid_results))}"}), 400

        finally:
            session.close()

        session = api_bp.db_manager.get_session()
        try:
            # Check if this exact association already exists
            existing_assoc = session.query(ExtractionAssociationModel).filter_by(
                outcome_name=outcome_name,
                extraction_result=extraction_result
            ).first()

            if existing_assoc:
                logger.warning(f"EXTRACTION_ASSOCIATION_DUPLICATE: user_id={user_id}, "
                              f"outcome_name={outcome_name}, extraction_result={extraction_result}, "
                              f"existing_id={existing_assoc.id}")
                return jsonify({
                    "success": False,
                    "error": f"Association already exists: {outcome_name} -> {extraction_result}"
                }), 400

            # No limit on number of associations per outcome - removed restriction

            # Create new association
            association = ExtractionAssociationModel(
                outcome_name=outcome_name,
                extraction_result=extraction_result,
                is_default=False
            )
            session.add(association)
            session.commit()

            logger.info(f"EXTRACTION_ASSOCIATION_CREATED: user_id={user_id}, user_role={user_role}, "
                       f"outcome_name={outcome_name}, extraction_result={extraction_result}, "
                       f"association_id={association.id}, source=web_dashboard_api, "
                       f"method=add_single_association")

            return jsonify({
                "success": True,
                "message": f"Association added: {outcome_name} -> {extraction_result}",
                "association": association.to_dict()
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API add extraction association error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/extraction/associations/remove', methods=['POST'])
@get_api_auth_decorator()
def remove_extraction_association():
    """Remove a specific extraction association"""
    try:
        from ..database.models import ExtractionAssociationModel

        data = request.get_json() or {}
        outcome_name = data.get('outcome_name')
        extraction_result = data.get('extraction_result')

        if not outcome_name or not extraction_result:
            return jsonify({"error": "outcome_name and extraction_result are required"}), 400

        # Log association removal attempt
        user_id = getattr(current_user, 'id', 'unknown') if current_user else 'unauthenticated'
        user_role = getattr(current_user, 'role', 'unknown') if current_user else 'unauthenticated'
        logger.info(f"EXTRACTION_ASSOCIATION_REMOVAL_ATTEMPT: user_id={user_id}, user_role={user_role}, "
                   f"outcome_name={outcome_name}, extraction_result={extraction_result}, "
                   f"source=web_dashboard_api, method=remove_single_association")

        session = api_bp.db_manager.get_session()
        try:
            # Find the specific association
            association = session.query(ExtractionAssociationModel).filter_by(
                outcome_name=outcome_name,
                extraction_result=extraction_result
            ).first()

            if not association:
                logger.warning(f"EXTRACTION_ASSOCIATION_NOT_FOUND: user_id={user_id}, "
                              f"outcome_name={outcome_name}, extraction_result={extraction_result}")
                return jsonify({
                    "success": False,
                    "error": f"Association not found: {outcome_name} -> {extraction_result}"
                }), 404

            # Log the association being removed
            logger.info(f"EXTRACTION_ASSOCIATION_REMOVED: user_id={user_id}, user_role={user_role}, "
                       f"outcome_name={outcome_name}, extraction_result={extraction_result}, "
                       f"association_id={association.id}, source=web_dashboard_api, "
                       f"method=remove_single_association")

            # Remove the association
            session.delete(association)
            session.commit()

            return jsonify({
                "success": True,
                "message": f"Association removed: {outcome_name} -> {extraction_result}"
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API remove extraction association error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/extraction/config')
@get_api_auth_decorator()
def get_extraction_config():
    """Get extraction configuration"""
    try:
        from ..database.models import GameConfigModel

        session = api_bp.db_manager.get_session()
        try:
            # Get all game config entries
            configs = session.query(GameConfigModel).all()
            config_data = {}

            for config in configs:
                config_data[config.config_key] = config.get_typed_value()

            return jsonify({
                "success": True,
                "config": config_data,
                "total": len(config_data)
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API get extraction config error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/extraction/config', methods=['POST'])
@get_api_auth_decorator()
def update_extraction_config():
    """Update extraction configuration"""
    try:
        from ..database.models import GameConfigModel

        data = request.get_json() or {}
        config_key = data.get('config_key')
        config_value = data.get('config_value')
        value_type = data.get('value_type', 'string')

        if not config_key or config_value is None:
            return jsonify({"error": "config_key and config_value are required"}), 400

        session = api_bp.db_manager.get_session()
        try:
            # Check if config exists
            existing_config = session.query(GameConfigModel).filter_by(config_key=config_key).first()

            if existing_config:
                # Update existing config
                existing_config.config_value = str(config_value)
                existing_config.value_type = value_type
                existing_config.updated_at = datetime.utcnow()
            else:
                # Create new config
                new_config = GameConfigModel(
                    config_key=config_key,
                    config_value=str(config_value),
                    value_type=value_type,
                    description=f"Extraction configuration for {config_key}",
                    is_system=False
                )
                session.add(new_config)

            session.commit()

            logger.info(f"Updated extraction config: {config_key} = {config_value}")

            return jsonify({
                "success": True,
                "message": f"Configuration {config_key} updated successfully",
                "config_key": config_key,
                "config_value": config_value
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API update extraction config error: {e}")
        return jsonify({"error": str(e)}), 500


# Betting Mode API routes
@api_bp.route('/betting-mode')
@get_api_auth_decorator()
def get_betting_mode():
    """Get global betting mode configuration"""
    try:
        from ..database.models import GameConfigModel

        session = api_bp.db_manager.get_session()
        try:
            # Get global betting mode configuration
            betting_mode_config = session.query(GameConfigModel).filter_by(
                config_key='betting_mode'
            ).first()

            if betting_mode_config:
                mode = betting_mode_config.get_typed_value()
                is_default = False
            else:
                # Return default if not set
                mode = 'all_bets_on_start'
                is_default = True

            return jsonify({
                "success": True,
                "betting_mode": mode,
                "is_default": is_default,
                "description": "Global system setting that determines how START_GAME affects match status"
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API get betting mode error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/betting-mode', methods=['POST'])
@get_api_auth_decorator()
@get_api_auth_decorator(require_admin=True)
def set_betting_mode():
    """Set global betting mode configuration (admin only)"""
    try:
        from ..database.models import GameConfigModel

        data = request.get_json() or {}
        mode = data.get('betting_mode')

        if not mode:
            return jsonify({"error": "betting_mode is required"}), 400

        # Validate mode
        valid_modes = ['all_bets_on_start', 'one_bet_at_a_time']
        if mode not in valid_modes:
            return jsonify({"error": f"Invalid betting mode. Must be one of: {', '.join(valid_modes)}"}), 400

        session = api_bp.db_manager.get_session()
        try:
            # Check if betting mode config already exists
            existing_config = session.query(GameConfigModel).filter_by(
                config_key='betting_mode'
            ).first()

            if existing_config:
                # Update existing config
                existing_config.set_typed_value(mode)
                existing_config.updated_at = datetime.utcnow()
            else:
                # Create new global betting mode config
                new_config = GameConfigModel(
                    config_key='betting_mode',
                    config_value=mode,
                    value_type='string',
                    description='Global betting mode: all_bets_on_start or one_bet_at_a_time',
                    is_system=True
                )
                session.add(new_config)

            session.commit()

            logger.info(f"Updated global betting mode: {mode}")

            return jsonify({
                "success": True,
                "message": f"Global betting mode set to '{mode}'",
                "betting_mode": mode
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API set betting mode error: {e}")
        return jsonify({"error": str(e)}), 500


# Extraction Configuration Persistence API routes
@api_bp.route('/extraction/under-over-config')
@get_api_auth_decorator()
def get_under_over_config():
    """Get UNDER/OVER zone configurations with defaults"""
    try:
        from ..database.models import GameConfigModel, MatchOutcomeModel
        import json

        session = api_bp.db_manager.get_session()
        try:
            # Get available outcomes from database
            available_outcomes_query = session.query(MatchOutcomeModel.column_name).distinct()
            available_outcomes = [row[0] for row in available_outcomes_query.all()]

            # Get saved config
            under_config = session.query(GameConfigModel).filter_by(
                config_key='extraction_under_outcomes'
            ).first()
            over_config = session.query(GameConfigModel).filter_by(
                config_key='extraction_over_outcomes'
            ).first()

            under_outcomes = []
            over_outcomes = []

            if under_config:
                try:
                    under_outcomes = json.loads(under_config.config_value)
                except (json.JSONDecodeError, TypeError):
                    under_outcomes = []

            if over_config:
                try:
                    over_outcomes = json.loads(over_config.config_value)
                except (json.JSONDecodeError, TypeError):
                    over_outcomes = []

            # Apply defaults if no configuration exists
            if not under_outcomes and not over_outcomes:
                # Set defaults: UNDER -> UNDER zone, OVER -> OVER zone (if they exist)
                if 'UNDER' in available_outcomes:
                    under_outcomes = ['UNDER']
                if 'OVER' in available_outcomes:
                    over_outcomes = ['OVER']

                # Save the defaults if they were applied
                if under_outcomes or over_outcomes:
                    if under_outcomes:
                        if under_config:
                            under_config.config_value = json.dumps(under_outcomes)
                            under_config.updated_at = datetime.utcnow()
                        else:
                            under_config = GameConfigModel(
                                config_key='extraction_under_outcomes',
                                config_value=json.dumps(under_outcomes),
                                value_type='json',
                                description='Outcomes assigned to UNDER zone for time extraction',
                                is_system=False
                            )
                            session.add(under_config)

                    if over_outcomes:
                        if over_config:
                            over_config.config_value = json.dumps(over_outcomes)
                            over_config.updated_at = datetime.utcnow()
                        else:
                            over_config = GameConfigModel(
                                config_key='extraction_over_outcomes',
                                config_value=json.dumps(over_outcomes),
                                value_type='json',
                                description='Outcomes assigned to OVER zone for time extraction',
                                is_system=False
                            )
                            session.add(over_config)

                    session.commit()
                    logger.info(f"Applied default UNDER/OVER config: UNDER={under_outcomes}, OVER={over_outcomes}")

            return jsonify({
                "success": True,
                "under_outcomes": under_outcomes,
                "over_outcomes": over_outcomes
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API get under/over config error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/extraction/under-over-config', methods=['POST'])
@get_api_auth_decorator()
def save_under_over_config():
    """Save UNDER/OVER zone configurations"""
    try:
        from ..database.models import GameConfigModel
        import json

        data = request.get_json() or {}
        under_outcomes = data.get('under_outcomes', [])
        over_outcomes = data.get('over_outcomes', [])

        session = api_bp.db_manager.get_session()
        try:
            # Save UNDER outcomes
            under_config = session.query(GameConfigModel).filter_by(
                config_key='extraction_under_outcomes'
            ).first()
            
            if under_config:
                under_config.config_value = json.dumps(under_outcomes)
                under_config.updated_at = datetime.utcnow()
            else:
                under_config = GameConfigModel(
                    config_key='extraction_under_outcomes',
                    config_value=json.dumps(under_outcomes),
                    value_type='json',
                    description='Outcomes assigned to UNDER zone for time extraction',
                    is_system=False
                )
                session.add(under_config)

            # Save OVER outcomes
            over_config = session.query(GameConfigModel).filter_by(
                config_key='extraction_over_outcomes'
            ).first()
            
            if over_config:
                over_config.config_value = json.dumps(over_outcomes)
                over_config.updated_at = datetime.utcnow()
            else:
                over_config = GameConfigModel(
                    config_key='extraction_over_outcomes',
                    config_value=json.dumps(over_outcomes),
                    value_type='json',
                    description='Outcomes assigned to OVER zone for time extraction',
                    is_system=False
                )
                session.add(over_config)

            session.commit()

            logger.info(f"Saved UNDER/OVER config: UNDER={len(under_outcomes)}, OVER={len(over_outcomes)}")

            return jsonify({
                "success": True,
                "message": "UNDER/OVER configuration saved successfully"
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API save under/over config error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/extraction/results-config')
@get_api_auth_decorator()
def get_results_config():
    """Get Results area configurations with defaults"""
    try:
        from ..database.models import GameConfigModel, MatchOutcomeModel
        import json

        session = api_bp.db_manager.get_session()
        try:
            # Get available outcomes from database
            available_outcomes_query = session.query(MatchOutcomeModel.column_name).distinct()
            available_outcomes = [row[0] for row in available_outcomes_query.all()]

            # Get saved config
            results_config = session.query(GameConfigModel).filter_by(
                config_key='extraction_results_outcomes'
            ).first()

            results_outcomes = []

            if results_config:
                try:
                    results_outcomes = json.loads(results_config.config_value)
                except (json.JSONDecodeError, TypeError):
                    results_outcomes = []

            # Apply defaults if no configuration exists
            if not results_outcomes:
                # Default results outcomes (only if they exist in database)
                default_results = ['DRAW', 'DKO', 'KO1', 'KO2', 'PTS1', 'PTS2', 'RET1', 'RET2']
                results_outcomes = [outcome for outcome in default_results if outcome in available_outcomes]

                # Save the defaults if any were found
                if results_outcomes:
                    if results_config:
                        results_config.config_value = json.dumps(results_outcomes)
                        results_config.updated_at = datetime.utcnow()
                    else:
                        results_config = GameConfigModel(
                            config_key='extraction_results_outcomes',
                            config_value=json.dumps(results_outcomes),
                            value_type='json',
                            description='Outcomes in Results area that create association columns',
                            is_system=False
                        )
                        session.add(results_config)

                    session.commit()
                    logger.info(f"Applied default Results config: {results_outcomes}")

            return jsonify({
                "success": True,
                "results_outcomes": results_outcomes
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API get results config error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/extraction/results-config', methods=['POST'])
@get_api_auth_decorator()
def save_results_config():
    """Save Results area configurations"""
    try:
        from ..database.models import GameConfigModel
        import json

        data = request.get_json() or {}
        results_outcomes = data.get('results_outcomes', [])

        session = api_bp.db_manager.get_session()
        try:
            # Save Results outcomes
            results_config = session.query(GameConfigModel).filter_by(
                config_key='extraction_results_outcomes'
            ).first()
            
            if results_config:
                results_config.config_value = json.dumps(results_outcomes)
                results_config.updated_at = datetime.utcnow()
            else:
                results_config = GameConfigModel(
                    config_key='extraction_results_outcomes',
                    config_value=json.dumps(results_outcomes),
                    value_type='json',
                    description='Outcomes in Results area that create association columns',
                    is_system=False
                )
                session.add(results_config)

            session.commit()

            logger.info(f"Saved Results config: {len(results_outcomes)} outcomes")

            return jsonify({
                "success": True,
                "message": "Results configuration saved successfully"
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API save results config error: {e}")
        return jsonify({"error": str(e)}), 500


# Available Bets API routes
@api_bp.route('/extraction/available-bets')
@get_api_auth_decorator()
def get_available_bets():
    """Get all available bets for extraction"""
    try:
        from ..database.models import AvailableBetModel

        session = api_bp.db_manager.get_session()
        try:
            # Get all available bets
            bets = session.query(AvailableBetModel).order_by(AvailableBetModel.bet_name.asc()).all()
            bets_data = [bet.to_dict() for bet in bets]

            return jsonify({
                "success": True,
                "bets": bets_data,
                "total": len(bets_data)
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API get available bets error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/extraction/available-bets/add', methods=['POST'])
@get_api_auth_decorator()
def add_available_bet():
    """Add a new available bet"""
    try:
        from ..database.models import AvailableBetModel

        data = request.get_json() or {}
        bet_name = data.get('bet_name', '').strip().upper()
        description = data.get('description', '').strip()

        if not bet_name:
            return jsonify({"error": "bet_name is required"}), 400

        if len(bet_name) > 50:
            return jsonify({"error": "bet_name must be 50 characters or less"}), 400

        session = api_bp.db_manager.get_session()
        try:
            # Check if bet already exists
            existing_bet = session.query(AvailableBetModel).filter_by(bet_name=bet_name).first()
            if existing_bet:
                return jsonify({"error": f"Bet '{bet_name}' already exists"}), 400

            # Create new bet
            new_bet = AvailableBetModel(
                bet_name=bet_name,
                description=description or f"Bet option: {bet_name}"
            )
            session.add(new_bet)
            session.commit()

            logger.info(f"Added available bet: {bet_name}")

            return jsonify({
                "success": True,
                "message": f"Bet '{bet_name}' added successfully",
                "bet": new_bet.to_dict()
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API add available bet error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/extraction/available-bets/delete', methods=['POST'])
@get_api_auth_decorator()
def delete_available_bet():
    """Delete an available bet and all associated associations"""
    try:
        from ..database.models import AvailableBetModel, ExtractionAssociationModel

        data = request.get_json() or {}
        bet_name = data.get('bet_name', '').strip().upper()

        if not bet_name:
            return jsonify({"error": "bet_name is required"}), 400

        session = api_bp.db_manager.get_session()
        try:
            # Find the bet
            bet = session.query(AvailableBetModel).filter_by(bet_name=bet_name).first()
            if not bet:
                return jsonify({"error": f"Bet '{bet_name}' not found"}), 404

            # Delete all associations that use this bet as extraction_result
            associations_deleted = session.query(ExtractionAssociationModel).filter_by(
                extraction_result=bet_name
            ).delete()

            # Delete the bet
            session.delete(bet)
            session.commit()

            logger.info(f"Deleted available bet: {bet_name} and {associations_deleted} associated associations")

            return jsonify({
                "success": True,
                "message": f"Bet '{bet_name}' deleted successfully",
                "associations_removed": associations_deleted
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API delete available bet error: {e}")
        return jsonify({"error": str(e)}), 500


# Result Options API routes
@api_bp.route('/extraction/result-options')
@get_api_auth_decorator()
def get_result_options():
    """Get all result options for extraction"""
    try:
        from ..database.models import ResultOptionModel

        session = api_bp.db_manager.get_session()
        try:
            # Get all result options
            result_options = session.query(ResultOptionModel).order_by(ResultOptionModel.result_name.asc()).all()
            options_data = [option.to_dict() for option in result_options]

            return jsonify({
                "success": True,
                "options": options_data,
                "total": len(options_data)
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API get result options error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/extraction/result-options/add', methods=['POST'])
@get_api_auth_decorator()
def add_result_option():
    """Add a new result option"""
    try:
        from ..database.models import ResultOptionModel

        data = request.get_json() or {}
        result_name = data.get('result_name', '').strip().upper()
        description = data.get('description', '').strip()

        if not result_name:
            return jsonify({"error": "result_name is required"}), 400

        if len(result_name) > 50:
            return jsonify({"error": "result_name must be 50 characters or less"}), 400

        session = api_bp.db_manager.get_session()
        try:
            # Check if result option already exists
            existing_option = session.query(ResultOptionModel).filter_by(result_name=result_name).first()
            if existing_option:
                return jsonify({"error": f"Result option '{result_name}' already exists"}), 400

            # Get the highest sort order
            max_sort = session.query(ResultOptionModel.sort_order).order_by(ResultOptionModel.sort_order.desc()).first()
            sort_order = (max_sort[0] + 1) if max_sort and max_sort[0] else 1

            # Create new result option
            new_option = ResultOptionModel(
                result_name=result_name,
                description=description or f"Result option: {result_name}",
                sort_order=sort_order
            )
            session.add(new_option)
            session.commit()

            logger.info(f"Added result option: {result_name}")

            return jsonify({
                "success": True,
                "message": f"Result option '{result_name}' added successfully",
                "option": new_option.to_dict()
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API add result option error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/extraction/result-options/delete', methods=['POST'])
@get_api_auth_decorator()
def delete_result_option():
    """Delete a result option and all associated associations"""
    try:
        from ..database.models import ResultOptionModel, ExtractionAssociationModel

        data = request.get_json() or {}
        result_name = data.get('result_name', '').strip().upper()

        if not result_name:
            return jsonify({"error": "result_name is required"}), 400

        session = api_bp.db_manager.get_session()
        try:
            # Find the result option
            option = session.query(ResultOptionModel).filter_by(result_name=result_name).first()
            if not option:
                return jsonify({"error": f"Result option '{result_name}' not found"}), 404

            # Delete all associations that use this result option
            associations_deleted = session.query(ExtractionAssociationModel).filter_by(
                extraction_result=result_name
            ).delete()

            # Delete the result option
            session.delete(option)
            session.commit()

            logger.info(f"Deleted result option: {result_name} and {associations_deleted} associated associations")

            return jsonify({
                "success": True,
                "message": f"Result option '{result_name}' deleted successfully",
                "associations_removed": associations_deleted
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API delete result option error: {e}")
        return jsonify({"error": str(e)}), 500




# Redistribution CAP API routes (admin-only)
@api_bp.route('/extraction/redistribution-cap')
@get_api_auth_decorator()
@get_api_auth_decorator(require_admin=True)
def get_redistribution_cap():
    """Get redistribution CAP configuration (admin only)"""
    try:
        from ..database.models import GameConfigModel

        session = api_bp.db_manager.get_session()
        try:
            # Get redistribution cap from config
            cap_config = session.query(GameConfigModel).filter_by(
                config_key='extraction_redistribution_cap'
            ).first()

            redistribution_cap = 70  # Default 70%

            if cap_config:
                try:
                    redistribution_cap = float(cap_config.config_value)
                except (ValueError, TypeError):
                    redistribution_cap = 70

            return jsonify({
                "success": True,
                "redistribution_cap": redistribution_cap
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API get redistribution cap error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/extraction/redistribution-cap', methods=['POST'])
@get_api_auth_decorator()
@get_api_auth_decorator(require_admin=True)
def save_redistribution_cap():
    """Save redistribution CAP configuration (admin only)"""
    try:
        from ..database.models import GameConfigModel

        data = request.get_json() or {}
        redistribution_cap = data.get('redistribution_cap')

        if redistribution_cap is None:
            return jsonify({"error": "redistribution_cap is required"}), 400

        # Validate range (10% to 100%)
        try:
            cap_value = float(redistribution_cap)
            if cap_value < 10 or cap_value > 100:
                return jsonify({"error": "Redistribution CAP must be between 10% and 100%"}), 400
        except (ValueError, TypeError):
            return jsonify({"error": "Redistribution CAP must be a valid number"}), 400

        session = api_bp.db_manager.get_session()
        try:
            # Save redistribution cap
            cap_config = session.query(GameConfigModel).filter_by(
                config_key='extraction_redistribution_cap'
            ).first()
            
            if cap_config:
                cap_config.config_value = str(cap_value)
                cap_config.updated_at = datetime.utcnow()
            else:
                cap_config = GameConfigModel(
                    config_key='extraction_redistribution_cap',
                    config_value=str(cap_value),
                    value_type='float',
                    description='Maximum redistribution percentage for extraction (10-100%)',
                    is_system=False
                )
                session.add(cap_config)

            session.commit()

            logger.info(f"Saved redistribution cap: {cap_value}%")

            return jsonify({
                "success": True,
                "message": f"Redistribution CAP set to {cap_value}%",
                "redistribution_cap": cap_value
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API save redistribution cap error: {e}")
        return jsonify({"error": str(e)}), 500


# Redistribution Balance API routes (admin-only)
@api_bp.route('/redistribution-balance')
@get_api_auth_decorator()
@get_api_auth_decorator(require_admin=True)
def get_redistribution_balance():
    """Get current redistribution balance (admin only)"""
    try:
        from ..database.models import PersistentRedistributionAdjustmentModel
        from datetime import date

        session = api_bp.db_manager.get_session()
        try:
            # Get the global redistribution adjustment record (fixed date 1970-01-01)
            global_date = date(1970, 1, 1)
            global_record = session.query(PersistentRedistributionAdjustmentModel)\
                .filter_by(date=global_date)\
                .first()

            current_balance = 0.0
            if global_record:
                current_balance = float(global_record.accumulated_shortfall)

            return jsonify({
                "success": True,
                "redistribution_balance": current_balance,
                "last_updated": global_record.updated_at.isoformat() if global_record else None
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API get redistribution balance error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/redistribution-balance/reset', methods=['POST'])
@get_api_auth_decorator()
@get_api_auth_decorator(require_admin=True)
def reset_redistribution_balance():
    """Reset redistribution balance to zero (admin only)"""
    try:
        from ..database.models import PersistentRedistributionAdjustmentModel
        from datetime import date, datetime

        session = api_bp.db_manager.get_session()
        try:
            # Get the global redistribution adjustment record (fixed date 1970-01-01)
            global_date = date(1970, 1, 1)
            global_record = session.query(PersistentRedistributionAdjustmentModel)\
                .filter_by(date=global_date)\
                .first()

            if global_record:
                # Reset the accumulated shortfall to zero
                old_balance = float(global_record.accumulated_shortfall)
                global_record.accumulated_shortfall = 0.0
                global_record.updated_at = datetime.utcnow()
                session.commit()

                logger.info(f"Global redistribution balance reset from {old_balance} to 0.0")

                return jsonify({
                    "success": True,
                    "message": f"Redistribution balance reset from {old_balance:.2f} to 0.00",
                    "old_balance": old_balance,
                    "new_balance": 0.0
                })
            else:
                # No global record exists, create one with zero balance
                new_record = PersistentRedistributionAdjustmentModel(
                    date=global_date,
                    accumulated_shortfall=0.0,
                    cap_percentage=70.0  # Default cap
                )
                session.add(new_record)
                session.commit()

                logger.info("Created global redistribution adjustment record with zero balance")

                return jsonify({
                    "success": True,
                    "message": "Redistribution balance set to 0.00 (global record created)",
                    "old_balance": None,
                    "new_balance": 0.0
                })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API reset redistribution balance error: {e}")
        return jsonify({"error": str(e)}), 500


# Currency Settings API routes
@api_bp.route('/currency-settings')
def get_currency_settings():
    """Get currency symbol configuration"""
    try:
        if api_bp.config_manager:
            currency_config = api_bp.config_manager.get_section_config("currency") or {}
            symbol = currency_config.get('symbol', 'USh')  # Default to Ugandan Shilling
            position = currency_config.get('position', 'before')  # Default to before amount
            primary_currency_id = currency_config.get('primary_currency_id')
        else:
            symbol = 'USh'
            position = 'before'
            primary_currency_id = None

        return jsonify({
            "success": True,
            "settings": {
                "symbol": symbol,
                "position": position,
                "primary_currency_id": primary_currency_id
            }
        })
    except Exception as e:
        logger.error(f"API get currency settings error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/currency-settings', methods=['POST'])
@get_api_auth_decorator()
@get_api_auth_decorator(require_admin=True)
def set_currency_settings():
    """Set currency symbol configuration (admin only)"""
    try:
        data = request.get_json() or {}
        symbol = data.get('symbol')
        position = data.get('position')
        primary_currency_id = data.get('primary_currency_id')

        if not symbol:
            return jsonify({"error": "Currency symbol is required"}), 400

        if position not in ['before', 'after']:
            return jsonify({"error": "Position must be 'before' or 'after'"}), 400

        if api_bp.config_manager:
            # Build currency configuration
            currency_config = {
                "symbol": symbol,
                "position": position
            }
            
            # Add primary currency ID if provided
            if primary_currency_id:
                currency_config['primary_currency_id'] = primary_currency_id
                
                # Also update the currency in the currencies table to mark it as primary
                if api_bp.db_manager:
                    session = api_bp.db_manager.get_session()
                    try:
                        from ..database.models import CurrencyModel
                        # First, clear is_primary from all currencies
                        session.query(CurrencyModel).update({"is_primary": False})
                        # Then set the selected currency as primary
                        currency = session.query(CurrencyModel).filter_by(id=primary_currency_id).first()
                        if currency:
                            currency.is_primary = True
                            session.commit()
                            logger.info(f"Set currency {currency.code} as primary")
                    except Exception as e:
                        logger.error(f"Error updating primary currency: {e}")
                        session.rollback()
                    finally:
                        session.close()
            
            # Save currency configuration
            result = api_bp.config_manager.update_section("currency", currency_config)

            if result:
                logger.info(f"Currency settings updated - symbol: {symbol}, position: {position}, primary_currency_id: {primary_currency_id}")

                return jsonify({
                    "success": True,
                    "message": f"Currency settings updated: {symbol} ({position} amount)",
                    "settings": {
                        "symbol": symbol,
                        "position": position,
                        "primary_currency_id": primary_currency_id
                    }
                })
            else:
                return jsonify({"error": "Failed to update currency settings"}), 500
        else:
            return jsonify({"error": "Configuration manager not available"}), 500

    except Exception as e:
        logger.error(f"API set currency settings error: {e}")
        return jsonify({"error": str(e)}), 500


# Server-side Match Timer API endpoints
@api_bp.route('/match-timer/state')
@get_api_auth_decorator()
def get_match_timer_state():
    """Get current match timer state from server-side component"""
    try:
        # Get timer state from web dashboard (which caches responses from match_timer)
        if hasattr(g, 'main_app') and g.main_app and hasattr(g.main_app, 'web_dashboard'):
            timer_state = g.main_app.web_dashboard.get_timer_state()

            return jsonify({
                "success": True,
                "running": timer_state.get("running", False),
                "remaining_seconds": timer_state.get("remaining_seconds", 0),
                "total_seconds": timer_state.get("total_seconds", 0),
                "fixture_id": timer_state.get("fixture_id"),
                "match_id": timer_state.get("match_id"),
                "start_time": timer_state.get("start_time"),
                "elapsed_seconds": timer_state.get("elapsed_seconds", 0)
            })
        else:
            return jsonify({
                "success": False,
                "error": "Web dashboard not available"
            }), 500

    except Exception as e:
        logger.error(f"API get match timer state error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/match-timer/control', methods=['POST'])
@get_api_auth_decorator()
@get_api_auth_decorator(require_admin=True)
def control_match_timer():
    """Control the match timer (admin only)"""
    try:
        data = request.get_json() or {}
        action = data.get('action')

        if not action:
            return jsonify({"error": "Action is required"}), 400

        if action == 'start':
            # Send SCHEDULE_GAMES message to start the timer
            if api_bp.message_bus:
                from ..core.message_bus import MessageBuilder, MessageType

                start_message = MessageBuilder.schedule_games(sender="web_dashboard")
                api_bp.message_bus.publish(start_message)

                return jsonify({
                    "success": True,
                    "message": "Match timer started"
                })
            else:
                return jsonify({
                    "success": False,
                    "error": "Message bus not available"
                }), 500

        elif action == 'stop':
            # Send custom stop message to timer component
            if api_bp.message_bus:
                from ..core.message_bus import Message, MessageType

                stop_message = Message(
                    type=MessageType.CUSTOM,
                    sender="web_dashboard",
                    recipient="match_timer",
                    data={
                        "request": "stop",
                        "timestamp": time.time()
                    }
                )
                api_bp.message_bus.publish(stop_message)

                return jsonify({
                    "success": True,
                    "message": "Match timer stop requested"
                })
            else:
                return jsonify({
                    "success": False,
                    "error": "Message bus not available"
                }), 500

        else:
            return jsonify({"error": f"Unknown action: {action}"}), 400

    except Exception as e:
        logger.error(f"API control match timer error: {e}")
        return jsonify({"error": str(e)}), 500


# Cashier Betting API endpoints
@api_bp.route('/cashier/bets')
@get_api_auth_decorator()
def get_cashier_bets():
    """Get bets for a specific date (cashier)"""
    try:
        from ..database.models import BetModel, BetDetailModel
        from datetime import datetime, date, timezone, timedelta

        # Get date parameter (default to today)
        date_param = request.args.get('date')
        if date_param:
            try:
                target_date = datetime.strptime(date_param, '%Y-%m-%d').date()
            except ValueError:
                return jsonify({"error": "Invalid date format. Use YYYY-MM-DD"}), 400
        else:
            target_date = date.today()

        # Get filter parameters
        status_filter = request.args.get('status', '').strip()
        match_number_filter = request.args.get('match_number', '').strip()

        session = api_bp.db_manager.get_session()
        try:
            # Import models
            from ..database.models import BetModel, BetDetailModel, MatchModel

            # Get all bets for the target date
            # The date picker sends dates in YYYY-MM-DD format representing local dates
            # Since the user is in UTC+2, we need to find bets from the previous day in UTC
            # For example, if user selects "2025-12-17", they want bets from 2025-12-17 in local time
            # Which is 2025-12-16 22:00 UTC to 2025-12-17 21:59 UTC

            # Create the date range using venue timezone
            venue_tz = get_venue_timezone(api_bp.db_manager)
            local_start = datetime.combine(target_date, datetime.min.time())
            local_end = datetime.combine(target_date, datetime.max.time())

            # Convert venue local time to UTC for database queries
            start_datetime = venue_to_utc_datetime(local_start, api_bp.db_manager)
            end_datetime = venue_to_utc_datetime(local_end, api_bp.db_manager)

            logger.info(f"Querying bets for local date {date_param}: naive UTC range {start_datetime} to {end_datetime}")

            bets_query = session.query(BetModel).filter(
                BetModel.bet_datetime >= start_datetime,
                BetModel.bet_datetime <= end_datetime
            ).order_by(BetModel.bet_datetime.desc())

            # Apply match number filter if specified
            if match_number_filter:
                try:
                    match_number = int(match_number_filter)
                    # Filter bets that have details for the specified match number
                    bets_query = bets_query.join(BetDetailModel).join(MatchModel).filter(
                        MatchModel.match_number == match_number
                    ).distinct()
                except ValueError:
                    return jsonify({"error": "Invalid match number format"}), 400

            bets = bets_query.all()
            logger.info(f"Found {len(bets)} bets in database for date {date_param}")
            bets_data = []

            # Statistics counters
            total_amount = 0.0
            won_bets = 0
            lost_bets = 0
            pending_bets = 0

            for bet in bets:
                bet_data = bet.to_dict()
                bet_data['paid'] = bet.paid  # Include paid status
                
                # Get bet details with match information
                bet_details = session.query(BetDetailModel).filter_by(bet_id=bet.uuid).all()
                bet_details_data = []
                
                for detail in bet_details:
                    # Get match information
                    match = session.query(MatchModel).filter_by(id=detail.match_id).first()
                    
                    # Parse winning_outcomes JSON string if it exists
                    winning_outcomes = []
                    if match and match.winning_outcomes:
                        try:
                            if isinstance(match.winning_outcomes, str):
                                winning_outcomes = json.loads(match.winning_outcomes)
                            elif isinstance(match.winning_outcomes, list):
                                winning_outcomes = match.winning_outcomes
                        except (json.JSONDecodeError, TypeError):
                            winning_outcomes = []
                    
                    # Get odds for this outcome
                    odds = 0.0
                    if match:
                        outcomes_dict = match.get_outcomes_dict()
                        odds = outcomes_dict.get(detail.outcome, 0.0)
                    
                    detail_dict = {
                        'id': detail.id,
                        'match_id': detail.match_id,
                        'outcome': detail.outcome,
                        'amount': float(detail.amount),
                        'odds': float(odds),
                        'potential_winning': float(detail.amount) * float(odds),
                        'result': detail.result,
                        'match': {
                            'match_number': match.match_number if match else 'Unknown',
                            'fighter1_township': match.fighter1_township if match else 'Unknown',
                            'fighter2_township': match.fighter2_township if match else 'Unknown',
                            'venue_kampala_township': match.venue_kampala_township if match else 'Unknown',
                            'status': match.status if match else 'Unknown',
                            'winning_outcomes': winning_outcomes
                        } if match else None
                    }
                    
                    bet_details_data.append(detail_dict)
                
                bet_data['details'] = bet_details_data

                # Calculate total amount for this bet
                bet_total = sum(float(detail.amount) for detail in bet_details)
                bet_data['total_amount'] = bet_total

                # Determine overall bet status for filtering and statistics using same logic as bet details page
                overall_status = 'pending'
                if bet_details:
                    won_count = 0
                    lost_count = 0
                    pending_count = 0
                    cancelled_count = 0

                    for detail in bet_details:
                        # Get match information for winning check
                        match = session.query(MatchModel).filter_by(id=detail.match_id).first()

                        if is_settled_bet_detail_winning(detail, match, session):
                            won_count += 1
                        elif detail.result == 'lost':
                            lost_count += 1
                        elif detail.result == 'pending':
                            pending_count += 1
                        elif detail.result == 'cancelled':
                            cancelled_count += 1

                    # Determine overall status
                    if pending_count > 0:
                        overall_status = 'pending'
                    elif won_count > 0 and lost_count == 0 and cancelled_count == 0:
                        overall_status = 'won'
                    elif lost_count > 0 or cancelled_count > 0:
                        overall_status = 'lost'
                    elif cancelled_count > 0:
                        overall_status = 'cancelled'

                bet_data['overall_status'] = overall_status

                # Apply status filter if specified
                if status_filter and status_filter != overall_status:
                    continue  # Skip this bet if it doesn't match the status filter

                total_amount += bet_total

                # Update statistics counters
                if overall_status == 'won':
                    won_bets += 1
                elif overall_status == 'lost':
                    lost_bets += 1
                else:
                    pending_bets += 1

                bets_data.append(bet_data)

            # Calculate statistics
            stats = {
                'total_bets': len(bets_data),
                'total_amount': total_amount,
                'won_bets': won_bets,
                'lost_bets': lost_bets,
                'pending_bets': pending_bets
            }

            return jsonify({
                "success": True,
                "bets": bets_data,
                "total": len(bets_data),
                "date": target_date.isoformat(),
                "stats": stats
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API get cashier bets error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/cashier/bets/test-simple', methods=['POST'])
@get_api_auth_decorator(require_admin=True)
def create_test_bet_simple():
    """Test endpoint: Create a bet without bet_details to isolate the issue"""
    try:
        from ..database.models import BetModel
        from datetime import datetime
        import uuid as uuid_lib

        # Get database session
        session = api_bp.db_manager.get_session()

        # Force enable foreign keys for this session
        try:
            session.execute(text("PRAGMA foreign_keys=ON"))
            logger.debug("Foreign keys enabled for test session")
        except Exception as fk_e:
            logger.warning(f"Failed to enable foreign keys: {fk_e}")

        try:
            # Generate UUID for the bet
            bet_uuid = str(uuid_lib.uuid4())

            # Create a simple bet without bet_details
            bet_datetime = datetime.utcnow()
            logger.info(f"TEST: Creating simple bet with datetime: {bet_datetime}")

            new_bet = BetModel(
                uuid=bet_uuid,
                bet_datetime=bet_datetime,
                fixture_id="test-fixture-123"  # Dummy fixture_id
            )
            session.add(new_bet)
            session.flush()

            # Test commit
            session.commit()

            # Verify bet was committed
            committed_bet = session.query(BetModel).filter_by(uuid=bet_uuid).first()

            if committed_bet:
                logger.info(f"TEST: Simple bet {bet_uuid} created and committed successfully")
                return jsonify({
                    "success": True,
                    "message": "Simple bet created successfully (no bet_details)",
                    "bet_id": bet_uuid
                })
            else:
                logger.error(f"TEST: Simple bet {bet_uuid} commit appeared successful but bet not found")
                return jsonify({"error": "Simple bet commit failed - bet not persisted"}), 500

        finally:
            session.close()

    except Exception as e:
        logger.error(f"TEST: Simple bet creation error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/cashier/bets', methods=['POST'])
@get_api_auth_decorator()
def create_cashier_bet():
    """Create a new bet (cashier) - thread-safe implementation"""
    try:
        from ..database.models import BetModel, BetDetailModel
        from datetime import datetime
        import uuid as uuid_lib
        import threading

        data = request.get_json() or {}
        bet_details = data.get('bet_details', [])

        if not bet_details:
            return jsonify({"error": "Bet details are required"}), 400

        # Validate bet details
        for detail in bet_details:
            if not all(key in detail for key in ['match_id', 'outcome', 'amount']):
                return jsonify({"error": "Each bet detail must have match_id, outcome, and amount"}), 400

            try:
                float(detail['amount'])
            except (ValueError, TypeError):
                return jsonify({"error": "Amount must be a valid number"}), 400

        # Log threading context for debugging
        current_thread = threading.current_thread()
        logger.debug(f"Bet creation started on thread: {current_thread.name} (ID: {current_thread.ident})")
        logger.debug(f"Is main thread: {current_thread is threading.main_thread()}")

        # Get database session - scoped_session should handle thread-local sessions
        session = api_bp.db_manager.get_session()

        try:
            # Validate match_id exists for all bet details
            from ..database.models import MatchModel
            for detail in bet_details:
                match_id = detail['match_id']
                existing_match = session.query(MatchModel).filter_by(id=match_id).first()
                if not existing_match:
                    return jsonify({"error": f"Match {match_id} not found"}), 404
                # Store match_number for later use
                detail['_match_number'] = existing_match.match_number

            # Generate UUID for the bet
            bet_uuid = str(uuid_lib.uuid4())

            # Ensure UUID is unique
            max_attempts = 10
            attempt = 0
            while attempt < max_attempts:
                existing_bet = session.query(BetModel).filter_by(uuid=bet_uuid).first()
                if existing_bet:
                    bet_uuid = str(uuid_lib.uuid4())
                    attempt += 1
                else:
                    break

            if attempt >= max_attempts:
                return jsonify({"error": "Failed to generate unique bet ID"}), 500

            # Get fixture_id from the first bet detail's match
            first_match_id = bet_details[0]['match_id']
            first_match = session.query(MatchModel).filter_by(id=first_match_id).first()
            fixture_id = first_match.fixture_id

            # Create the bet record
            bet_datetime = datetime.utcnow()

            # Get barcode configuration if available
            barcode_standard = 'none'
            barcode_data = None

            if api_bp.db_manager:
                barcode_standard = api_bp.db_manager.get_config_value('barcode.standard', 'none')
                if barcode_standard and barcode_standard != 'none':
                    # Format bet UUID for barcode, ensuring uniqueness within fixture
                    from ..utils.barcode_utils import format_bet_id_for_barcode
                    from ..database.models import BetModel

                    # Try to generate unique barcode for this fixture
                    max_attempts = 100  # Prevent infinite loop
                    counter = 0

                    while counter < max_attempts:
                        barcode_data = format_bet_id_for_barcode(bet_uuid, barcode_standard, counter)

                        # Check if this barcode already exists for the same fixture
                        existing_bet = session.query(BetModel).filter_by(
                            barcode_data=barcode_data,
                            barcode_standard=barcode_standard,
                            fixture_id=fixture_id
                        ).first()

                        if not existing_bet:
                            # Unique barcode found
                            break

                        counter += 1
                        logger.debug(f"Barcode collision detected for fixture {fixture_id}, trying counter {counter}")

                    if counter >= max_attempts:
                        logger.warning(f"Could not generate unique barcode for bet {bet_uuid} in fixture {fixture_id} after {max_attempts} attempts")
                        barcode_data = None  # Fall back to no barcode

            new_bet = BetModel(
                uuid=bet_uuid,
                bet_datetime=bet_datetime,
                fixture_id=fixture_id,
                barcode_standard=barcode_standard,
                barcode_data=barcode_data
            )
            session.add(new_bet)

            # Create bet details
            for detail_data in bet_details:
                bet_detail = BetDetailModel(
                    bet_id=bet_uuid,
                    match_id=detail_data['match_id'],
                    match_number=detail_data['_match_number'],
                    outcome=detail_data['outcome'],
                    amount=float(detail_data['amount']),
                    result='pending'
                )
                session.add(bet_detail)

            # Commit the transaction with proper error handling
            try:
                session.commit()
                logger.info(f"Bet {bet_uuid} committed successfully on thread {current_thread.name}")
            except Exception as commit_error:
                logger.error(f"Commit failed on thread {current_thread.name}: {commit_error}")
                session.rollback()
                raise commit_error

            logger.info(f"Created bet {bet_uuid} with {len(bet_details)} details on thread {current_thread.name}")

            return jsonify({
                "success": True,
                "message": "Bet created successfully",
                "bet_id": bet_uuid,
                "details_count": len(bet_details),
                "thread_info": {
                    "thread_name": current_thread.name,
                    "thread_id": current_thread.ident,
                    "is_main_thread": current_thread is threading.main_thread()
                }
            })

        except Exception as e:
            logger.error(f"Failed to create bet on thread {current_thread.name}: {e}")
            session.rollback()
            return jsonify({"error": str(e)}), 500
        finally:
            # Ensure session is properly closed in this thread context
            try:
                if session:
                    session.close()
                    logger.debug(f"Session closed properly on thread {current_thread.name}")
            except Exception as close_error:
                logger.error(f"Error closing session on thread {current_thread.name}: {close_error}")

    except Exception as e:
        logger.error(f"API create cashier bet error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/cashier/bets/<uuid:bet_id>')
@get_api_auth_decorator()
def get_cashier_bet_details(bet_id):
    """Get detailed information for a specific bet (cashier)"""
    try:
        from ..database.models import BetModel, BetDetailModel, MatchModel

        bet_uuid = str(bet_id)
        session = api_bp.db_manager.get_session()
        try:
            # Get the bet
            bet = session.query(BetModel).filter_by(uuid=bet_uuid).first()
            if not bet:
                return jsonify({"error": "Bet not found"}), 404

            bet_data = bet.to_dict()
            bet_data['paid'] = bet.paid  # Include paid status

            # Get bet details with match information
            bet_details = session.query(BetDetailModel).filter_by(bet_id=bet_uuid).all()
            details_data = []

            for detail in bet_details:
                detail_data = detail.to_dict()

                # Include stored match_number
                detail_data['match_number'] = detail.match_number

                # Get match information
                match = session.query(MatchModel).filter_by(id=detail.match_id).first()
                if match:
                    detail_data['match'] = {
                        'match_number': match.match_number,
                        'fighter1_township': match.fighter1_township,
                        'fighter2_township': match.fighter2_township,
                        'venue_kampala_township': match.venue_kampala_township,
                        'status': match.status,
                        'result': match.result
                    }
                else:
                    detail_data['match'] = None

                details_data.append(detail_data)

            bet_data['details'] = details_data
            bet_data['details_count'] = len(details_data)

            # Calculate total amount
            total_amount = sum(float(detail.amount) for detail in bet_details)
            bet_data['total_amount'] = total_amount

            # Calculate overall bet status and results
            results = {
                'pending': 0,
                'won': 0,
                'lost': 0,
                'cancelled': 0,
                'winnings': 0.0
            }

            overall_status = 'pending'
            for detail in bet_details:
                if detail.result == 'pending':
                    results['pending'] += 1
                elif is_settled_bet_detail_winning(detail, session.query(MatchModel).filter_by(id=detail.match_id).first(), session):
                    results['won'] += 1
                    results['winnings'] += float(detail.win_amount or 0.0)
                elif detail.result == 'lost':
                    results['lost'] += 1
                elif detail.result == 'cancelled':
                    results['cancelled'] += 1

            # Determine overall status
            if results['pending'] == 0:
                if results['won'] > 0 and results['lost'] == 0:
                    overall_status = 'won'
                elif results['lost'] > 0:
                    overall_status = 'lost'
                elif results['cancelled'] > 0:
                    overall_status = 'cancelled'

            bet_data['overall_status'] = overall_status
            bet_data['results'] = results

            return jsonify({
                "success": True,
                "bet": bet_data
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API get cashier bet details error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/cashier/bets/<uuid:bet_id>', methods=['DELETE'])
@get_api_auth_decorator()
def cancel_cashier_bet(bet_id):
    """Cancel a bet and all its details (cashier)"""
    try:
        from ..database.models import BetModel, BetDetailModel, MatchModel

        bet_uuid = str(bet_id)
        session = api_bp.db_manager.get_session()
        try:
            # Get the bet
            bet = session.query(BetModel).filter_by(uuid=bet_uuid).first()
            if not bet:
                return jsonify({"error": "Bet not found"}), 404

            # Get bet details
            bet_details = session.query(BetDetailModel).filter_by(bet_id=bet_uuid).all()

            # Check if any match has already started
            match_ids = [detail.match_id for detail in bet_details]
            matches = session.query(MatchModel).filter(MatchModel.id.in_(match_ids)).all()
            if any(match.status in ['ingame', 'done'] for match in matches):
                return jsonify({"error": "Cannot cancel bet because one or more matches have already started"}), 400

            # Check if bet can be cancelled (only pending bets)
            if any(detail.result != 'pending' for detail in bet_details):
                return jsonify({"error": "Cannot cancel bet with non-pending results"}), 400

            # Update all bet details to cancelled
            for detail in bet_details:
                detail.result = 'cancelled'

            session.commit()
            logger.info(f"Cancelled bet {bet_uuid}")

            return jsonify({
                "success": True,
                "message": "Bet cancelled successfully",
                "bet_id": bet_uuid
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API cancel cashier bet error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/cashier/available-matches')
@get_api_auth_decorator()
def get_available_matches_for_betting():
    """Get matches that are available for betting (status = 'bet') with actual match outcomes"""
    try:
        from ..database.models import MatchModel, MatchOutcomeModel
        from datetime import datetime, date, timedelta

        session = api_bp.db_manager.get_session()
        try:
            # Get today's and yesterday's dates
            today = date.today()
            yesterday = today - timedelta(days=1)

            # Get matches in 'bet' status
            matches_query = session.query(MatchModel).filter(
                MatchModel.status == 'bet'
            ).order_by(MatchModel.match_number.asc())

            # Get venue timezone for date filtering
            venue_tz = get_venue_timezone(api_bp.db_manager)

            # Get today's matches
            local_start_today = datetime.combine(today, datetime.min.time())
            local_end_today = datetime.combine(today, datetime.max.time())
            start_datetime_today = venue_to_utc_datetime(local_start_today, api_bp.db_manager)
            end_datetime_today = venue_to_utc_datetime(local_end_today, api_bp.db_manager)

            today_matches = matches_query.filter(
                MatchModel.start_time >= start_datetime_today,
                MatchModel.start_time < end_datetime_today
            ).all()

            # Get yesterday's matches
            local_start_yesterday = datetime.combine(yesterday, datetime.min.time())
            local_end_yesterday = datetime.combine(yesterday, datetime.max.time())
            start_datetime_yesterday = venue_to_utc_datetime(local_start_yesterday, api_bp.db_manager)
            end_datetime_yesterday = venue_to_utc_datetime(local_end_yesterday, api_bp.db_manager)

            yesterday_matches = matches_query.filter(
                MatchModel.start_time >= start_datetime_yesterday,
                MatchModel.start_time < end_datetime_yesterday
            ).all()

            # Combine today's and yesterday's matches (yesterday first, then today)
            matches = yesterday_matches + today_matches

            # Remove duplicates (in case some matches span the date boundary)
            seen_ids = set()
            unique_matches = []
            for match in matches:
                if match.id not in seen_ids:
                    seen_ids.add(match.id)
                    unique_matches.append(match)

            matches = unique_matches

            # If no matches from today or yesterday, fallback to all matches in bet status
            if not matches:
                matches = matches_query.all()

            matches_data = []
            for match in matches:
                # Get actual match outcomes from the database
                match_outcomes = session.query(MatchOutcomeModel).filter_by(match_id=match.id).all()

                # Convert outcomes to betting options format - show ALL fixture outcomes
                betting_outcomes = []
                existing_outcome_names = set()

                for outcome in match_outcomes:
                    betting_outcomes.append({
                        'outcome_id': outcome.id,
                        'outcome_name': outcome.column_name,
                        'outcome_value': outcome.float_value,
                        'display_name': outcome.column_name  # Use actual outcome name from database
                    })
                    existing_outcome_names.add(outcome.column_name)

                # Always add UNDER and OVER outcomes if not already present
                if 'UNDER' not in existing_outcome_names:
                    betting_outcomes.append({
                        'outcome_id': None,  # No database ID for UNDER/OVER
                        'outcome_name': 'UNDER',
                        'outcome_value': None,
                        'display_name': 'UNDER'
                    })

                if 'OVER' not in existing_outcome_names:
                    betting_outcomes.append({
                        'outcome_id': None,  # No database ID for UNDER/OVER
                        'outcome_name': 'OVER',
                        'outcome_value': None,
                        'display_name': 'OVER'
                    })

                # If no outcomes found, fallback to standard betting options as safety measure
                if not betting_outcomes:
                    logger.warning(f"No outcomes found for match {match.id}, using standard options")
                    betting_outcomes = [
                        {'outcome_id': None, 'outcome_name': 'WIN1', 'outcome_value': None, 'display_name': 'WIN1'},
                        {'outcome_id': None, 'outcome_name': 'X', 'outcome_value': None, 'display_name': 'X'},
                        {'outcome_id': None, 'outcome_name': 'WIN2', 'outcome_value': None, 'display_name': 'WIN2'}
                    ]

                # Build match data manually to avoid lazy loading issues
                match_data = {
                    'id': match.id,
                    'match_number': match.match_number,
                    'fighter1_township': match.fighter1_township,
                    'fighter2_township': match.fighter2_township,
                    'venue_kampala_township': match.venue_kampala_township,
                    'start_time': match.start_time.isoformat() if match.start_time else None,
                    'end_time': match.end_time.isoformat() if match.end_time else None,
                    'result': match.result,
                    'winning_outcomes': match.winning_outcomes,
                    'under_over_result': match.under_over_result,
                    'done': match.done,
                    'running': match.running,
                    'status': match.status,
                    'fixture_active_time': match.fixture_active_time,
                    'filename': match.filename,
                    'file_sha1sum': match.file_sha1sum,
                    'fixture_id': match.fixture_id,
                    'active_status': match.active_status,
                    'zip_filename': match.zip_filename,
                    'zip_sha1sum': match.zip_sha1sum,
                    'zip_upload_status': match.zip_upload_status,
                    'zip_upload_progress': match.zip_upload_progress,
                    'zip_validation_status': match.zip_validation_status,
                    'created_by': match.created_by,
                    'created_at': match.created_at.isoformat() if match.created_at else None,
                    'updated_at': match.updated_at.isoformat() if match.updated_at else None,
                    'outcomes': betting_outcomes,
                    'outcomes_count': len(betting_outcomes)
                }
                matches_data.append(match_data)

            return jsonify({
                "success": True,
                "matches": matches_data,
                "total": len(matches_data),
                "date": today.isoformat()
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API get available matches error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/cashier/bet-details/<int:detail_id>', methods=['DELETE'])
@get_api_auth_decorator()
def delete_bet_detail(detail_id):
    """Delete a specific bet detail (cashier)"""
    try:
        from ..database.models import BetDetailModel

        session = api_bp.db_manager.get_session()
        try:
            # Get the bet detail
            bet_detail = session.query(BetDetailModel).filter_by(id=detail_id).first()
            if not bet_detail:
                return jsonify({"error": "Bet detail not found"}), 404

            # Check if detail can be deleted (only pending)
            if bet_detail.result != 'pending':
                return jsonify({"error": "Cannot delete non-pending bet detail"}), 400

            bet_id = bet_detail.bet_id
            session.delete(bet_detail)
            session.commit()

            logger.info(f"Deleted bet detail {detail_id} from bet {bet_id}")

            return jsonify({
                "success": True,
                "message": "Bet detail deleted successfully",
                "detail_id": detail_id,
                "bet_id": bet_id
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API delete bet detail error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/bets/<uuid:bet_id>', methods=['DELETE'])
@get_api_auth_decorator()
@get_api_auth_decorator(require_admin=True)
def delete_admin_bet(bet_id):
    """Delete a complete bet and all its details (admin only)"""
    try:
        from ..database.models import BetModel, BetDetailModel

        bet_uuid = str(bet_id)
        session = api_bp.db_manager.get_session()
        try:
            # Get the bet
            bet = session.query(BetModel).filter_by(uuid=bet_uuid).first()
            if not bet:
                return jsonify({"error": "Bet not found"}), 404

            # Delete all bet details first (due to foreign key constraints)
            bet_details = session.query(BetDetailModel).filter_by(bet_id=bet_uuid).all()
            deleted_details_count = len(bet_details)

            for detail in bet_details:
                session.delete(detail)

            # Delete the bet record
            session.delete(bet)
            session.commit()

            logger.info(f"Admin deleted bet {bet_uuid} and {deleted_details_count} details")

            return jsonify({
                "success": True,
                "message": f"Bet and {deleted_details_count} details deleted successfully",
                "bet_id": bet_uuid,
                "details_deleted": deleted_details_count
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API delete admin bet error: {e}")
        return jsonify({"error": str(e)}), 500


# Bet Verification Routes
@main_bp.route('/verify-bet')
@login_required
def verify_bet_page():
    """Bet verification page with QR code for mobile access"""
    try:
        # Generate a unique verification session UUID
        import uuid as uuid_lib
        verification_session = str(uuid_lib.uuid4())
        
        # Store session in a simple way for demo (in production, use proper session management)
        # For now, we'll just generate the QR code with the verification URL
        from urllib.parse import urljoin
        base_url = request.host_url
        mobile_verify_url = urljoin(base_url, f'/verify-bet-mobile/{verification_session}')
        
        return render_template('dashboard/verify_bet.html',
                             user=current_user,
                             verification_session=verification_session,
                             mobile_verify_url=mobile_verify_url,
                             page_title="Bet Verification")
    except Exception as e:
        logger.error(f"Verify bet page error: {e}")
        flash("Error loading bet verification page", "error")
        return render_template('errors/500.html'), 500


@main_bp.route('/cashier/verify-bet')
@login_required
def cashier_verify_bet_page():
    """Cashier bet verification page with QR code for mobile access"""
    try:
        # Verify user is cashier
        if not (hasattr(current_user, 'role') and current_user.role == 'cashier'):
            if not (hasattr(current_user, 'is_cashier_user') and current_user.is_cashier_user()):
                flash("Cashier access required", "error")
                return redirect(url_for('main.index'))
        
        # Generate a unique verification session UUID
        import uuid as uuid_lib
        verification_session = str(uuid_lib.uuid4())
        
        # Store session in a simple way for demo (in production, use proper session management)
        from urllib.parse import urljoin
        base_url = request.host_url
        mobile_verify_url = urljoin(base_url, f'/verify-bet-mobile/{verification_session}')
        
        return render_template('dashboard/cashier_verify_bet.html',
                             user=current_user,
                             verification_session=verification_session,
                             mobile_verify_url=mobile_verify_url,
                             page_title="Bet Verification")
    except Exception as e:
        logger.error(f"Cashier verify bet page error: {e}")
        flash("Error loading bet verification page", "error")
        return render_template('errors/500.html'), 500


@main_bp.route('/verify-bet-mobile/<verification_session>')
def verify_bet_mobile(verification_session):
    """Mobile bet verification page - no authentication required"""
    try:
        return render_template('dashboard/verify_bet_mobile.html',
                             verification_session=verification_session,
                             page_title="Bet Verification - Mobile")
    except Exception as e:
        logger.error(f"Mobile verify bet page error: {e}")
        return render_template('errors/500.html'), 500


@api_bp.route('/verify-bet/<uuid:bet_id>')
@get_api_auth_decorator()
def verify_bet_details(bet_id):
    """Get bet details for verification - no authentication required"""
    try:
        from ..database.models import BetModel, BetDetailModel, MatchModel

        bet_uuid = str(bet_id)
        session = api_bp.db_manager.get_session()
        try:
            # Get the bet
            bet = session.query(BetModel).filter_by(uuid=bet_uuid).first()
            if not bet:
                return jsonify({"error": "Bet not found"}), 404

            bet_data = bet.to_dict()
            bet_data['paid'] = bet.paid  # Include paid status

            # Get bet details with match information
            bet_details = session.query(BetDetailModel).filter_by(bet_id=bet_uuid).all()
            details_data = []

            for detail in bet_details:
                detail_data = detail.to_dict()

                # Include stored match_number
                detail_data['match_number'] = detail.match_number

                # Get match information
                match = session.query(MatchModel).filter_by(id=detail.match_id).first()
                if match:
                    detail_data['match'] = {
                        'match_number': match.match_number,
                        'fighter1_township': match.fighter1_township,
                        'fighter2_township': match.fighter2_township,
                        'venue_kampala_township': match.venue_kampala_township,
                        'status': match.status,
                        'result': match.result
                    }
                else:
                    detail_data['match'] = None

                details_data.append(detail_data)

            bet_data['details'] = details_data
            bet_data['details_count'] = len(details_data)

            # Calculate total amount
            total_amount = sum(float(detail.amount) for detail in bet_details)
            bet_data['total_amount'] = total_amount

            return jsonify({
                "success": True,
                "bet": bet_data
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API verify bet details error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/verify-barcode')
@get_api_auth_decorator()
def verify_barcode():
    """Get bet details for verification by barcode data - no authentication required"""
    try:
        from ..database.models import BetModel, BetDetailModel, MatchModel

        # Get barcode data from query parameters
        barcode_data = request.args.get('data', '').strip()
        barcode_standard = request.args.get('standard', '').strip()

        if not barcode_data:
            return jsonify({"error": "Barcode data is required"}), 400

        session = api_bp.db_manager.get_session()
        try:
            # Look up bet by barcode data and standard
            query = session.query(BetModel).filter_by(barcode_data=barcode_data)

            # If standard is provided, also filter by it
            if barcode_standard:
                query = query.filter_by(barcode_standard=barcode_standard)

            # Order by creation date descending to get the latest bet in case of duplicates
            bet = query.order_by(BetModel.created_at.desc()).first()
            if not bet:
                return jsonify({"error": "Bet not found for this barcode"}), 404

            bet_data = bet.to_dict()
            bet_data['paid'] = bet.paid  # Include paid status

            # Get bet details with match information
            bet_details = session.query(BetDetailModel).filter_by(bet_id=bet.uuid).all()
            details_data = []
            total_amount = 0.0

            for detail in bet_details:
                detail_data = detail.to_dict()
                total_amount += float(detail.amount)

                # Include stored match_number
                detail_data['match_number'] = detail.match_number

                # Get match information
                match = session.query(MatchModel).filter_by(id=detail.match_id).first()
                if match:
                    detail_data['match'] = {
                        'match_number': match.match_number,
                        'fighter1_township': match.fighter1_township,
                        'fighter2_township': match.fighter2_township,
                        'venue_kampala_township': match.venue_kampala_township,
                        'status': match.status,
                        'result': match.result
                    }
                else:
                    detail_data['match'] = None

                details_data.append(detail_data)

            bet_data['details'] = details_data
            bet_data['details_count'] = len(details_data)
            bet_data['total_amount'] = total_amount

            # Calculate overall bet status and results
            results = {
                'pending': 0,
                'won': 0,
                'lost': 0,
                'cancelled': 0,
                'winnings': 0.0
            }

            overall_status = 'pending'
            for detail in bet_details:
                if detail.result == 'pending':
                    results['pending'] += 1
                elif is_settled_bet_detail_winning(detail, session.query(MatchModel).filter_by(id=detail.match_id).first(), session):
                    results['won'] += 1
                    results['winnings'] += float(detail.win_amount or 0.0)
                elif detail.result == 'lost':
                    results['lost'] += 1
                elif detail.result == 'cancelled':
                    results['cancelled'] += 1

            # Determine overall status
            if results['pending'] == 0:
                if results['won'] > 0 and results['lost'] == 0:
                    overall_status = 'won'
                elif results['lost'] > 0:
                    overall_status = 'lost'
                elif results['cancelled'] > 0:
                    overall_status = 'cancelled'

            bet_data['overall_status'] = overall_status
            bet_data['results'] = results

            return jsonify({
                "success": True,
                "bet": bet_data,
                "verified_at": datetime.now().isoformat()
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API verify bet details error: {e}")
        return jsonify({"error": str(e)}), 500


# Mark Bet as Paid API endpoints
@api_bp.route('/cashier/bets/<uuid:bet_id>/mark-paid', methods=['POST'])
@get_api_auth_decorator()
def mark_cashier_bet_paid(bet_id):
    """Mark bet as paid (cashier)"""
    try:
        from ..database.models import BetModel
        
        bet_uuid = str(bet_id)
        session = api_bp.db_manager.get_session()
        try:
            # Get the bet
            bet = session.query(BetModel).filter_by(uuid=bet_uuid).first()
            if not bet:
                return jsonify({"error": "Bet not found"}), 404

            # Check if already paid
            if bet.paid:
                return jsonify({"error": "Bet is already marked as paid"}), 400

            # Mark as paid
            bet.paid = True
            bet.updated_at = datetime.now()
            session.commit()
            
            logger.info(f"Bet {bet_uuid} marked as paid by cashier")

            return jsonify({
                "success": True,
                "message": "Bet marked as paid successfully",
                "bet_id": bet_uuid,
                "paid_at": bet.updated_at.isoformat()
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API mark cashier bet paid error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/bets/<uuid:bet_id>/mark-paid', methods=['POST'])
@get_api_auth_decorator()
def mark_admin_bet_paid(bet_id):
    """Mark bet as paid (admin/user)"""
    try:
        from ..database.models import BetModel
        
        bet_uuid = str(bet_id)
        session = api_bp.db_manager.get_session()
        try:
            # Get the bet
            bet = session.query(BetModel).filter_by(uuid=bet_uuid).first()
            if not bet:
                return jsonify({"error": "Bet not found"}), 404

            # Check if already paid
            if bet.paid:
                return jsonify({"error": "Bet is already marked as paid"}), 400

            # Mark as paid
            bet.paid = True
            bet.updated_at = datetime.now()
            session.commit()
            
            logger.info(f"Bet {bet_uuid} marked as paid by admin/user")

            return jsonify({
                "success": True,
                "message": "Bet marked as paid successfully",
                "bet_id": bet_uuid,
                "paid_at": bet.updated_at.isoformat()
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API mark admin bet paid error: {e}")


# Barcode Settings API routes
@api_bp.route('/barcode-settings')
@get_api_auth_decorator()
def get_barcode_settings():
    """Get barcode configuration"""
    try:
        if api_bp.db_manager:
            settings = {
                'enabled': api_bp.db_manager.get_config_value('barcode.enabled', False),
                'standard': api_bp.db_manager.get_config_value('barcode.standard', 'none'),
                'width': api_bp.db_manager.get_config_value('barcode.width', 200),
                'height': api_bp.db_manager.get_config_value('barcode.height', 100),
                'show_on_thermal': api_bp.db_manager.get_config_value('barcode.show_on_thermal', True),
                'show_on_verification': api_bp.db_manager.get_config_value('barcode.show_on_verification', True)
            }
        else:
            # Default settings
            settings = {
                'enabled': False,
                'standard': 'none',
                'width': 200,
                'height': 100,
                'show_on_thermal': True,
                'show_on_verification': True
            }

        return jsonify({
            "success": True,
            "settings": settings
        })
    except Exception as e:
        logger.error(f"API get barcode settings error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/barcode-settings', methods=['POST'])
@get_api_auth_decorator()
@get_api_auth_decorator(require_admin=True)
def set_barcode_settings():
    """Set barcode configuration (admin only)"""
    try:
        data = request.get_json() or {}
        enabled = data.get('enabled', False)
        standard = data.get('standard', 'none')
        width = data.get('width', 200)
        height = data.get('height', 100)
        show_on_thermal = data.get('show_on_thermal', True)
        show_on_verification = data.get('show_on_verification', True)

        # Validation
        valid_standards = ['none', 'code128', 'code39', 'ean13', 'ean8', 'upca', 'upce', 'codabar', 'itf']
        if standard not in valid_standards:
            return jsonify({"error": f"Invalid barcode standard. Must be one of: {', '.join(valid_standards)}"}), 400

        if not isinstance(width, int) or width < 100 or width > 800:
            return jsonify({"error": "Width must be an integer between 100 and 800"}), 400

        if not isinstance(height, int) or height < 50 or height > 300:
            return jsonify({"error": "Height must be an integer between 50 and 300"}), 400

        if enabled and standard == 'none':
            return jsonify({"error": "Barcode standard must be selected when barcode is enabled"}), 400

        if api_bp.db_manager:
            # Save barcode configuration to database
            api_bp.db_manager.set_config_value('barcode.enabled', enabled)
            api_bp.db_manager.set_config_value('barcode.standard', standard)
            api_bp.db_manager.set_config_value('barcode.width', width)
            api_bp.db_manager.set_config_value('barcode.height', height)
            api_bp.db_manager.set_config_value('barcode.show_on_thermal', show_on_thermal)
            api_bp.db_manager.set_config_value('barcode.show_on_verification', show_on_verification)

            logger.info(f"Barcode settings updated - enabled: {enabled}, standard: {standard}, size: {width}x{height}")

            return jsonify({
                "success": True,
                "message": f"Barcode settings updated: {standard.upper() if enabled and standard != 'none' else 'disabled'}",
                "settings": {
                    "enabled": enabled,
                    "standard": standard,
                    "width": width,
                    "height": height,
                    "show_on_thermal": show_on_thermal,
                    "show_on_verification": show_on_verification
                }
            })
        else:
            return jsonify({"error": "Database manager not available"}), 500

    except Exception as e:
        logger.error(f"API set barcode settings error: {e}")
        return jsonify({"error": str(e)}), 500


# QR Code Settings API routes
@api_bp.route('/qrcode-settings')
@get_api_auth_decorator()
def get_qrcode_settings():
    """Get QR code configuration"""
    try:
        if api_bp.db_manager:
            settings = {
                'enabled': api_bp.db_manager.get_config_value('qrcode.enabled', False),
                'size': api_bp.db_manager.get_config_value('qrcode.size', 200),
                'error_correction': api_bp.db_manager.get_config_value('qrcode.error_correction', 'M'),
                'show_on_thermal': api_bp.db_manager.get_config_value('qrcode.show_on_thermal', True),
                'show_on_verification': api_bp.db_manager.get_config_value('qrcode.show_on_verification', True)
            }
        else:
            # Default settings
            settings = {
                'enabled': False,
                'size': 200,
                'error_correction': 'M',
                'show_on_thermal': True,
                'show_on_verification': True
            }

        return jsonify({
            "success": True,
            "settings": settings
        })
    except Exception as e:
        logger.error(f"API get QR code settings error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/qrcode-settings', methods=['POST'])
@get_api_auth_decorator()
@get_api_auth_decorator(require_admin=True)
def set_qrcode_settings():
    """Set QR code configuration (admin only)"""
    try:
        data = request.get_json() or {}
        enabled = data.get('enabled', False)
        size = data.get('size', 200)
        error_correction = data.get('error_correction', 'M')
        show_on_thermal = data.get('show_on_thermal', True)
        show_on_verification = data.get('show_on_verification', True)

        # Validation
        valid_error_levels = ['L', 'M', 'Q', 'H']
        if error_correction not in valid_error_levels:
            return jsonify({"error": f"Invalid error correction level. Must be one of: {', '.join(valid_error_levels)}"}), 400

        if not isinstance(size, int) or size < 100 or size > 500:
            return jsonify({"error": "Size must be an integer between 100 and 500"}), 400

        if api_bp.db_manager:
            # Save QR code configuration to database
            api_bp.db_manager.set_config_value('qrcode.enabled', enabled)
            api_bp.db_manager.set_config_value('qrcode.size', size)
            api_bp.db_manager.set_config_value('qrcode.error_correction', error_correction)
            api_bp.db_manager.set_config_value('qrcode.show_on_thermal', show_on_thermal)
            api_bp.db_manager.set_config_value('qrcode.show_on_verification', show_on_verification)

            logger.info(f"QR code settings updated - enabled: {enabled}, size: {size}, error_correction: {error_correction}")

            return jsonify({
                "success": True,
                "message": f"QR code settings updated: {'enabled' if enabled else 'disabled'}",
                "settings": {
                    "enabled": enabled,
                    "size": size,
                    "error_correction": error_correction,
                    "show_on_thermal": show_on_thermal,
                    "show_on_verification": show_on_verification
                }
            })
        else:
            return jsonify({"error": "Database manager not available"}), 500

    except Exception as e:
        logger.error(f"API set QR code settings error: {e}")
        return jsonify({"error": str(e)}), 500


# Reports API endpoints
@api_bp.route('/reports/daily-summary')
@get_api_auth_decorator()
def get_daily_reports_summary():
    """Get daily reports summary for a specific date"""
    try:
        from ..database.models import BetModel, BetDetailModel, MatchModel, ExtractionStatsModel
        from datetime import datetime, date, timezone, timedelta

        # Get date parameter (default to today)
        date_param = request.args.get('date')
        if date_param:
            try:
                target_date = datetime.strptime(date_param, '%Y-%m-%d').date()
            except ValueError:
                return jsonify({"error": "Invalid date format. Use YYYY-MM-DD"}), 400
        else:
            target_date = date.today()
        # Get time filter parameters
        start_time_param = request.args.get('start_time', '00:00')
        end_time_param = request.args.get('end_time', '23:59')
 
        # Parse time parameters
        try:
            start_hour, start_minute = map(int, start_time_param.split(':'))
            end_hour, end_minute = map(int, end_time_param.split(':'))
        except (ValueError, AttributeError):
            return jsonify({"error": "Invalid time format. Use HH:MM"}), 400

        session = api_bp.db_manager.get_session()
        try:
            # Create the date range using venue timezone
            venue_tz = get_venue_timezone(api_bp.db_manager)
            local_start = datetime.combine(target_date, datetime.min.time()).replace(
                hour=start_hour, minute=start_minute, second=0, microsecond=0
            )
            local_end = datetime.combine(target_date, datetime.max.time()).replace(
                hour=end_hour, minute=end_minute, second=59, microsecond=999999
            )

            # Convert venue local time to UTC for database queries
            start_datetime = venue_to_utc_datetime(local_start, api_bp.db_manager)
            end_datetime = venue_to_utc_datetime(local_end, api_bp.db_manager)

            logger.info(f"Querying daily summary for local date {date_param} and time {start_time_param}-{end_time_param}: UTC range {start_datetime} to {end_datetime}")

            # Get all bets for the target date
            bets_query = session.query(BetModel).filter(
                BetModel.bet_datetime >= start_datetime,
                BetModel.bet_datetime <= end_datetime
            )

            bets = bets_query.all()
            logger.info(f"Found {len(bets)} bets for daily summary")

            # Calculate totals (excluding cancelled bets)
            total_payin = 0.0
            total_bets = 0

            for bet in bets:
                # Get bet details for this bet (excluding cancelled bets)
                bet_details = session.query(BetDetailModel).filter_by(
                    bet_id=bet.uuid
                ).filter(BetDetailModel.result != 'cancelled').all()
                bet_total = sum(float(detail.amount) for detail in bet_details)
                total_payin += bet_total
                total_bets += len(bet_details)

            # Calculate total payout from winning bets for the day
            # Get all bet details for the target date (excluding cancelled bets)
            bet_details_query = session.query(BetDetailModel).join(
                BetModel, BetDetailModel.bet_id == BetModel.uuid
            ).filter(
                BetModel.bet_datetime >= start_datetime,
                BetModel.bet_datetime <= end_datetime,
                BetDetailModel.result != 'cancelled'
            )
            
            bet_details = bet_details_query.all()
            
            # Sum win_amount for all winning bet details
            total_payout = 0.0
            for detail in bet_details:
                match = session.query(MatchModel).filter_by(id=detail.match_id).first()
                if is_settled_bet_detail_winning(detail, match, session):
                    total_payout += float(detail.win_amount or 0)
            
            # Get unique matches count
            unique_match_ids = set(detail.match_id for detail in bet_details)
            total_matches = len(unique_match_ids)

            summary = {
                'date': target_date.isoformat(),
                'total_payin': float(total_payin),
                'total_payout': float(total_payout),
                'net_profit': float(total_payin - total_payout),
                'total_bets': total_bets,
                'total_matches': total_matches
            }

            return jsonify({
                "success": True,
                "summary": summary
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API get daily reports summary error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/reports/match-reports')
@get_api_auth_decorator()
def get_match_reports():
    """Get match reports for a specific date"""
    try:
        from ..database.models import BetModel, BetDetailModel, MatchModel, ExtractionStatsModel
        from datetime import datetime, date, timezone, timedelta

        # Get date parameter (default to today)
        date_param = request.args.get('date')
        if date_param:
            try:
                target_date = datetime.strptime(date_param, '%Y-%m-%d').date()
            except ValueError:
                return jsonify({"error": "Invalid date format. Use YYYY-MM-DD"}), 400
        else:
            target_date = date.today()
        
        # Get time filter parameters
        start_time_param = request.args.get('start_time', '00:00')
        end_time_param = request.args.get('end_time', '23:59')
  
        # Parse time parameters
        try:
            start_hour, start_minute = map(int, start_time_param.split(':'))
            end_hour, end_minute = map(int, end_time_param.split(':'))
        except (ValueError, AttributeError):
            return jsonify({"error": "Invalid time format. Use HH:MM"}), 400

        session = api_bp.db_manager.get_session()
        try:
            # Create the date range using venue timezone
            venue_tz = get_venue_timezone(api_bp.db_manager)
            local_start = datetime.combine(target_date, datetime.min.time())
            local_end = datetime.combine(target_date, datetime.max.time())

            # Convert venue local time to UTC for database queries
            start_datetime = venue_to_utc_datetime(local_start, api_bp.db_manager)
            end_datetime = venue_to_utc_datetime(local_end, api_bp.db_manager)

            logger.info(f"Querying match reports for local date {date_param} and time {start_time_param}-{end_time_param}: UTC range {start_datetime} to {end_datetime}")

            # Get all matches that had bets on this day (excluding cancelled bets)
            bet_details_query = session.query(BetDetailModel).join(
                BetModel, BetDetailModel.bet_id == BetModel.uuid
            ).filter(
                BetModel.bet_datetime >= start_datetime,
                BetModel.bet_datetime <= end_datetime,
                BetDetailModel.result != 'cancelled'
            )

            # Group by match_id and calculate statistics
            match_stats = {}
            bet_details = bet_details_query.all()

            for detail in bet_details:
                match_id = detail.match_id
                if match_id not in match_stats:
                    match_stats[match_id] = {
                        'match_id': match_id,
                        'bets_count': 0,
                        'payin': 0.0,
                        'payout': 0.0
                    }

                match = session.query(MatchModel).filter_by(id=match_id).first()

                match_stats[match_id]['bets_count'] += 1
                match_stats[match_id]['payin'] += float(detail.amount)

                # Calculate payout using actual win_amount (stored when bet was settled)
                # This ensures consistency with bet list and avoids recalculation issues
                if is_settled_bet_detail_winning(detail, match, session):
                    # Use the actual win_amount stored when the bet was settled
                    match_stats[match_id]['payout'] += float(detail.win_amount or 0)

            # Get match information and merge with stats
            matches_data = []
            for match_id, stats in match_stats.items():
                match = session.query(MatchModel).filter_by(id=match_id).first()
                if match:
                    match_data = {
                        'match_id': match_id,
                        'match_number': match.match_number,
                        'fighter1_township': match.fighter1_township,
                        'fighter2_township': match.fighter2_township,
                        'venue_kampala_township': match.venue_kampala_township,
                        'status': match.status,
                        'bets_count': stats['bets_count'],
                        'payin': stats['payin'],
                        'payout': stats['payout'],
                        'net': stats['payin'] - stats['payout']
                    }
                    matches_data.append(match_data)

            # Sort by match number
            matches_data.sort(key=lambda x: x.get('match_number', 0))

            return jsonify({
                "success": True,
                "matches": matches_data,
                "total": len(matches_data),
                "date": target_date.isoformat()
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API get match reports error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/reports/match-details')
@get_api_auth_decorator()
def get_match_details():
    """Get detailed bets for a specific match on a specific date"""
    try:
        from ..database.models import BetModel, BetDetailModel, MatchModel
        from datetime import datetime, date, timezone, timedelta

        # Get parameters
        match_id = request.args.get('match_id')
        date_param = request.args.get('date')

        if not match_id:
            return jsonify({"error": "match_id parameter is required"}), 400

        if date_param:
            try:
                target_date = datetime.strptime(date_param, '%Y-%m-%d').date()
            except ValueError:
                return jsonify({"error": "Invalid date format. Use YYYY-MM-DD"}), 400
        else:
            target_date = date.today()

        session = api_bp.db_manager.get_session()
        try:
            # Create the date range using venue timezone
            venue_tz = get_venue_timezone(api_bp.db_manager)
            local_start = datetime.combine(target_date, datetime.min.time())
            local_end = datetime.combine(target_date, datetime.max.time())

            # Convert venue local time to UTC for database queries
            start_datetime = venue_to_utc_datetime(local_start, api_bp.db_manager)
            end_datetime = venue_to_utc_datetime(local_end, api_bp.db_manager)

            logger.info(f"Querying match details for match {match_id}, local date {date_param}: UTC range {start_datetime} to {end_datetime}")

            # Get bet details for this match on this date
            bet_details_query = session.query(BetDetailModel).join(
                BetModel, BetDetailModel.bet_id == BetModel.uuid
            ).filter(
                BetDetailModel.match_id == match_id,
                BetModel.bet_datetime >= start_datetime,
                BetModel.bet_datetime <= end_datetime
            ).order_by(BetModel.bet_datetime.desc())

            bet_details = bet_details_query.all()
            logger.info(f"Found {len(bet_details)} bet details for match {match_id}")

            # Get match information
            match = session.query(MatchModel).filter_by(id=match_id).first()

            # Convert to response format
            bets_data = []
            for detail in bet_details:
                # Get the bet for datetime
                bet = session.query(BetModel).filter_by(uuid=detail.bet_id).first()

                # Calculate potential winning
                potential_winning = 0.0
                if match:
                    outcomes_dict = match.get_outcomes_dict()
                    odds = outcomes_dict.get(detail.outcome, 0.0)
                    potential_winning = float(detail.amount) * float(odds)
                
                # Determine actual result status using is_bet_detail_winning function
                # This checks if bet detail is winning based on match's winning outcomes
                actual_result = detail.result
                if is_settled_bet_detail_winning(detail, match, session):
                    actual_result = 'won'
                elif detail.result == 'lost':
                    actual_result = 'lost'
                elif detail.result == 'cancelled':
                    actual_result = 'cancelled'
                else:
                    actual_result = 'pending'
                
                bet_data = {
                    'bet_id': detail.bet_id,
                    'bet_datetime': bet.bet_datetime.isoformat() if bet else None,
                    'outcome': detail.outcome,
                    'amount': float(detail.amount),
                    'result': actual_result,
                    'potential_winning': potential_winning
                }
                bets_data.append(bet_data)

            return jsonify({
                "success": True,
                "match": {
                    'match_id': match_id,
                    'match_number': match.match_number if match else None,
                    'fighter1_township': match.fighter1_township if match else None,
                    'fighter2_township': match.fighter2_township if match else None,
                    'venue_kampala_township': match.venue_kampala_township if match else None
                } if match else None,
                "bets": bets_data,
                "total": len(bets_data),
                "date": target_date.isoformat()
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API get match details error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/reports/download-excel')
@get_api_auth_decorator()
def download_excel_report():
    """Generate and download Excel report for a specific date"""
    try:
        from ..database.models import BetModel, BetDetailModel, MatchModel, ExtractionStatsModel
        from datetime import datetime, date, timezone, timedelta
        from flask import send_file
        import io
        from openpyxl import Workbook
        from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
        from openpyxl.utils import get_column_letter

        # Get date parameter
        date_param = request.args.get('date')
        if date_param:
            try:
                target_date = datetime.strptime(date_param, '%Y-%m-%d').date()
            except ValueError:
                return jsonify({"error": "Invalid date format. Use YYYY-MM-DD"}), 400
        else:
            target_date = date.today()
        
        # Get time filter parameters
        start_time_param = request.args.get('start_time', '00:00')
        end_time_param = request.args.get('end_time', '23:59')
  
        # Parse time parameters
        try:
            start_hour, start_minute = map(int, start_time_param.split(':'))
            end_hour, end_minute = map(int, end_time_param.split(':'))
        except (ValueError, AttributeError):
            return jsonify({"error": "Invalid time format. Use HH:MM"}), 400

        session = api_bp.db_manager.get_session()
        try:
            # Create the date range using venue timezone
            venue_tz = get_venue_timezone(api_bp.db_manager)
            local_start = datetime.combine(target_date, datetime.min.time())
            local_end = datetime.combine(target_date, datetime.max.time())

            # Convert venue local time to UTC for database queries
            start_datetime = venue_to_utc_datetime(local_start, api_bp.db_manager)
            end_datetime = venue_to_utc_datetime(local_end, api_bp.db_manager)

            logger.info(f"Generating Excel report for local date {date_param} and time {start_time_param}-{end_time_param}: UTC range {start_datetime} to {end_datetime}")

            # Create workbook
            wb = Workbook()
            ws_summary = wb.active
            ws_summary.title = "Daily Summary"
            ws_matches = wb.create_sheet("Match Reports")

            # Define styles
            header_font = Font(bold=True, color="FFFFFF")
            header_fill = PatternFill(start_color="366092", end_color="366092", fill_type="solid")
            border = Border(left=Side(style='thin'), right=Side(style='thin'), top=Side(style='thin'), bottom=Side(style='thin'))

            # Generate Daily Summary sheet
            ws_summary['A1'] = f"Daily Report - {target_date.strftime('%Y-%m-%d')}"
            ws_summary['A1'].font = Font(bold=True, size=16)
            ws_summary.merge_cells('A1:E1')

            # Get daily summary data (excluding cancelled bets)
            bets_query = session.query(BetModel).filter(
                BetModel.bet_datetime >= start_datetime,
                BetModel.bet_datetime <= end_datetime
            )
            bets = bets_query.all()

            total_payin = 0.0
            total_bets = 0
            for bet in bets:
                bet_details = session.query(BetDetailModel).filter_by(
                    bet_id=bet.uuid
                ).filter(BetDetailModel.result != 'cancelled').all()
                bet_total = sum(float(detail.amount) for detail in bet_details)
                total_payin += bet_total
                total_bets += len(bet_details)

            bet_details_query = session.query(BetDetailModel).join(
                BetModel, BetDetailModel.bet_id == BetModel.uuid
            ).filter(
                BetModel.bet_datetime >= start_datetime,
                BetModel.bet_datetime <= end_datetime,
                BetDetailModel.result != 'cancelled'
            )
            bet_details = bet_details_query.all()

            total_payout = 0.0
            for detail in bet_details:
                match = session.query(MatchModel).filter_by(id=detail.match_id).first()
                if is_settled_bet_detail_winning(detail, match, session):
                    total_payout += float(detail.win_amount or 0)

            unique_match_ids = {detail.match_id for detail in bet_details}

            # Summary headers
            ws_summary['A3'] = "Metric"
            ws_summary['B3'] = "Value"
            ws_summary['A3'].font = header_font
            ws_summary['B3'].font = header_font
            ws_summary['A3'].fill = header_fill
            ws_summary['B3'].fill = header_fill

            # Summary data
            summary_data = [
                ("Total Payin", f"{total_payin:.2f}"),
                ("Total Payout", f"{total_payout:.2f}"),
                ("Net Profit", f"{total_payin - total_payout:.2f}"),
                ("Total Bets", str(total_bets)),
                ("Total Matches", str(len(unique_match_ids)))
            ]

            for i, (metric, value) in enumerate(summary_data, 4):
                ws_summary[f'A{i}'] = metric
                ws_summary[f'B{i}'] = value
                ws_summary[f'A{i}'].border = border
                ws_summary[f'B{i}'].border = border

            # Auto-adjust column widths for summary
            for col in ['A', 'B']:
                ws_summary.column_dimensions[col].width = 20

            # Generate Match Reports sheet
            ws_matches['A1'] = f"Match Reports - {target_date.strftime('%Y-%m-%d')}"
            ws_matches['A1'].font = Font(bold=True, size=16)
            ws_matches.merge_cells('A1:H1')

            # Headers
            headers = ["Match #", "Fighter 1", "Fighter 2", "Venue", "Bets Count", "Payin", "Payout", "Net"]
            for col, header in enumerate(headers, 1):
                cell = ws_matches.cell(row=3, column=col, value=header)
                cell.font = header_font
                cell.fill = header_fill
                cell.border = border
                cell.alignment = Alignment(horizontal='center')

            # Get match data (excluding cancelled bets)
            bet_details_query = session.query(BetDetailModel).join(
                BetModel, BetDetailModel.bet_id == BetModel.uuid
            ).filter(
                BetModel.bet_datetime >= start_datetime,
                BetModel.bet_datetime <= end_datetime,
                BetDetailModel.result != 'cancelled'
            )

            match_stats = {}
            bet_details = bet_details_query.all()

            for detail in bet_details:
                match_id = detail.match_id
                if match_id not in match_stats:
                    match_stats[match_id] = {
                        'match_id': match_id,
                        'bets_count': 0,
                        'payin': 0.0,
                        'payout': 0.0
                    }

                match_stats[match_id]['bets_count'] += 1
                match_stats[match_id]['payin'] += float(detail.amount)

                # Add actual payout for winning bets (use same logic as bet list)
                match = session.query(MatchModel).filter_by(id=match_id).first()
                if is_settled_bet_detail_winning(detail, match, session):
                    match_stats[match_id]['payout'] += float(detail.win_amount or 0)

            # Write match data
            row = 4
            for match_id, stats in match_stats.items():
                match = session.query(MatchModel).filter_by(id=match_id).first()
                if match:
                    ws_matches.cell(row=row, column=1, value=match.match_number).border = border
                    ws_matches.cell(row=row, column=2, value=match.fighter1_township).border = border
                    ws_matches.cell(row=row, column=3, value=match.fighter2_township).border = border
                    ws_matches.cell(row=row, column=4, value=match.venue_kampala_township).border = border
                    ws_matches.cell(row=row, column=5, value=stats['bets_count']).border = border
                    ws_matches.cell(row=row, column=6, value=f"{stats['payin']:.2f}").border = border
                    ws_matches.cell(row=row, column=7, value=f"{stats['payout']:.2f}").border = border
                    ws_matches.cell(row=row, column=8, value=f"{stats['payin'] - stats['payout']:.2f}").border = border
                    row += 1

            # Auto-adjust column widths for matches
            for col in range(1, 9):
                ws_matches.column_dimensions[get_column_letter(col)].width = 15

            # Save workbook to memory buffer
            buffer = io.BytesIO()
            wb.save(buffer)
            buffer.seek(0)

            # Return file for download
            filename = f"report_{target_date.strftime('%Y-%m-%d')}.xlsx"
            return send_file(
                buffer,
                as_attachment=True,
                download_name=filename,
                mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
            )

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API download Excel report error: {e}")
        return jsonify({"error": str(e)}), 500


# Statistics API endpoints
@api_bp.route('/statistics')
@get_api_auth_decorator()
def get_statistics():
    """Get extraction statistics with filtering"""
    try:
        from ..database.models import ExtractionStatsModel
        from datetime import datetime, timedelta

        # Get filter parameters
        date_range = request.args.get('date_range', 'month')
        fixture_id = request.args.get('fixture_id', '').strip()
        result_filter = request.args.get('result', '').strip()

        session = api_bp.db_manager.get_session()
        try:
            # Build query with filters
            query = session.query(ExtractionStatsModel)

            # Date range filter
            now = datetime.utcnow()
            if date_range == 'today':
                start_date = now.replace(hour=0, minute=0, second=0, microsecond=0)
                query = query.filter(ExtractionStatsModel.match_datetime >= start_date)
            elif date_range == 'week':
                start_date = now - timedelta(days=7)
                query = query.filter(ExtractionStatsModel.match_datetime >= start_date)
            elif date_range == 'month':
                start_date = now - timedelta(days=30)
                query = query.filter(ExtractionStatsModel.match_datetime >= start_date)
            # 'all' doesn't need filtering

            # Fixture ID filter
            if fixture_id:
                query = query.filter(ExtractionStatsModel.fixture_id.ilike(f'%{fixture_id}%'))

            # Result filter
            if result_filter:
                query = query.filter(ExtractionStatsModel.actual_result == result_filter)

            # Get statistics ordered by most recent first
            stats = query.order_by(ExtractionStatsModel.match_datetime.desc()).all()

            # Calculate summary
            total_matches = len(stats)
            total_collected = sum(stat.total_amount_collected for stat in stats)
            total_redistributed = sum(stat.total_redistributed for stat in stats)
            net_profit = total_collected - total_redistributed

            summary = {
                'total_matches': total_matches,
                'total_collected': float(total_collected),
                'total_redistributed': float(total_redistributed),
                'net_profit': float(net_profit)
            }

            # Convert stats to dict
            stats_data = []
            for stat in stats:
                stat_dict = {
                    'id': stat.id,
                    'match_id': stat.match_id,
                    'fixture_id': stat.fixture_id,
                    'match_datetime': stat.match_datetime.isoformat(),
                    'total_bets': stat.total_bets,
                    'total_amount_collected': float(stat.total_amount_collected),
                    'total_redistributed': float(stat.total_redistributed),
                    'actual_result': stat.actual_result,
                    'extraction_result': stat.extraction_result,
                    'cap_applied': stat.cap_applied,
                    'cap_percentage': float(stat.cap_percentage) if stat.cap_percentage else None,
                    'under_bets': stat.under_bets,
                    'under_amount': float(stat.under_amount),
                    'over_bets': stat.over_bets,
                    'over_amount': float(stat.over_amount)
                }
                stats_data.append(stat_dict)

            return jsonify({
                "success": True,
                "summary": summary,
                "statistics": stats_data,
                "total": len(stats_data)
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API get statistics error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/statistics/<int:stats_id>')
@get_api_auth_decorator()
def get_statistics_details(stats_id):
    """Get detailed information for a specific statistics record"""
    try:
        from ..database.models import ExtractionStatsModel

        session = api_bp.db_manager.get_session()
        try:
            stat = session.query(ExtractionStatsModel).filter_by(id=stats_id).first()

            if not stat:
                return jsonify({"error": "Statistics record not found"}), 404

            stat_dict = {
                'id': stat.id,
                'match_id': stat.match_id,
                'fixture_id': stat.fixture_id,
                'match_datetime': stat.match_datetime.isoformat(),
                'total_bets': stat.total_bets,
                'total_amount_collected': float(stat.total_amount_collected),
                'total_redistributed': float(stat.total_redistributed),
                'actual_result': stat.actual_result,
                'extraction_result': stat.extraction_result,
                'cap_applied': stat.cap_applied,
                'cap_percentage': float(stat.cap_percentage) if stat.cap_percentage else None,
                'under_bets': stat.under_bets,
                'under_amount': float(stat.under_amount),
                'over_bets': stat.over_bets,
                'over_amount': float(stat.over_amount),
                'result_breakdown': stat.result_breakdown,
                'created_at': stat.created_at.isoformat(),
                'updated_at': stat.updated_at.isoformat()
            }

            return jsonify({
                "success": True,
                "statistics": stat_dict
            })

        finally:
            session.close()

    except Exception as e:
        logger.error(f"API get statistics details error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/barcode/<uuid:bet_id>')
@get_api_auth_decorator()
def generate_bet_barcode(bet_id):
    """Generate barcode image for bet verification - no authentication required"""
    try:
        from ..utils.barcode_utils import generate_barcode_image, format_bet_id_for_barcode
        from flask import Response
        import base64
        import io

        bet_uuid = str(bet_id)

        # Get barcode configuration
        if api_bp.db_manager:
            enabled = api_bp.db_manager.get_config_value('barcode.enabled', False)
            standard = api_bp.db_manager.get_config_value('barcode.standard', 'none')
            width = api_bp.db_manager.get_config_value('barcode.width', 200)
            height = api_bp.db_manager.get_config_value('barcode.height', 100)
        else:
            return jsonify({"error": "Database manager not available"}), 500

        # Check if barcodes are enabled
        if not enabled or standard == 'none':
            return jsonify({"error": "Barcodes are not enabled"}), 404

        # First try to get saved barcode data from bet
        barcode_data = None
        session = api_bp.db_manager.get_session()
        try:
            from ..database.models import BetModel
            bet = session.query(BetModel).filter_by(uuid=bet_uuid).first()
            if bet and bet.barcode_data:
                barcode_data = bet.barcode_data
                logger.debug(f"Using saved barcode data for bet {bet_uuid}: {barcode_data}")
        finally:
            session.close()

        # If no saved barcode data, format bet ID for barcode (fallback)
        if not barcode_data:
            barcode_data = format_bet_id_for_barcode(bet_uuid, standard)
            logger.debug(f"Using generated barcode data for bet {bet_uuid}: {barcode_data}")

        # Generate barcode image
        barcode_image_bytes = generate_barcode_image(barcode_data, standard, width, height)

        if barcode_image_bytes:
            return Response(
                barcode_image_bytes,
                mimetype='image/png',
                headers={'Cache-Control': 'public, max-age=3600'}  # Cache for 1 hour
            )
        else:
            return jsonify({"error": "Failed to generate barcode"}), 500

    except Exception as e:
        logger.error(f"API generate bet barcode error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/barcode-data/<uuid:bet_id>')
@get_api_auth_decorator()
def get_bet_barcode_data(bet_id):
    """Get barcode data and configuration for bet - no authentication required"""
    try:
        from ..utils.barcode_utils import format_bet_id_for_barcode, generate_barcode_image
        import base64
        import io

        bet_uuid = str(bet_id)

        # Get barcode configuration
        if api_bp.db_manager:
            enabled = api_bp.db_manager.get_config_value('barcode.enabled', False)
            standard = api_bp.db_manager.get_config_value('barcode.standard', 'none')
            width = api_bp.db_manager.get_config_value('barcode.width', 200)
            height = api_bp.db_manager.get_config_value('barcode.height', 100)
            show_on_thermal = api_bp.db_manager.get_config_value('barcode.show_on_thermal', True)
            show_on_verification = api_bp.db_manager.get_config_value('barcode.show_on_verification', True)
        else:
            return jsonify({"error": "Database manager not available"}), 500

        # Check if barcodes are enabled
        if not enabled or standard == 'none':
            return jsonify({
                "success": True,
                "enabled": False,
                "barcode_data": None
            })

        # First try to get saved barcode data from bet
        barcode_data = None
        session = api_bp.db_manager.get_session()
        try:
            from ..database.models import BetModel
            bet = session.query(BetModel).filter_by(uuid=bet_uuid).first()
            if bet and bet.barcode_data:
                barcode_data = bet.barcode_data
                logger.debug(f"Using saved barcode data for bet {bet_uuid}: {barcode_data}")
        finally:
            session.close()

        # If no saved barcode data, format bet ID for barcode (fallback)
        if not barcode_data:
            barcode_data = format_bet_id_for_barcode(bet_uuid, standard)
            logger.debug(f"Using generated barcode data for bet {bet_uuid}: {barcode_data}")

        # Generate barcode image and convert to base64
        barcode_image_bytes = generate_barcode_image(barcode_data, standard, width, height)
        barcode_base64 = None

        if barcode_image_bytes:
            # generate_barcode_image() returns PNG bytes directly, so encode to base64
            barcode_base64 = base64.b64encode(barcode_image_bytes).decode('utf-8')

        return jsonify({
            "success": True,
            "enabled": enabled,
            "barcode_data": {
                "data": barcode_data,
                "standard": standard,
                "width": width,
                "height": height,
                "show_on_thermal": show_on_thermal,
                "show_on_verification": show_on_verification,
                "image_base64": barcode_base64,
                "bet_uuid": bet_uuid
            }
        })

    except Exception as e:
        logger.error(f"API get bet barcode data error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/templates/<template_name>')
@get_api_auth_decorator()
def get_template_preview(template_name):
    """Serve template preview with black background - no authentication required"""
    try:
        api = g.get('api')
        if not api:
            return "API not available", 500

        # Get template preview HTML
        preview_html = api.get_template_preview(template_name)

        # Return HTML response
        from flask import Response
        return Response(preview_html, mimetype='text/html')

    except Exception as e:
        logger.error(f"Template preview route error: {e}")
        return f"Error loading template preview: {str(e)}", 500


@api_bp.route('/upload-intro-video', methods=['POST'])
@get_api_auth_decorator()
@get_api_auth_decorator(require_admin=True)
def upload_intro_video():
    """Upload default intro video file (admin only)"""
    try:
        from ..config.settings import get_user_data_dir
        from werkzeug.utils import secure_filename
        import os

        # Check if file was uploaded
        if 'video_file' not in request.files:
            return jsonify({"error": "No video file provided"}), 400

        file = request.files['video_file']
        overwrite = request.form.get('overwrite', 'false').lower() == 'true'

        if file.filename == '':
            return jsonify({"error": "No video file selected"}), 400

        # Validate file type
        allowed_extensions = {'mp4', 'avi', 'mov', 'mkv', 'webm'}
        if not ('.' in file.filename and
                file.filename.rsplit('.', 1)[1].lower() in allowed_extensions):
            return jsonify({"error": "Invalid file type. Allowed: MP4, AVI, MOV, MKV, WebM"}), 400

        # Get persistent storage directory
        user_data_dir = get_user_data_dir()
        videos_dir = user_data_dir / "videos"
        videos_dir.mkdir(parents=True, exist_ok=True)

        # Set filename for intro video
        filename = secure_filename("intro_video." + file.filename.rsplit('.', 1)[1].lower())
        filepath = videos_dir / filename

        # Check if file already exists
        if filepath.exists() and not overwrite:
            return jsonify({"error": "Intro video already exists. Check 'overwrite existing' to replace it."}), 400

        # Save the file
        file.save(str(filepath))

        # Store the path in configuration
        if api_bp.db_manager:
            api_bp.db_manager.set_config_value('intro_video_path', str(filepath))

        logger.info(f"Intro video uploaded successfully: {filepath}")

        return jsonify({
            "success": True,
            "message": "Intro video uploaded successfully",
            "filename": filename,
            "path": str(filepath)
        })

    except Exception as e:
        logger.error(f"API upload intro video error: {e}")
        return jsonify({"error": str(e)}), 500


# Timezone Settings API routes
@api_bp.route('/timezone-settings')
@get_api_auth_decorator()
def get_timezone_settings():
    """Get venue timezone configuration"""
    try:
        if api_bp.db_manager:
            from ..utils.timezone_utils import get_venue_timezone
            venue_tz = get_venue_timezone(api_bp.db_manager)
            settings = {
                'timezone': str(venue_tz)
            }
        else:
            settings = {
                'timezone': 'Africa/Johannesburg'  # Default
            }

        return jsonify({
            "success": True,
            "settings": settings
        })
    except Exception as e:
        logger.error(f"API get timezone settings error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/timezone-settings', methods=['POST'])
@get_api_auth_decorator()
@get_api_auth_decorator(require_admin=True)
def set_timezone_settings():
    """Set venue timezone configuration (admin only)"""
    try:
        data = request.get_json() or {}
        timezone_str = data.get('timezone', '').strip()

        # Validate timezone
        import pytz
        try:
            pytz.timezone(timezone_str)
        except pytz.exceptions.UnknownTimeZoneError:
            return jsonify({"error": f"Invalid timezone: {timezone_str}"}), 400

        if api_bp.db_manager:
            # Save timezone configuration to database
            api_bp.db_manager.set_config_value('venue.timezone', timezone_str)

            logger.info(f"Venue timezone updated to {timezone_str}")

            return jsonify({
                "success": True,
                "message": f"Venue timezone set to {timezone_str}",
                "settings": {
                    "timezone": timezone_str
                }
            })
        else:
            return jsonify({"error": "Database manager not available"}), 500

    except Exception as e:
        logger.error(f"API set timezone settings error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/timezone/current-time', methods=['POST'])
@get_api_auth_decorator()
def get_current_venue_time():
    """Get current time in specified timezone"""
    try:
        data = request.get_json() or {}
        timezone_str = data.get('timezone', '').strip()

        if not timezone_str:
            return jsonify({"error": "Timezone parameter is required"}), 400

        # Validate timezone
        import pytz
        try:
            tz = pytz.timezone(timezone_str)
        except pytz.exceptions.UnknownTimeZoneError:
            return jsonify({"error": f"Invalid timezone: {timezone_str}"}), 400

        # Get current time in the specified timezone
        from datetime import datetime
        now_utc = datetime.now(pytz.UTC)
        now_local = now_utc.astimezone(tz)

        # Format for display
        current_time = now_local.strftime('%Y-%m-%d %H:%M:%S %Z')

        return jsonify({
            "success": True,
            "current_time": current_time,
            "timezone": timezone_str,
            "utc_offset": now_local.strftime('%z')
        })

    except Exception as e:
        logger.error(f"API get current venue time error: {e}")
        return jsonify({"error": str(e)}), 500


# Sign-up API endpoint
@api_bp.route('/signup', methods=['POST'])
def signup():
    """Sign-up endpoint for creating new broker or player accounts"""
    try:
        data = request.get_json() or {}
        
        # Log incoming request for debugging
        logger.info(f"Signup request received: {data}")
        
        username = data.get('username', '').strip()
        email = data.get('email', '').strip()
        password = data.get('password', '')
        role = data.get('role', 'player').strip()  # Default to player if not specified
        
        logger.info(f"Signup data - username: {username}, email: {email}, role: {role}")
        
        # Validate input
        if not username:
            logger.warning("Signup failed: username is empty")
            return jsonify({
                "success": False,
                "error": "Username is required"
            }), 400
        
        if not email:
            logger.warning("Signup failed: email is empty")
            return jsonify({
                "success": False,
                "error": "Email is required"
            }), 400
        
        if not password:
            logger.warning("Signup failed: password is empty")
            return jsonify({
                "success": False,
                "error": "Password is required"
            }), 400
        
        if len(username) < 3:
            logger.warning(f"Signup failed: username too short - {len(username)} characters")
            return jsonify({
                "success": False,
                "error": "Username must be at least 3 characters"
            }), 400
        
        if len(password) < 6:
            logger.warning(f"Signup failed: password too short - {len(password)} characters")
            return jsonify({
                "success": False,
                "error": "Password must be at least 6 characters"
            }), 400
        
        # Validate role
        if role not in ['broker', 'player']:
            logger.warning(f"Signup failed: invalid role - {role}")
            return jsonify({
                "success": False,
                "error": "Invalid role. Must be 'broker' or 'player'"
            }), 400
        
        # Check if website sign-up is enabled
        if api_bp.db_manager:
            website_signup_enabled = api_bp.db_manager.get_config_value('website_signup.enabled', False)
            logger.info(f"Website signup enabled status: {website_signup_enabled}")
            if not website_signup_enabled:
                logger.warning("Signup failed: website sign-up is disabled")
                return jsonify({
                    "success": False,
                    "error": "Sign-up is currently disabled. Please contact an administrator."
                }), 403
        else:
            logger.warning("Signup: db_manager not available, assuming sign-up is disabled")
            return jsonify({
                "success": False,
                "error": "Sign-up is currently disabled. Please contact an administrator."
            }), 403
        
        # Create user using auth manager
        auth_manager = getattr(api_bp, 'auth_manager', None)
        if not auth_manager:
            logger.error("Signup failed: auth_manager not available")
            return jsonify({
                "success": False,
                "error": "Authentication system not available"
            }), 500
        
        logger.info(f"Attempting to create user: {username} with role: {role}")
        
        # Create user with specified role
        user_data = auth_manager.create_user(
            username=username,
            email=email,
            password=password,
            is_admin=False,
            role=role
        )
        
        if user_data:
            logger.info(f"New user signed up successfully: {username} with role: {role}")
            
            # Try to send verification email if email services are enabled
            try:
                # Get email settings
                settings_response = get_email_settings()
                if settings_response[1] == 200:
                    settings_data = settings_response[0].get_json()
                    settings = settings_data.get('settings', {})
                    
                    if settings.get('enabled'):
                        # Send verification email
                        import smtplib
                        from email.mime.text import MIMEText
                        from email.mime.multipart import MIMEMultipart
                        import secrets
                        
                        session = api_bp.db_manager.get_session()
                        try:
                            from ..database.models import UserModel, EmailVerificationModel
                            
                            # Get the newly created user
                            user = session.query(UserModel).filter_by(email=email).first()
                            
                            if user:
                                # Generate verification token
                                token = secrets.token_urlsafe(32)
                                expires_at = datetime.utcnow() + timedelta(hours=24)
                                
                                # Store verification token
                                verification = EmailVerificationModel(
                                    user_id=user.id,
                                    email=email,
                                    token=token,
                                    token_type='email_verification',
                                    expires_at=expires_at
                                )
                                session.add(verification)
                                session.commit()
                                
                                # Create verification link
                                app_domain = settings.get('app_domain', '').rstrip('/')
                                verification_link = f"{app_domain}/verify-email?token={token}"
                                
                                # Create email
                                msg = MIMEMultipart('alternative')
                                msg['Subject'] = 'Verify Your Email Address'
                                msg['From'] = f"{settings.get('smtp_from_name', 'MbetterClient')} <{settings.get('smtp_from_email')}>"
                                msg['To'] = email
                                
                                html_content = f"""
                                <html>
                                <body style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
                                    <h2 style="color: #333;">Verify Your Email Address</h2>
                                    <p>Hi {username},</p>
                                    <p>Thank you for signing up for MbetterClient. Please verify your email address by clicking the button below:</p>
                                    <p style="text-align: center; margin: 30px 0;">
                                        <a href="{verification_link}" style="background-color: #007bff; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px; display: inline-block;">Verify Email</a>
                                    </p>
                                    <p>Or copy and paste this link into your browser:</p>
                                    <p style="word-break: break-all; color: #666;">{verification_link}</p>
                                    <p style="color: #666; font-size: 12px;">This link will expire in 24 hours.</p>
                                    <hr style="margin: 30px 0; border: none; border-top: 1px solid #eee;">
                                    <p style="color: #999; font-size: 12px;">If you didn't create an account, please ignore this email.</p>
                                </body>
                                </html>
                                """
                                
                                msg.attach(MIMEText(html_content, 'html'))
                                
                                # Send email
                                smtp_host = settings.get('smtp_host')
                                smtp_port = settings.get('smtp_port', 587)
                                smtp_username = settings.get('smtp_username')
                                smtp_password = settings.get('smtp_password')
                                smtp_use_tls = settings.get('smtp_use_tls', 'tls')
                                
                                if smtp_use_tls == 'ssl':
                                    server = smtplib.SMTP_SSL(smtp_host, smtp_port, timeout=10)
                                else:
                                    server = smtplib.SMTP(smtp_host, smtp_port, timeout=10)
                                    if smtp_use_tls == 'tls':
                                        server.starttls()
                                
                                server.login(smtp_username, smtp_password)
                                server.send_message(msg)
                                server.quit()
                                
                                logger.info(f"Verification email sent to {email}")
                        finally:
                            session.close()
            except Exception as email_error:
                # Don't fail signup if email sending fails
                logger.warning(f"Failed to send verification email: {email_error}")
            
            return jsonify({
                "success": True,
                "message": "Account created successfully. Please check your email to verify your account.",
                "user": {
                    "username": username,
                    "email": email,
                    "role": role
                }
            }), 201
        else:
            logger.warning(f"Signup failed: create_user returned None for username: {username}")
            return jsonify({
                "success": False,
                "error": "Failed to create account. Username or email may already exist."
            }), 400
            
    except Exception as e:
        logger.error(f"API signup error: {e}", exc_info=True)
        return jsonify({
            "success": False,
            "error": "An error occurred during sign-up"
        }), 500


# Sign-up Settings API routes
@api_bp.route('/signup-settings')
def get_signup_settings():
    """Get sign-up configuration"""
    try:
        if api_bp.db_manager:
            settings = {
                'dashboard_enabled': api_bp.db_manager.get_config_value('dashboard_signup.enabled', False),
                'website_enabled': api_bp.db_manager.get_config_value('website_signup.enabled', False)
            }
        else:
            # Default settings
            settings = {
                'dashboard_enabled': False,
                'website_enabled': False
            }
        
        return jsonify({
            "success": True,
            "settings": settings
        })
    except Exception as e:
        logger.error(f"API get signup settings error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/signup-settings', methods=['POST'])
@get_api_auth_decorator()
@get_api_auth_decorator(require_admin=True)
def set_signup_settings():
    """Set sign-up configuration (admin only)"""
    try:
        data = request.get_json() or {}
        dashboard_enabled = data.get('dashboard_enabled', False)
        website_enabled = data.get('website_enabled', False)
        
        if api_bp.db_manager:
            # Save sign-up configuration to database
            api_bp.db_manager.set_config_value('dashboard_signup.enabled', dashboard_enabled)
            api_bp.db_manager.set_config_value('website_signup.enabled', website_enabled)
            
            logger.info(f"Sign-up settings updated - dashboard: {dashboard_enabled}, website: {website_enabled}")
            
            return jsonify({
                "success": True,
                "message": "Sign-up settings updated",
                "settings": {
                    "dashboard_enabled": dashboard_enabled,
                    "website_enabled": website_enabled
                }
            })
        else:
            return jsonify({"error": "Database manager not available"}), 500
            
    except Exception as e:
        logger.error(f"API set signup settings error: {e}")
        return jsonify({"error": str(e)}), 500

# API Login endpoint for broker/player authentication
@api_bp.route('/auth/login', methods=['POST'])
def api_login():
    """API login endpoint for broker/player authentication - returns JWT token"""
    try:
        logger.info(f"API login endpoint called - method: {request.method}, content-type: {request.content_type}")
        
        data = request.get_json() or {}
        logger.info(f"API login request data: {data}")
        
        username = data.get('username', '').strip()
        email = data.get('email', '').strip()
        password = data.get('password', '')
        remember = data.get('remember', False)
        
        logger.info(f"API login - username: '{username}', email: '{email}', password length: {len(password)}, remember: {remember}")
        
        # Allow login with either username or email
        if not password:
            logger.warning(f"API login 400 error - password empty")
            return jsonify({
                "success": False,
                "error": "Password is required"
            }), 400
        
        if not username and not email:
            logger.warning(f"API login 400 error - both username and email empty")
            return jsonify({
                "success": False,
                "error": "Username or email is required"
            }), 400
        
        # Get auth manager
        auth_manager = getattr(api_bp, 'auth_manager', None)
        logger.info(f"API login - auth_manager available: {auth_manager is not None}")
        
        if not auth_manager:
            logger.error("API login failed: auth_manager not available")
            return jsonify({
                "success": False,
                "error": "Authentication system not available"
            }), 500
        
        # Authenticate user - try username first, then email if provided
        authenticated_user = None
        login_identifier = ""
        
        if username:
            logger.info(f"API login - attempting to authenticate with username: {username}")
            authenticated_user = auth_manager.authenticate_user(username, password)
            login_identifier = username
        elif email:
            logger.info(f"API login - attempting to authenticate with email: {email}")
            authenticated_user = auth_manager.authenticate_user(email, password)
            login_identifier = email
        
        logger.info(f"API login - authentication result: {authenticated_user is not None}")
        
        if authenticated_user:
            # Check email verification for brokers and players only
            user_role = getattr(authenticated_user, 'role', 'normal')
            email_verified = getattr(authenticated_user, 'email_verified', True)
            
            # Only require email verification for brokers and players
            if user_role in ['broker', 'player'] and not email_verified:
                logger.warning(f"API login failed for user {login_identifier}: email not verified")
                return jsonify({
                    "success": False,
                    "error": "Please verify your email address before logging in",
                    "requires_verification": True
                }), 403
            
            # Create JWT token for API access (with refresh token if remember is true)
            logger.info(f"API login - creating JWT token for user: {login_identifier}, remember: {remember}")
            token_data = auth_manager.create_jwt_token(authenticated_user.id, include_refresh=remember)
            logger.info(f"API login - token created: {token_data is not None}")
            
            if token_data:
                logger.info(f"API login successful for user: {login_identifier}")
                response = {
                    "success": True,
                    "message": "Login successful",
                    "access_token": token_data['access_token'],
                    "expires_in": token_data['expires_in'],
                    "token_type": token_data['token_type'],
                    "user": {
                        "id": authenticated_user.id,
                        "username": authenticated_user.username,
                        "email": authenticated_user.email,
                        "role": user_role,
                        "is_admin": authenticated_user.is_admin,
                        "email_verified": email_verified
                    }
                }
                # Include refresh token only if remember is true
                if remember and 'refresh_token' in token_data:
                    response['refresh_token'] = token_data['refresh_token']
                
                return jsonify(response), 200
            else:
                logger.error(f"API login failed for user {login_identifier}: could not create token")
                return jsonify({
                    "success": False,
                    "error": "Failed to create authentication token"
                }), 500
        else:
            logger.warning(f"API login failed for user: {login_identifier} - invalid credentials")
            return jsonify({
                "success": False,
                "error": "Invalid username/email or password"
            }), 401
            
    except Exception as e:
        logger.error(f"API login error: {e}", exc_info=True)
        return jsonify({
            "success": False,
            "error": "An error occurred during login"
        }), 500


# API Refresh token endpoint for persistent sessions
@api_bp.route('/auth/refresh', methods=['POST'])
def api_refresh_token():
    """API refresh token endpoint - refresh access token using refresh token"""
    try:
        data = request.get_json() or {}
        refresh_token = data.get('refresh_token')
        
        if not refresh_token:
            return jsonify({
                "success": False,
                "error": "Refresh token is required"
            }), 400
        
        # Get auth manager
        auth_manager = getattr(api_bp, 'auth_manager', None)
        
        if not auth_manager:
            logger.error("Token refresh failed: auth_manager not available")
            return jsonify({
                "success": False,
                "error": "Authentication system not available"
            }), 500
        
        # Refresh the token
        token_data = auth_manager.refresh_jwt_token(refresh_token)
        
        if token_data:
            return jsonify({
                "success": True,
                "access_token": token_data['access_token'],
                "expires_in": token_data['expires_in'],
                "token_type": token_data['token_type']
            }), 200
        else:
            return jsonify({
                "success": False,
                "error": "Invalid or expired refresh token"
            }), 401
            
    except Exception as e:
        logger.error(f"Token refresh error: {e}", exc_info=True)
        return jsonify({
            "success": False,
            "error": "An error occurred during token refresh"
        }), 500


# API Logout endpoint for revoking tokens
@api_bp.route('/auth/logout', methods=['POST'])
def api_logout():
    """API logout endpoint - revoke tokens"""
    try:
        data = request.get_json() or {}
        refresh_token = data.get('refresh_token')
        
        # Get auth manager
        auth_manager = getattr(api_bp, 'auth_manager', None)
        
        if not auth_manager:
            logger.error("Logout failed: auth_manager not available")
            return jsonify({
                "success": False,
                "error": "Authentication system not available"
            }), 500
        
        # Revoke refresh token if provided
        if refresh_token:
            auth_manager.revoke_jwt_token(refresh_token)
            logger.info("Refresh token revoked during logout")
        
        # Also try to revoke the access token from Authorization header
        auth_header = request.headers.get('Authorization')
        if auth_header and auth_header.startswith('Bearer '):
            access_token = auth_header.split(' ', 1)[1]
            auth_manager.revoke_jwt_token(access_token)
            logger.info("Access token revoked during logout")
        
        return jsonify({
            "success": True,
            "message": "Logged out successfully"
        }), 200
            
    except Exception as e:
        logger.error(f"Logout error: {e}", exc_info=True)
        return jsonify({
            "success": False,
            "error": "An error occurred during logout"
        }), 500


# Broker API endpoints
@api_bp.route('/broker/api-key')
@get_api_auth_decorator()
def get_broker_api_key():
    """Get the first active API key for the current broker (backwards compatibility)"""
    try:
        # Check if user is a broker
        logger.debug(f"request.current_user: {request.current_user}")
        logger.debug(f"User role: {request.current_user.get('role', 'unknown')}")
        
        if request.current_user.get('role', '') != 'broker':
            logger.warning(f"API key access denied to non-broker user: {request.current_user.get('username', 'unknown')}")
            return jsonify({
                "success": False,
                "error": "Broker access required"
            }), 403
        
        user_id = request.current_user.get('user_id')
        
        session = api_bp.db_manager.get_session()
        try:
            from ..database.models import ApiKeyModel
            
            # Get the first active API key for the user
            api_key = session.query(ApiKeyModel).filter_by(
                user_id=user_id,
                is_active=True
            ).order_by(ApiKeyModel.created_at.desc()).first()
            
            if api_key:
                logger.info(f"Retrieved API key for broker: {request.current_user.get('username', 'unknown')}")
                return jsonify({
                    "success": True,
                    "api_key": api_key.key_hash  # Note: We don't store plain text keys, so we can't return the actual key here
                })
            else:
                # If no API key exists, create one
                from datetime import datetime, timedelta
                new_api_key = ApiKeyModel.generate_api_key()
                key_hash = ApiKeyModel.hash_key(new_api_key)
                
                api_key = ApiKeyModel(
                    user_id=user_id,
                    name="Default API Key",
                    key_hash=key_hash,
                    is_active=True,
                    expires_at=datetime.utcnow() + timedelta(days=365)
                )
                
                session.add(api_key)
                session.commit()
                
                logger.info(f"Created new API key for broker: {request.current_user.get('username', 'unknown')}")
                return jsonify({
                    "success": True,
                    "api_key": new_api_key
                })
            
        finally:
            session.close()
            
    except Exception as e:
        logger.error(f"Get API key error: {e}")
        return jsonify({
            "success": False,
            "error": "Failed to retrieve API key"
        }), 500


@api_bp.route('/broker/api-keys')
@get_api_auth_decorator()
def get_broker_api_keys():
    """Get all API keys for the current broker"""
    try:
        # Check if user is a broker
        if request.current_user.get('role', '') != 'broker':
            logger.warning(f"API keys access denied to non-broker user: {request.current_user.get('username', 'unknown')}")
            return jsonify({
                "success": False,
                "error": "Broker access required"
            }), 403
        
        user_id = request.current_user.get('user_id')
        
        session = api_bp.db_manager.get_session()
        try:
            from ..database.models import ApiKeyModel
            
            # Get all active API keys for the user
            api_keys = session.query(ApiKeyModel).filter_by(
                user_id=user_id,
                is_active=True
            ).order_by(ApiKeyModel.created_at.desc()).all()
            
            # Convert to dict format
            keys_data = []
            for api_key in api_keys:
                keys_data.append({
                    'id': api_key.id,
                    'name': api_key.name,
                    'is_active': api_key.is_active,
                    'expires_at': api_key.expires_at.isoformat(),
                    'created_at': api_key.created_at.isoformat(),
                    'updated_at': api_key.updated_at.isoformat(),
                    'last_used_at': api_key.last_used_at.isoformat() if api_key.last_used_at else None,
                    'last_used_ip': api_key.last_used_ip
                })
            
            logger.info(f"Retrieved {len(keys_data)} API keys for broker: {request.current_user.get('username', 'unknown')}")
            
            return jsonify({
                "success": True,
                "api_keys": keys_data
            })
            
        finally:
            session.close()
            
    except Exception as e:
        logger.error(f"Get API keys error: {e}")
        return jsonify({
            "success": False,
            "error": "Failed to retrieve API keys"
        }), 500


@api_bp.route('/broker/api-keys', methods=['POST'])
@get_api_auth_decorator()
def create_broker_api_key():
    """Create a new API key for the broker"""
    try:
        # Check if user is a broker
        if request.current_user.get('role', '') != 'broker':
            logger.warning(f"API key creation denied to non-broker user: {request.current_user.get('username', 'unknown')}")
            return jsonify({
                "success": False,
                "error": "Broker access required"
            }), 403
        
        user_id = request.current_user.get('user_id')
        data = request.get_json() or {}
        key_name = data.get('name', 'Default API Key')
        expires_days = data.get('expires_days', 365)
        
        session = api_bp.db_manager.get_session()
        try:
            from ..database.models import ApiKeyModel
            from datetime import datetime, timedelta
            
            # Generate new API key
            api_key = ApiKeyModel.generate_api_key()
            key_hash = ApiKeyModel.hash_key(api_key)
            
            # Create new API key record
            new_key = ApiKeyModel(
                user_id=user_id,
                name=key_name,
                key_hash=key_hash,
                is_active=True,
                expires_at=datetime.utcnow() + timedelta(days=expires_days)
            )
            
            session.add(new_key)
            session.commit()
            
            logger.info(f"API key created for broker: {request.current_user.get('username', 'unknown')}, name: {key_name}")
            
            return jsonify({
                "success": True,
                "message": "API key created successfully",
                "api_key": {
                    'id': new_key.id,
                    'name': new_key.name,
                    'api_key': api_key,  # Return the plain text key only once
                    'is_active': new_key.is_active,
                    'expires_at': new_key.expires_at.isoformat(),
                    'created_at': new_key.created_at.isoformat(),
                    'updated_at': new_key.updated_at.isoformat()
                }
            })
            
        finally:
            session.close()
            
    except Exception as e:
        logger.error(f"Create API key error: {e}")
        return jsonify({
            "success": False,
            "error": "Failed to create API key"
        }), 500


@api_bp.route('/broker/api-keys/<int:key_id>', methods=['DELETE'])
@get_api_auth_decorator()
def revoke_broker_api_key(key_id):
    """Revoke an API key"""
    try:
        # Check if user is a broker
        if request.current_user.get('role', '') != 'broker':
            logger.warning(f"API key revocation denied to non-broker user: {request.current_user.get('username', 'unknown')}")
            return jsonify({
                "success": False,
                "error": "Broker access required"
            }), 403
        
        user_id = request.current_user.get('user_id')
        
        session = api_bp.db_manager.get_session()
        try:
            from ..database.models import ApiKeyModel
            
            # Find the API key
            api_key = session.query(ApiKeyModel).filter_by(
                id=key_id,
                user_id=user_id
            ).first()
            
            if not api_key:
                return jsonify({
                    "success": False,
                    "error": "API key not found"
                }), 404
            
            # Revoke the key
            api_key.revoke()
            session.commit()
            
            logger.info(f"API key revoked for broker: {request.current_user.get('username', 'unknown')}, key id: {key_id}")
            
            return jsonify({
                "success": True,
                "message": "API key revoked successfully"
            })
            
        finally:
            session.close()
            
    except Exception as e:
        logger.error(f"Revoke API key error: {e}")
        return jsonify({
            "success": False,
            "error": "Failed to revoke API key"
        }), 500

@api_bp.route('/broker/regenerate-api-key', methods=['POST'])
@get_api_auth_decorator()
def regenerate_broker_api_key():
    """Regenerate the current broker's API key (backwards compatibility)"""
    try:
        # Check if user is a broker
        if request.current_user.get('role', '') != 'broker':
            logger.warning(f"API key regeneration denied to non-broker user: {request.current_user.get('username', 'unknown')}")
            return jsonify({
                "success": False,
                "error": "Broker access required"
            }), 403
        
        user_id = request.current_user.get('user_id')
        
        session = api_bp.db_manager.get_session()
        try:
            from ..database.models import ApiKeyModel
            from datetime import datetime, timedelta
            
            # Find the current active API key
            api_key = session.query(ApiKeyModel).filter_by(
                user_id=user_id,
                is_active=True
            ).first()
            
            if api_key:
                # Regenerate existing key
                new_api_key = ApiKeyModel.generate_api_key()
                new_key_hash = ApiKeyModel.hash_key(new_api_key)
                
                api_key.key_hash = new_key_hash
                api_key.updated_at = datetime.utcnow()
                api_key.last_used_at = None
                api_key.last_used_ip = None
                
                session.commit()
                
                logger.info(f"API key regenerated for broker: {request.current_user.get('username', 'unknown')}, key id: {api_key.id}")
                
                return jsonify({
                    "success": True,
                    "message": "API key regenerated successfully",
                    "api_key": new_api_key
                })
            else:
                # If no API key exists, create one
                new_api_key = ApiKeyModel.generate_api_key()
                key_hash = ApiKeyModel.hash_key(new_api_key)
                
                api_key = ApiKeyModel(
                    user_id=user_id,
                    name="Default API Key",
                    key_hash=key_hash,
                    is_active=True,
                    expires_at=datetime.utcnow() + timedelta(days=365)
                )
                
                session.add(api_key)
                session.commit()
                
                logger.info(f"Created new API key for broker: {request.current_user.get('username', 'unknown')}")
                return jsonify({
                    "success": True,
                    "message": "API key created successfully",
                    "api_key": new_api_key
                })
            
        finally:
            session.close()
            
    except Exception as e:
        logger.error(f"Regenerate API key error: {e}")
        return jsonify({
            "success": False,
            "error": "Failed to regenerate API key"
        }), 500


@api_bp.route('/broker/api-keys/<int:key_id>/regenerate', methods=['POST'])
@get_api_auth_decorator()
def regenerate_broker_api_key_by_id(key_id):
    """Regenerate an existing API key by ID"""
    try:
        # Check if user is a broker
        if request.current_user.get('role', '') != 'broker':
            logger.warning(f"API key regeneration denied to non-broker user: {request.current_user.get('username', 'unknown')}")
            return jsonify({
                "success": False,
                "error": "Broker access required"
            }), 403
        
        user_id = request.current_user.get('user_id')
        
        session = api_bp.db_manager.get_session()
        try:
            from ..database.models import ApiKeyModel
            from datetime import datetime, timedelta
            
            # Find the API key
            api_key = session.query(ApiKeyModel).filter_by(
                id=key_id,
                user_id=user_id
            ).first()
            
            if not api_key:
                return jsonify({
                    "success": False,
                    "error": "API key not found"
                }), 404
            
            # Generate new API key
            new_api_key = ApiKeyModel.generate_api_key()
            new_key_hash = ApiKeyModel.hash_key(new_api_key)
            
            # Update the key
            api_key.key_hash = new_key_hash
            api_key.updated_at = datetime.utcnow()
            api_key.last_used_at = None
            api_key.last_used_ip = None
            
            session.commit()
            
            logger.info(f"API key regenerated for broker: {request.current_user.get('username', 'unknown')}, key id: {key_id}")
            
            return jsonify({
                "success": True,
                "message": "API key regenerated successfully",
                "api_key": {
                    'id': api_key.id,
                    'name': api_key.name,
                    'api_key': new_api_key,  # Return the plain text key only once
                    'is_active': api_key.is_active,
                    'expires_at': api_key.expires_at.isoformat(),
                    'created_at': api_key.created_at.isoformat(),
                    'updated_at': api_key.updated_at.isoformat()
                }
            })
            
        finally:
            session.close()
            
    except Exception as e:
        logger.error(f"Regenerate API key error: {e}")
        return jsonify({
            "success": False,
            "error": "Failed to regenerate API key"
        }), 500



@api_bp.route('/broker/membership')
@get_api_auth_decorator()
def get_broker_membership():
    """Get broker's membership information"""
    try:
        # Check if user is a broker
        if request.current_user.get('role', '') != 'broker':
            logger.warning(f"Membership info access denied to non-broker user: {request.current_user.get('username', 'unknown')}")
            return jsonify({
                "success": False,
                "error": "Broker access required"
            }), 403
        
        # Return dummy membership information
        return jsonify({
            "success": True,
            "membership": {
                "plan": "Basic",
                "status": "Active",
                "api_calls": 1200,
                "api_limit": 2000,
                "start_date": "2026-01-01",
                "expiry_date": "2026-12-31",
                "renewal_date": "2026-12-01",
                "payment_method": "Credit Card"
            }
        })
        
    except Exception as e:
        logger.error(f"Get membership info error: {e}")
        return jsonify({
            "success": False,
            "error": "Failed to retrieve membership information"
        }), 500


@api_bp.route('/broker/upgrade-membership', methods=['POST'])
@get_api_auth_decorator()
def upgrade_broker_membership():
    """Upgrade broker's membership plan"""
    try:
        # Check if user is a broker
        if request.current_user.get('role', '') != 'broker':
            logger.warning(f"Membership upgrade denied to non-broker user: {request.current_user.get('username', 'unknown')}")
            return jsonify({
                "success": False,
                "error": "Broker access required"
            }), 403
        
        data = request.get_json() or {}
        new_plan = data.get('plan', '').strip()
        
        if not new_plan:
            return jsonify({
                "success": False,
                "error": "Plan name is required"
            }), 400
        
        logger.info(f"Membership upgrade request for broker {request.current_user.get('username', 'unknown')} to plan: {new_plan}")
        
        return jsonify({
            "success": True,
            "message": f"Membership upgraded to {new_plan} successfully"
        })
        
    except Exception as e:
        logger.error(f"Upgrade membership error: {e}")
        return jsonify({
            "success": False,
            "error": "Failed to upgrade membership"
        }), 500


@api_bp.route('/broker/stats')
@get_api_auth_decorator()
def get_broker_stats():
    """Get broker's statistics"""
    try:
        # Check if user is a broker
        if request.current_user.get('role', '') != 'broker':
            logger.warning(f"Stats access denied to non-broker user: {request.current_user.get('username', 'unknown')}")
            return jsonify({
                "success": False,
                "error": "Broker access required"
            }), 403
        
        # Return dummy statistics
        return jsonify({
            "success": True,
            "statistics": {
                "total_bets": 125,
                "total_amount": 15000,
                "total_wins": 45,
                "win_rate": 36,
                "total_payout": 8900,
                "profit": 6100,
                "api_calls_today": 45,
                "active_players": 23
            }
        })
        
    except Exception as e:
        logger.error(f"Get stats error: {e}")
        return jsonify({
            "success": False,
            "error": "Failed to retrieve statistics"
        }), 500


@api_bp.route('/broker/webhook')
@get_api_auth_decorator()
def get_broker_webhook():
    """Get broker's webhook configuration"""
    try:
        # Check if user is a broker
        if request.current_user.get('role', '') != 'broker':
            logger.warning(f"Webhook config access denied to non-broker user: {request.current_user.get('username', 'unknown')}")
            return jsonify({
                "success": False,
                "error": "Broker access required"
            }), 403
        
        # Return dummy webhook configuration
        return jsonify({
            "success": True,
            "webhook": {
                "url": "https://api.yourdomain.com/webhook",
                "secret": "whsec_1234567890abcdef",
                "events": ["bet_placed", "bet_result", "payment_received"],
                "active": True
            }
        })
        
    except Exception as e:
        logger.error(f"Get webhook config error: {e}")
        return jsonify({
            "success": False,
            "error": "Failed to retrieve webhook configuration"
        }), 500


@api_bp.route('/broker/webhook', methods=['PUT'])
@get_api_auth_decorator()
def update_broker_webhook():
    """Update broker's webhook configuration"""
    try:
        # Check if user is a broker
        if request.current_user.get('role', '') != 'broker':
            logger.warning(f"Webhook config update denied to non-broker user: {request.current_user.get('username', 'unknown')}")
            return jsonify({
                "success": False,
                "error": "Broker access required"
            }), 403
        
        data = request.get_json() or {}
        
        logger.info(f"Webhook config updated for broker: {request.current_user.get('username', 'unknown')}")
        
        return jsonify({
            "success": True,
            "message": "Webhook configuration updated successfully"
        })
        
    except Exception as e:
        logger.error(f"Update webhook config error: {e}")
        return jsonify({
            "success": False,
            "error": "Failed to update webhook configuration"
        }), 500


@api_bp.route('/broker/settings')
@get_api_auth_decorator()
def get_broker_settings():
    """Get broker's application settings"""
    try:
        # Check if user is a broker
        if request.current_user.get('role', '') != 'broker':
            logger.warning(f"Settings access denied to non-broker user: {request.current_user.get('username', 'unknown')}")
            return jsonify({
                "success": False,
                "error": "Broker access required"
            }), 403
        
        # Return dummy settings
        return jsonify({
            "success": True,
            "settings": {
                "application_name": "My Betting App",
                "currency": "USD",
                "commission_rate": 5,
                "minimum_bet": 1,
                "maximum_bet": 500,
                "betting_hours": "08:00-22:00",
                "notifications": True,
                "email_alerts": True,
                "payment_methods": ["credit_card", "paypal", "mobile_money"]
            }
        })
        
    except Exception as e:
        logger.error(f"Get settings error: {e}")
        return jsonify({
            "success": False,
            "error": "Failed to retrieve settings"
        }), 500


@api_bp.route('/broker/settings', methods=['PUT'])
@get_api_auth_decorator()
def update_broker_settings():
    """Update broker's application settings"""
    try:
        # Check if user is a broker
        if request.current_user.get('role', '') != 'broker':
            logger.warning(f"Settings update denied to non-broker user: {request.current_user.get('username', 'unknown')}")
            return jsonify({
                "success": False,
                "error": "Broker access required"
            }), 403
        
        data = request.get_json() or {}
        
        logger.info(f"Settings updated for broker: {request.current_user.get('username', 'unknown')}")
        
        return jsonify({
            "success": True,
            "message": "Settings updated successfully"
        })
        
    except Exception as e:
        logger.error(f"Update settings error: {e}")
        return jsonify({
            "success": False,
            "error": "Failed to update settings"
        }), 500


@api_bp.route('/broker/test-connection', methods=['POST'])
@get_api_auth_decorator()
def test_broker_api_connection():
    """Test broker API connection"""
    try:
        # Check if user is a broker
        if request.current_user.get('role', '') != 'broker':
            logger.warning(f"API connection test denied to non-broker user: {request.current_user.get('username', 'unknown')}")
            return jsonify({
                "success": False,
                "error": "Broker access required"
            }), 403
        
        # In a real implementation, this would test the API connection
        # For now, we'll just return a success message
        logger.info(f"API connection test performed by broker: {request.current_user.get('username', 'unknown')}")
        
        return jsonify({
            "success": True,
            "message": "API connection successful",
            "timestamp": datetime.now().isoformat(),
            "status": "online"
        })
        
    except Exception as e:
        logger.error(f"Test API connection error: {e}")
        return jsonify({
            "success": False,
            "error": "API connection test failed"
        }), 500


@api_bp.route('/broker/logs')
@get_api_auth_decorator()
def get_broker_logs():
    """Get broker's API logs"""
    try:
        # Check if user is a broker
        if request.current_user.get('role', '') != 'broker':
            logger.warning(f"Logs access denied to non-broker user: {request.current_user.get('username', 'unknown')}")
            return jsonify({
                "success": False,
                "error": "Broker access required"
            }), 403
        
        # Return dummy logs
        logs = [
            {
                "id": 1,
                "timestamp": "2026-02-09T10:15:00",
                "level": "INFO",
                "message": "Bet placed successfully",
                "request_id": "req_12345",
                "duration_ms": 150
            },
            {
                "id": 2,
                "timestamp": "2026-02-09T10:14:30",
                "level": "INFO",
                "message": "Payment received",
                "request_id": "req_12344",
                "duration_ms": 200
            },
            {
                "id": 3,
                "timestamp": "2026-02-09T10:14:00",
                "level": "WARNING",
                "message": "API key will expire soon",
                "request_id": "req_12343",
                "duration_ms": 50
            },
            {
                "id": 4,
                "timestamp": "2026-02-09T10:13:45",
                "level": "ERROR",
                "message": "Failed to process bet",
                "request_id": "req_12342",
                "duration_ms": 300,
                "error": "Insufficient balance"
            }
        ]
        
        logger.info(f"Logs retrieved for broker: {request.current_user.get('username', 'unknown')}")
        
        return jsonify({
            "success": True,
            "logs": logs,
            "total_count": len(logs)
        })
        
    except Exception as e:
        logger.error(f"Get logs error: {e}")
        return jsonify({
            "success": False,
            "error": "Failed to retrieve logs"
        }), 500


# Player API endpoints
@api_bp.route('/player/matches')
@get_api_auth_decorator()
def get_player_matches():
    """Get upcoming and live matches for player"""
    try:
        # Check if user is a player
        if request.current_user.get('role', '') != 'player':
            logger.warning(f"Matches access denied to non-player user: {request.current_user.get('username', 'unknown')}")
            return jsonify({
                "success": False,
                "error": "Player access required"
            }), 403
        
        # Return dummy matches
        matches = [
            {
                "id": 1,
                "match_number": 5,
                "fighter1": "Lion",
                "fighter2": "Tiger",
                "fighter1_township": "Kampala",
                "fighter2_township": "Jinja",
                "venue": "Kampala",
                "status": "live",
                "time_remaining": 150,
                "current_round": 2,
                "odds": {
                    "fighter1": 1.85,
                    "fighter2": 1.95,
                    "draw": 3.20
                }
            },
            {
                "id": 2,
                "match_number": 6,
                "fighter1": "Elephant",
                "fighter2": "Giraffe",
                "fighter1_township": "Mbarara",
                "fighter2_township": "Mbale",
                "venue": "Kampala",
                "status": "upcoming",
                "start_time": "2026-02-09T14:00:00",
                "odds": {
                    "fighter1": 1.75,
                    "fighter2": 2.05,
                    "draw": 3.30
                }
            },
            {
                "id": 3,
                "match_number": 7,
                "fighter1": "Rhino",
                "fighter2": "Hippo",
                "fighter1_township": "Gulu",
                "fighter2_township": "Arua",
                "venue": "Kampala",
                "status": "upcoming",
                "start_time": "2026-02-09T14:30:00",
                "odds": {
                    "fighter1": 2.10,
                    "fighter2": 1.70,
                    "draw": 3.10
                }
            }
        ]
        
        logger.info(f"Matches retrieved for player: {request.current_user.get('username', 'unknown')}")
        
        return jsonify({
            "success": True,
            "matches": matches
        })
        
    except Exception as e:
        logger.error(f"Get matches error: {e}")
        return jsonify({
            "success": False,
            "error": "Failed to retrieve matches"
        }), 500


@api_bp.route('/player/bets')
@get_api_auth_decorator()
def get_player_bets():
    """Get player's bet history"""
    try:
        # Check if user is a player
        if request.current_user.get('role', '') != 'player':
            logger.warning(f"Bets access denied to non-player user: {request.current_user.get('username', 'unknown')}")
            return jsonify({
                "success": False,
                "error": "Player access required"
            }), 403
        
        # Return dummy bets
        bets = [
            {
                "id": 1,
                "match_id": 1,
                "match_number": 5,
                "fighter1": "Lion",
                "fighter2": "Tiger",
                "outcome": "fighter1",
                "amount": 50,
                "odds": 1.85,
                "potential_winning": 92.50,
                "status": "pending",
                "bet_datetime": "2026-02-09T10:15:00"
            },
            {
                "id": 2,
                "match_id": 4,
                "match_number": 4,
                "fighter1": "Cheetah",
                "fighter2": "Leopard",
                "outcome": "fighter2",
                "amount": 30,
                "odds": 1.90,
                "potential_winning": 57.00,
                "status": "won",
                "bet_datetime": "2026-02-09T09:45:00"
            },
            {
                "id": 3,
                "match_id": 3,
                "match_number": 3,
                "fighter1": "Zebra",
                "fighter2": "Horse",
                "outcome": "draw",
                "amount": 20,
                "odds": 3.20,
                "potential_winning": 64.00,
                "status": "lost",
                "bet_datetime": "2026-02-09T09:15:00"
            }
        ]
        
        logger.info(f"Bets retrieved for player: {request.current_user.get('username', 'unknown')}")
        
        return jsonify({
            "success": True,
            "bets": bets
        })
        
    except Exception as e:
        logger.error(f"Get bets error: {e}")
        return jsonify({
            "success": False,
            "error": "Failed to retrieve bets"
        }), 500


@api_bp.route('/player/bets', methods=['POST'])
@get_api_auth_decorator()
def place_player_bet():
    """Place a bet for player"""
    try:
        # Check if user is a player
        if request.current_user.get('role', '') != 'player':
            logger.warning(f"Bet placement denied to non-player user: {request.current_user.get('username', 'unknown')}")
            return jsonify({
                "success": False,
                "error": "Player access required"
            }), 403
        
        data = request.get_json() or {}
        match_id = data.get('match_id')
        outcome = data.get('outcome', '').strip()
        amount = data.get('amount')
        
        if not all([match_id, outcome, amount]):
            return jsonify({
                "success": False,
                "error": "Match ID, outcome, and amount are required"
            }), 400
        
        if amount <= 0:
            return jsonify({
                "success": False,
                "error": "Amount must be greater than 0"
            }), 400
        
        logger.info(f"Bet placed by player {request.current_user.get('username', 'unknown')}: Match {match_id}, Outcome {outcome}, Amount {amount}")
        
        return jsonify({
            "success": True,
            "message": "Bet placed successfully",
            "bet": {
                "id": 4,
                "match_id": match_id,
                "outcome": outcome,
                "amount": amount,
                "odds": 1.85,
                "potential_winning": amount * 1.85,
                "status": "pending",
                "bet_datetime": datetime.now().isoformat()
            }
        })
        
    except Exception as e:
        logger.error(f"Place bet error: {e}")
        return jsonify({
            "success": False,
            "error": "Failed to place bet"
        }), 500


@api_bp.route('/player/balance')
@get_api_auth_decorator()
def get_player_balance():
    """Get player's account balance"""
    try:
        # Check if user is a player
        if request.current_user.get('role', '') != 'player':
            logger.warning(f"Balance access denied to non-player user: {request.current_user.get('username', 'unknown')}")
            return jsonify({
                "success": False,
                "error": "Player access required"
            }), 403
        
        # Return dummy balance (flat structure for frontend compatibility)
        return jsonify({
            "success": True,
            "balance": 250.00,
            "available": 200.00,
            "pending": 50.00
        })
        
    except Exception as e:
        logger.error(f"Get balance error: {e}")
        return jsonify({
            "success": False,
            "error": "Failed to retrieve balance"
        }), 500


@api_bp.route('/player/statistics')
@get_api_auth_decorator()
def get_player_statistics():
    """Get player's betting statistics"""
    try:
        # Check if user is a player
        if request.current_user.get('role', '') != 'player':
            logger.warning(f"Statistics access denied to non-player user: {request.current_user.get('username', 'unknown')}")
            return jsonify({
                "success": False,
                "error": "Player access required"
            }), 403
        
        # Return dummy statistics
        return jsonify({
            "success": True,
            "total_bets": 15,
            "bets_won": 8,
            "bets_lost": 5,
            "bets_pending": 2,
            "win_rate": 61.5,
            "profit_loss": 125.50,
            "total_staked": 500.00,
            "total_winnings": 625.50
        })
        
    except Exception as e:
        logger.error(f"Get statistics error: {e}")
        return jsonify({
            "success": False,
            "error": "Failed to retrieve statistics"
        }), 500


@api_bp.route('/player/deposit', methods=['POST'])
@get_api_auth_decorator()
def deposit_player_funds():
    """Deposit funds to player's account"""
    try:
        # Check if user is a player
        if request.current_user.get('role', '') != 'player':
            logger.warning(f"Deposit denied to non-player user: {request.current_user.get('username', 'unknown')}")
            return jsonify({
                "success": False,
                "error": "Player access required"
            }), 403
        
        data = request.get_json() or {}
        amount = data.get('amount')
        payment_method = data.get('payment_method', '').strip()
        
        if not all([amount, payment_method]):
            return jsonify({
                "success": False,
                "error": "Amount and payment method are required"
            }), 400
        
        if amount <= 0:
            return jsonify({
                "success": False,
                "error": "Amount must be greater than 0"
            }), 400
        
        logger.info(f"Deposit by player {request.current_user.get('username', 'unknown')}: Amount {amount}, Method {payment_method}")
        
        return jsonify({
            "success": True,
            "message": "Deposit successful",
            "transaction": {
                "id": 1001,
                "type": "deposit",
                "amount": amount,
                "payment_method": payment_method,
                "status": "completed",
                "timestamp": datetime.now().isoformat()
            }
        })
        
    except Exception as e:
        logger.error(f"Deposit error: {e}")
        return jsonify({
            "success": False,
            "error": "Failed to process deposit"
        }), 500


@api_bp.route('/player/withdraw', methods=['POST'])
@get_api_auth_decorator()
def withdraw_player_funds():
    """Withdraw funds from player's account"""
    try:
        # Check if user is a player
        if request.current_user.get('role', '') != 'player':
            logger.warning(f"Withdrawal denied to non-player user: {request.current_user.get('username', 'unknown')}")
            return jsonify({
                "success": False,
                "error": "Player access required"
            }), 403
        
        data = request.get_json() or {}
        amount = data.get('amount')
        payment_method = data.get('payment_method', '').strip()
        
        if not all([amount, payment_method]):
            return jsonify({
                "success": False,
                "error": "Amount and payment method are required"
            }), 400
        
        if amount <= 0:
            return jsonify({
                "success": False,
                "error": "Amount must be greater than 0"
            }), 400
        
        logger.info(f"Withdrawal by player {request.current_user.get('username', 'unknown')}: Amount {amount}, Method {payment_method}")
        
        return jsonify({
            "success": True,
            "message": "Withdrawal requested",
            "transaction": {
                "id": 1002,
                "type": "withdrawal",
                "amount": amount,
                "payment_method": payment_method,
                "status": "pending",
                "timestamp": datetime.now().isoformat()
            }
        })
        
    except Exception as e:
        logger.error(f"Withdrawal error: {e}")
        return jsonify({
            "success": False,
            "error": "Failed to process withdrawal"
        }), 500


@api_bp.route('/player/video-stream')
@get_api_auth_decorator()
def get_player_video_stream():
    """Get HLS video stream URL for player"""
    try:
        # Check if user is a player
        if request.current_user.get('role', '') != 'player':
            logger.warning(f"Video stream access denied to non-player user: {request.current_user.get('username', 'unknown')}")
            return jsonify({
                "success": False,
                "error": "Player access required"
            }), 403
        
        logger.info(f"Video stream requested by player: {request.current_user.get('username', 'unknown')}")
        
        return jsonify({
            "success": True,
            "video_stream": {
                "url": "/hls/stream.m3u8",
                "status": "live",
                "bitrate": "1080p",
                "latency": 2.5,
                "active_viewers": 125
            }
        })
        
    except Exception as e:
        logger.error(f"Get video stream error: {e}")
        return jsonify({
            "success": False,
            "error": "Failed to retrieve video stream"
        }), 500


@api_bp.route('/streaming/config')
def get_streaming_config():
    """Get streaming configuration for web player (public endpoint for player.js)
    
    Returns relative URL when no video_hostname is configured, so it works:
    - Behind a proxy: uses the proxy's URL
    - Direct connection: uses the current host
    """
    try:
        streaming_config = {}
        web_config = {}
        
        if main_bp.config_manager:
            streaming_config = main_bp.config_manager.get_section_config("streaming") or {}
            web_config = main_bp.config_manager.get_section_config("web") or {}
        
        # Build the video stream URL based on configuration
        video_hostname = streaming_config.get("video_hostname")
        video_port = streaming_config.get("video_port")
        video_use_ssl = streaming_config.get("video_use_ssl")
        
        # Determine HLS URL
        if video_hostname:
            # Use separate video hostname - build absolute URL
            use_ssl = video_use_ssl if video_use_ssl is not None else web_config.get("enable_ssl", False)
            protocol = "https" if use_ssl else "http"
            port = video_port if video_port is not None else web_config.get("port", 5001)
            base_url = f"{protocol}://{video_hostname}:{port}"
            hls_url = f"{base_url}/hls/stream.m3u8"
        else:
            # No separate video hostname - use relative URL
            # This works correctly both behind a proxy and with direct connections
            # The browser will resolve it relative to the current page URL
            base_url = None  # Indicates relative URLs should be used
            hls_url = "/hls/stream.m3u8"  # Relative URL - works with proxy and direct
        
        return jsonify({
            "success": True,
            "streaming": {
                "enabled": streaming_config.get("enabled", True),
                "port": streaming_config.get("port", 5884),
                "framerate": streaming_config.get("framerate", 25),
                "segment_duration": streaming_config.get("segment_duration", 2),
                "video_hostname": video_hostname,
                "video_port": video_port,
                "video_use_ssl": video_use_ssl,
                "base_url": base_url,
                "hls_url": hls_url,
                "use_relative_url": video_hostname is None  # Indicates relative URL usage
            }
        })
        
    except Exception as e:
        logger.error(f"Get streaming config error: {e}")
        return jsonify({
            "success": False,
            "error": "Failed to retrieve streaming configuration"
        }), 500


# ============================================
# Profile API Endpoints (for brokers and players)
# ============================================

@api_bp.route('/profile', methods=['GET'])
@get_api_auth_decorator()
def get_profile():
    """Get current user's profile"""
    try:
        user_id = request.current_user.get('user_id')
        
        session = api_bp.db_manager.get_session()
        try:
            from ..database.models import UserModel, BrokerSubscriptionModel, SubscriptionPlanModel
            
            user = session.query(UserModel).filter_by(id=user_id).first()
            
            if not user:
                return jsonify({"success": False, "error": "User not found"}), 404
            
            profile_data = {
                'id': user.id,
                'username': user.username,
                'email': user.email,
                'role': getattr(user, 'role', 'normal'),
                'display_name': getattr(user, 'display_name', None),
                'phone': getattr(user, 'phone', None),
                'avatar_url': getattr(user, 'avatar_url', None),
                'email_verified': getattr(user, 'email_verified', False),
                'created_at': user.created_at.isoformat() if user.created_at else None,
                'preferred_currency_id': getattr(user, 'preferred_currency_id', None)
            }
            
            # Get subscription info for brokers
            if user.role == 'broker':
                subscription = session.query(BrokerSubscriptionModel).filter_by(
                    user_id=user_id,
                    status='active'
                ).first()
                
                if subscription:
                    plan = session.query(SubscriptionPlanModel).filter_by(
                        id=subscription.plan_id
                    ).first()
                    
                    profile_data['subscription'] = {
                        'plan_name': plan.name if plan else 'Unknown',
                        'status': subscription.status,
                        'current_period_end': subscription.current_period_end.isoformat() if subscription.current_period_end else None
                    }
            
            return jsonify({"success": True, "profile": profile_data})
            
        finally:
            session.close()
            
    except Exception as e:
        logger.error(f"Get profile error: {e}")
        return jsonify({"success": False, "error": str(e)}), 500


@api_bp.route('/profile', methods=['PUT'])
@get_api_auth_decorator()
def update_profile():
    """Update current user's profile"""
    try:
        user_id = request.current_user.get('user_id')
        data = request.get_json() or {}
        
        session = api_bp.db_manager.get_session()
        try:
            from ..database.models import UserModel
            
            user = session.query(UserModel).filter_by(id=user_id).first()
            
            if not user:
                return jsonify({"success": False, "error": "User not found"}), 404
            
            # Update allowed fields
            if 'display_name' in data:
                user.display_name = data['display_name'][:100] if data['display_name'] else None
            if 'phone' in data:
                user.phone = data['phone'][:20] if data['phone'] else None
            
            session.commit()
            
            return jsonify({"success": True, "message": "Profile updated successfully"})
            
        finally:
            session.close()
            
    except Exception as e:
        logger.error(f"Update profile error: {e}")
        return jsonify({"success": False, "error": str(e)}), 500


@api_bp.route('/profile/password', methods=['PUT'])
@get_api_auth_decorator()
def change_profile_password():
    """Change current user's password"""
    try:
        user_id = request.current_user.get('user_id')
        data = request.get_json() or {}
        
        current_password = data.get('current_password', '')
        new_password = data.get('new_password', '')
        
        if not current_password or not new_password:
            return jsonify({"success": False, "error": "Current and new password are required"}), 400
        
        if len(new_password) < 8:
            return jsonify({"success": False, "error": "Password must be at least 8 characters"}), 400
        
        # Get auth manager
        auth_manager = getattr(api_bp, 'auth_manager', None)
        if not auth_manager:
            return jsonify({"success": False, "error": "Authentication system not available"}), 500
        
        # Change password
        success = auth_manager.change_password(user_id, current_password, new_password)
        
        if success:
            return jsonify({"success": True, "message": "Password changed successfully"})
        else:
            return jsonify({"success": False, "error": "Current password is incorrect"}), 400
            
    except Exception as e:
        logger.error(f"Change password error: {e}")
        return jsonify({"success": False, "error": str(e)}), 500


@api_bp.route('/profile/email', methods=['PUT'])
@get_api_auth_decorator()
def change_profile_email():
    """Request email change for current user"""
    try:
        user_id = request.current_user.get('user_id')
        data = request.get_json() or {}
        
        new_email = data.get('new_email', '').strip()
        password = data.get('password', '')
        
        if not new_email or not password:
            return jsonify({"success": False, "error": "New email and password are required"}), 400
        
        session = api_bp.db_manager.get_session()
        try:
            from ..database.models import UserModel
            from werkzeug.security import check_password_hash
            
            user = session.query(UserModel).filter_by(id=user_id).first()
            
            if not user:
                return jsonify({"success": False, "error": "User not found"}), 404
            
            # Verify password
            if not check_password_hash(user.password_hash, password):
                return jsonify({"success": False, "error": "Incorrect password"}), 400
            
            # Check if email is already in use
            existing = session.query(UserModel).filter_by(email=new_email).first()
            if existing and existing.id != user_id:
                return jsonify({"success": False, "error": "Email is already in use"}), 400
            
            # Store pending email change
            user.pending_email = new_email
            session.commit()
            
            # Send verification email to new address
            # TODO: Implement email sending
            
            return jsonify({"success": True, "message": "Verification email sent to new address"})
            
        finally:
            session.close()
            
    except Exception as e:
        logger.error(f"Change email error: {e}")
        return jsonify({"success": False, "error": str(e)}), 500


@api_bp.route('/profile/currency', methods=['PUT'])
@get_api_auth_decorator()
def set_preferred_currency():
    """Set user's preferred currency"""
    try:
        user_id = request.current_user.get('user_id')
        data = request.get_json() or {}
        
        currency_id = data.get('preferred_currency_id')
        
        if not currency_id:
            return jsonify({"success": False, "error": "Currency ID is required"}), 400
        
        session = api_bp.db_manager.get_session()
        try:
            from ..database.models import UserModel, CurrencyModel
            
            # Verify currency exists
            currency = session.query(CurrencyModel).filter_by(id=currency_id, is_active=True).first()
            if not currency:
                return jsonify({"success": False, "error": "Invalid currency"}), 400
            
            user = session.query(UserModel).filter_by(id=user_id).first()
            if user:
                user.preferred_currency_id = currency_id
                session.commit()
            
            return jsonify({"success": True, "message": "Currency preference saved"})
            
        finally:
            session.close()
            
    except Exception as e:
        logger.error(f"Set currency error: {e}")
        return jsonify({"success": False, "error": str(e)}), 500


@api_bp.route('/profile/avatar', methods=['POST'])
@get_api_auth_decorator()
def upload_profile_avatar():
    """Upload profile avatar"""
    try:
        user_id = request.current_user.get('user_id')
        
        if 'avatar' not in request.files:
            return jsonify({"success": False, "error": "No file provided"}), 400
        
        file = request.files['avatar']
        
        if file.filename == '':
            return jsonify({"success": False, "error": "No file selected"}), 400
        
        # Check file type
        allowed_extensions = {'png', 'jpg', 'jpeg', 'gif', 'webp'}
        filename = file.filename.lower()
        if not any(filename.endswith(ext) for ext in allowed_extensions):
            return jsonify({"success": False, "error": "Invalid file type. Allowed: PNG, JPG, GIF, WEBP"}), 400
        
        # Check file size (max 2MB)
        file.seek(0, 2)  # Seek to end
        size = file.tell()
        file.seek(0)  # Reset to beginning
        
        if size > 2 * 1024 * 1024:
            return jsonify({"success": False, "error": "File too large. Maximum size: 2MB"}), 400
        
        # Save file
        import os
        import uuid
        from werkzeug.utils import secure_filename
        
        # Create avatars directory if it doesn't exist
        avatars_dir = os.path.join('mbetterclient', 'web_dashboard', 'static', 'uploads', 'avatars')
        os.makedirs(avatars_dir, exist_ok=True)
        
        # Generate unique filename
        ext = filename.rsplit('.', 1)[1]
        avatar_filename = f"{user_id}_{uuid.uuid4().hex[:8]}.{ext}"
        avatar_path = os.path.join(avatars_dir, avatar_filename)
        
        file.save(avatar_path)
        
        # Update user record
        avatar_url = f"/static/uploads/avatars/{avatar_filename}"
        
        session = api_bp.db_manager.get_session()
        try:
            from ..database.models import UserModel
            
            user = session.query(UserModel).filter_by(id=user_id).first()
            if user:
                # Delete old avatar if exists
                if user.avatar_url:
                    old_path = os.path.join('mbetterclient', 'web_dashboard', 'static', 
                                           user.avatar_url.lstrip('/'))
                    if os.path.exists(old_path):
                        os.remove(old_path)
                
                user.avatar_url = avatar_url
                session.commit()
            
            return jsonify({"success": True, "avatar_url": avatar_url})
            
        finally:
            session.close()
            
    except Exception as e:
        logger.error(f"Upload avatar error: {e}")
        return jsonify({"success": False, "error": str(e)}), 500


@api_bp.route('/profile/delete', methods=['POST'])
@get_api_auth_decorator()
def delete_profile_account():
    """Delete current user's account"""
    try:
        user_id = request.current_user.get('user_id')
        data = request.get_json() or {}
        
        password = data.get('password', '')
        
        if not password:
            return jsonify({"success": False, "error": "Password is required"}), 400
        
        session = api_bp.db_manager.get_session()
        try:
            from ..database.models import UserModel, ApiKeyModel, BrokerSubscriptionModel
            from werkzeug.security import check_password_hash
            
            user = session.query(UserModel).filter_by(id=user_id).first()
            
            if not user:
                return jsonify({"success": False, "error": "User not found"}), 404
            
            # Verify password
            if not check_password_hash(user.password_hash, password):
                return jsonify({"success": False, "error": "Incorrect password"}), 400
            
            # Don't allow admin deletion through this endpoint
            if user.is_admin:
                return jsonify({"success": False, "error": "Admin accounts cannot be deleted through this endpoint"}), 403
            
            # Delete user's API keys
            session.query(ApiKeyModel).filter_by(user_id=user_id).delete()
            
            # Cancel user's subscriptions
            subscriptions = session.query(BrokerSubscriptionModel).filter_by(user_id=user_id).all()
            for sub in subscriptions:
                sub.status = 'cancelled'
            
            # Soft delete user (mark as inactive rather than hard delete)
            user.is_active = False
            user.deleted_at = datetime.utcnow()
            user.email = f"deleted_{user_id}_{user.email}"
            user.username = f"deleted_{user_id}_{user.username}"
            
            session.commit()
            
            # TODO: Send confirmation email
            
            return jsonify({"success": True, "message": "Account deleted successfully"})
            
        finally:
            session.close()
            
    except Exception as e:
        logger.error(f"Delete account error: {e}")
        return jsonify({"success": False, "error": str(e)}), 500


# ============================================================================
# Web Overlay API Endpoints
# These endpoints provide data for the web player overlay system
# Equivalent to the Qt WebChannel methods in OverlayWebChannel
# ============================================================================

@api_bp.route('/overlay/data', methods=['GET'])
@get_api_auth_decorator()
def get_overlay_data():
    """Get current overlay data for web player
    
    Equivalent to Qt WebChannel getCurrentData() method.
    Returns current overlay data including title, subtitle, match info, etc.
    
    Video Phase State Machine:
    - video_phase: Current phase ('idle', 'intro', 'match', 'result')
    - phase_start_time: Server timestamp when phase started
    - phase_video_duration: Expected duration of current video
    - hls_delay_compensation: Configurable delay for HLS synchronization
    """
    try:
        # Get main application from Flask g context
        main_app = g.get('main_app')
        
        overlay_data = {}
        
        # Try to get overlay data from headless player (RTSPStreamer)
        if main_app:
            headless_player = getattr(main_app, 'headless_player', None)
            if headless_player and hasattr(headless_player, 'overlay_data'):
                overlay_data = headless_player.overlay_data.copy() if headless_player.overlay_data else {}
                
                # Add match video state for template switching
                if hasattr(headless_player, 'is_playing_match_video'):
                    overlay_data['is_playing_match_video'] = headless_player.is_playing_match_video
                if hasattr(headless_player, 'current_match_video_filename'):
                    overlay_data['current_match_video_filename'] = headless_player.current_match_video_filename
                if hasattr(headless_player, 'current_match_id'):
                    overlay_data['current_match_id'] = headless_player.current_match_id
                
                # Add video phase state machine data for HLS synchronization
                if hasattr(headless_player, 'video_phase'):
                    overlay_data['video_phase'] = headless_player.video_phase
                if hasattr(headless_player, 'phase_start_time'):
                    overlay_data['phase_start_time'] = headless_player.phase_start_time
                if hasattr(headless_player, 'phase_video_duration'):
                    overlay_data['phase_video_duration'] = headless_player.phase_video_duration
                if hasattr(headless_player, 'hls_delay_compensation'):
                    overlay_data['hls_delay_compensation'] = headless_player.hls_delay_compensation
                if hasattr(headless_player, 'result_data'):
                    overlay_data['result_data'] = headless_player.result_data
                    
                    # If we have result_data, fetch match details from database for fighter names
                    if headless_player.result_data and headless_player.result_data.get('match_id'):
                        try:
                            from ..database.models import MatchModel
                            if not api_bp.db_manager:
                                logger.warning("Database manager not available for overlay data")
                                return jsonify(overlay_data)
                            session = api_bp.db_manager.get_session()
                            try:
                                match = session.query(MatchModel).filter_by(id=headless_player.result_data['match_id']).first()
                                if match:
                                    # Add match data with fighter names to result_data
                                    overlay_data['result_data']['match'] = {
                                        'id': match.id,
                                        'match_number': match.match_number,
                                        'fighter1_township': match.fighter1_township,
                                        'fighter2_township': match.fighter2_township,
                                        'venue_kampala_township': match.venue_kampala_township
                                    }
                                    # Also add under_over_result if available
                                    if match.under_over_result:
                                        overlay_data['result_data']['under_over_result'] = match.under_over_result
                                    
                                    # Get winning outcomes from ExtractionAssociationModel (like Qt player does)
                                    winning_outcome_names = []
                                    result = match.result or overlay_data['result_data'].get('result') or overlay_data['result_data'].get('outcome')
                                    
                                    if result:
                                        try:
                                            from ..database.models import ExtractionAssociationModel
                                            winning_outcomes_query = session.query(ExtractionAssociationModel.outcome_name).filter(
                                                ExtractionAssociationModel.extraction_result == result
                                            ).all()
                                            winning_outcome_names = [outcome.outcome_name for outcome in winning_outcomes_query]
                                            logger.debug(f"Found {len(winning_outcome_names)} winning outcomes from associations for result {result}: {winning_outcome_names}")
                                        except Exception as assoc_error:
                                            logger.warning(f"Failed to query ExtractionAssociationModel: {assoc_error}")
                                    
                                    # Include under_over_result if available
                                    if match.under_over_result and match.under_over_result not in winning_outcome_names:
                                        winning_outcome_names.append(match.under_over_result)
                                    
                                    # Convert to the format expected by the template
                                    if winning_outcome_names:
                                        overlay_data['result_data']['winningOutcomes'] = [{'outcome': o} for o in winning_outcome_names]
                                    elif match.winning_outcomes:
                                        # Fallback to match.winning_outcomes if no associations found
                                        import json
                                        try:
                                            winning_outcomes = json.loads(match.winning_outcomes) if isinstance(match.winning_outcomes, str) else match.winning_outcomes
                                            overlay_data['result_data']['winningOutcomes'] = [{'outcome': o} for o in winning_outcomes]
                                        except (json.JSONDecodeError, TypeError):
                                            pass
                            finally:
                                session.close()
                        except Exception as match_error:
                            logger.error(f"Failed to fetch match data for result: {match_error}")
                
                # For match phase, fetch match details from database for fighter names
                current_phase = overlay_data.get('video_phase', 'idle')
                current_match_id = overlay_data.get('current_match_id')
                if current_phase == 'match' and current_match_id:
                    try:
                        from ..database.models import MatchModel
                        if not api_bp.db_manager:
                            logger.warning("Database manager not available for match phase data")
                            return jsonify(overlay_data)
                        session = api_bp.db_manager.get_session()
                        try:
                            match = session.query(MatchModel).filter_by(id=current_match_id).first()
                            if match:
                                # Add match data with fighter names for match_video template
                                overlay_data['match'] = {
                                    'id': match.id,
                                    'match_number': match.match_number,
                                    'fighter1_township': match.fighter1_township,
                                    'fighter2_township': match.fighter2_township,
                                    'venue_kampala_township': match.venue_kampala_township
                                }
                        finally:
                            session.close()
                    except Exception as match_error:
                        logger.error(f"Failed to fetch match data for match phase: {match_error}")
        
        # Add some default data if not available
        if not overlay_data:
            overlay_data = {
                'title': 'Townships Combat League',
                'subtitle': 'Live Stream',
                'stream_status': 'live',
                'is_playing_match_video': False,
                'video_phase': 'idle',
                'phase_start_time': 0,
                'phase_video_duration': 0,
                'hls_delay_compensation': 5.0
            }
        
        return jsonify(overlay_data)
        
    except Exception as e:
        logger.error(f"Failed to get overlay data: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/overlay/fixtures', methods=['GET'])
@get_api_auth_decorator()
def get_overlay_fixtures():
    """Get fixture data for web player overlay
    
    Equivalent to Qt WebChannel getFixtureData() method.
    Returns upcoming and live matches for fixtures overlay.
    Uses the same timezone-aware date filtering as Qt player.
    """
    try:
        from ..database.models import MatchModel, MatchOutcomeModel
        from datetime import datetime, timedelta
        from ..utils.timezone_utils import get_today_venue_date, venue_to_utc_datetime
        
        session = api_bp.db_manager.get_session()
        
        try:
            # Get today's date in venue timezone for proper date handling (same as Qt player)
            today_venue = get_today_venue_date(api_bp.db_manager)
            yesterday_venue = today_venue - timedelta(days=1)
            
            # Convert venue dates to UTC datetime ranges for database queries
            today_start_utc = venue_to_utc_datetime(datetime.combine(today_venue, datetime.min.time()), api_bp.db_manager)
            today_end_utc = venue_to_utc_datetime(datetime.combine(today_venue, datetime.max.time()), api_bp.db_manager)
            yesterday_start_utc = venue_to_utc_datetime(datetime.combine(yesterday_venue, datetime.min.time()), api_bp.db_manager)
            yesterday_end_utc = venue_to_utc_datetime(datetime.combine(yesterday_venue, datetime.max.time()), api_bp.db_manager)
            
            logger.info(f"[FIXTURES API] today_venue: {today_venue}, yesterday_venue: {yesterday_venue}")
            logger.info(f"[FIXTURES API] today_start_utc: {today_start_utc}, today_end_utc: {today_end_utc}")
            logger.info(f"[FIXTURES API] yesterday_start_utc: {yesterday_start_utc}, yesterday_end_utc: {yesterday_end_utc}")
            
            # Convert timezone-aware datetime to timezone-naive for database comparison
            # Database stores start_time without timezone info
            today_start_naive = today_start_utc.replace(tzinfo=None) if today_start_utc.tzinfo else today_start_utc
            today_end_naive = today_end_utc.replace(tzinfo=None) if today_end_utc.tzinfo else today_end_utc
            yesterday_start_naive = yesterday_start_utc.replace(tzinfo=None) if yesterday_start_utc.tzinfo else yesterday_start_utc
            yesterday_end_naive = yesterday_end_utc.replace(tzinfo=None) if yesterday_end_utc.tzinfo else yesterday_end_utc
            
            logger.info(f"[FIXTURES API] today_start_naive: {today_start_naive}, today_end_naive: {today_end_naive}")
            
            # Get active matches for today (non-terminal states) - same as Qt player
            today_matches = session.query(MatchModel).filter(
                MatchModel.start_time.isnot(None),
                MatchModel.start_time >= today_start_naive,
                MatchModel.start_time < today_end_naive,
                MatchModel.status.notin_(['done', 'end', 'cancelled', 'failed', 'paused']),
                MatchModel.active_status == True
            ).order_by(MatchModel.start_time.asc()).all()
            
            logger.info(f"[FIXTURES API] Found {len(today_matches)} today matches")
            
            # Check if there are any incomplete matches from yesterday - same as Qt player
            yesterday_incomplete_matches = session.query(MatchModel).filter(
                MatchModel.start_time.isnot(None),
                MatchModel.start_time >= yesterday_start_naive,
                MatchModel.start_time < yesterday_end_naive,
                MatchModel.status.notin_(['done', 'end', 'cancelled', 'failed', 'paused']),
                MatchModel.active_status == True
            ).order_by(MatchModel.start_time.asc()).all()
            
            # Combine matches: yesterday first (if any incomplete), then today
            all_matches = []
            if yesterday_incomplete_matches:
                logger.info(f"[FIXTURES API] Found {len(yesterday_incomplete_matches)} incomplete matches from yesterday - including them")
                all_matches.extend(yesterday_incomplete_matches)
            
            all_matches.extend(today_matches)
            
            # Limit to 5 matches total
            display_matches = all_matches[:5]
            logger.info(f"[FIXTURES API] Processing {len(display_matches)} matches for display")
            
            fixtures_data = []
            for i, match in enumerate(display_matches):
                try:
                    logger.info(f"[FIXTURES API] Processing match {i+1}: id={match.id}, match_number={match.match_number}")
                    
                    # Get outcomes from MatchOutcomeModel
                    outcomes = session.query(MatchOutcomeModel).filter_by(match_id=match.id).all()
                    logger.info(f"[FIXTURES API] Found {len(outcomes)} outcomes for match {match.id}")
                    
                    # Format outcomes as array expected by template
                    outcomes_array = []
                    for outcome in outcomes:
                        outcomes_array.append({
                            'outcome_name': outcome.column_name,
                            'outcome_value': outcome.float_value
                        })
                    
                    # If no outcomes in database, create default ones from match odds
                    if not outcomes_array:
                        outcomes_array = [
                            {'outcome_name': 'WIN1', 'outcome_value': float(match.odds_fighter1) if match.odds_fighter1 else 1.0},
                            {'outcome_name': 'X', 'outcome_value': float(match.odds_draw) if match.odds_draw else 3.0},
                            {'outcome_name': 'WIN2', 'outcome_value': float(match.odds_fighter2) if match.odds_fighter2 else 1.0},
                            {'outcome_name': 'UNDER', 'outcome_value': 1.75},
                            {'outcome_name': 'OVER', 'outcome_value': 2.05}
                        ]
                    
                    match_data = {
                        'id': match.id,
                        'match_number': match.match_number,
                        'fixture_id': match.fixture_id,
                        'fighter1_township': match.fighter1_township,
                        'fighter2_township': match.fighter2_township,
                        'venue_kampala_township': match.venue_kampala_township,
                        'status': match.status,
                        'start_time': match.start_time.isoformat() if match.start_time else None,
                        'result': match.result,
                        'outcomes': outcomes_array  # Array format expected by template
                    }
                    fixtures_data.append(match_data)
                    logger.info(f"[FIXTURES API] Successfully processed match {match.id}")
                except Exception as match_error:
                    logger.error(f"[FIXTURES API] Error processing match {match.id}: {match_error}")
                    continue
            
            logger.info(f"[FIXTURES API] Returning {len(fixtures_data)} matches")
            return jsonify(fixtures_data)
            
        finally:
            session.close()
            
    except Exception as e:
        logger.error(f"Failed to get overlay fixtures: {e}")
        return jsonify([])


@api_bp.route('/overlay/timer', methods=['GET'])
@get_api_auth_decorator()
def get_overlay_timer():
    """Get timer state for web player overlay
    
    Equivalent to Qt WebChannel getTimerState() method.
    Returns countdown timer state for match start.
    """
    try:
        # Get main application from Flask g context
        main_app = g.get('main_app')
        
        timer_state = {"running": False, "remaining_seconds": 0}
        
        # Try to get timer state from web_dashboard (where it's actually stored)
        if main_app:
            web_dashboard = getattr(main_app, 'web_dashboard', None)
            if web_dashboard and hasattr(web_dashboard, 'current_timer_state'):
                timer_state = web_dashboard.current_timer_state.copy()
                logger.debug(f"[OVERLAY TIMER] Returning timer state: {timer_state}")
        
        return jsonify(timer_state)
        
    except Exception as e:
        logger.error(f"Failed to get overlay timer: {e}")
        return jsonify({"running": False, "remaining_seconds": 0})


@api_bp.route('/overlay/license', methods=['GET'])
@get_api_auth_decorator()
def get_overlay_license():
    """Get license text for web player overlay
    
    Equivalent to Qt WebChannel getLicenseText() method.
    Returns license text from database configuration.
    """
    try:
        # Get license text from database configuration
        license_text = api_bp.db_manager.get_config_value('license_text', 'License text not configured')
        
        return jsonify({"license_text": license_text})
        
    except Exception as e:
        logger.error(f"Failed to get overlay license: {e}")
        return jsonify({"license_text": "License text not available"})


@api_bp.route('/overlay/completed-matches', methods=['GET'])
@get_api_auth_decorator()
def get_overlay_completed_matches():
    """Get last 3 completed matches for web player overlay
    
    Equivalent to Qt WebChannel getCompletedMatches() method.
    Returns last 3 completed matches for results overlay.
    """
    try:
        from ..database.models import MatchModel
        
        session = api_bp.db_manager.get_session()
        
        try:
            # Get last 3 completed matches
            completed_matches = session.query(MatchModel).filter(
                MatchModel.status == 'done',
                MatchModel.result.isnot(None),
                MatchModel.end_time.isnot(None),
                MatchModel.active_status == True
            ).order_by(MatchModel.end_time.desc()).limit(3).all()
            
            matches_data = []
            for match in completed_matches:
                # Parse winning_outcomes JSON if available
                winning_outcomes = []
                if match.winning_outcomes:
                    try:
                        winning_outcomes = json.loads(match.winning_outcomes) if isinstance(match.winning_outcomes, str) else match.winning_outcomes
                    except (json.JSONDecodeError, TypeError):
                        winning_outcomes = []
                
                # Determine the main result to display
                # If result is UNDER/OVER, use the first non-UNDER/OVER winning outcome as the main result
                display_result = match.result
                if match.result in ['UNDER', 'OVER'] and winning_outcomes:
                    for outcome in winning_outcomes:
                        if outcome not in ['UNDER', 'OVER']:
                            display_result = outcome
                            break
                
                match_data = {
                    'id': match.id,
                    'match_number': match.match_number,
                    'fighter1_township': match.fighter1_township,
                    'fighter2_township': match.fighter2_township,
                    'venue_kampala_township': match.venue_kampala_township,
                    'result': match.result,
                    'display_result': display_result,  # The result to display (actual fight winner)
                    'under_over_result': match.under_over_result,
                    'winning_outcomes': winning_outcomes,
                    'end_time': match.end_time.isoformat() if match.end_time else None
                }
                matches_data.append(match_data)
            
            return jsonify(matches_data)
            
        finally:
            session.close()
            
    except Exception as e:
        logger.error(f"Failed to get completed matches: {e}")
        return jsonify([])


@api_bp.route('/overlay/winning-outcomes/<int:match_id>', methods=['GET'])
@get_api_auth_decorator()
def get_overlay_winning_outcomes(match_id):
    """Get winning outcomes for a match
    
    Equivalent to Qt WebChannel getWinningOutcomes() method.
    Returns winning outcomes for a completed match.
    """
    try:
        from ..database.models import MatchModel
        
        session = api_bp.db_manager.get_session()
        
        try:
            match = session.query(MatchModel).filter_by(id=match_id).first()
            
            if not match:
                return jsonify([])
            
            winning_outcomes = []
            
            # Get winning outcomes from match
            if match.winning_outcomes:
                import json
                if isinstance(match.winning_outcomes, str):
                    winning_outcomes = json.loads(match.winning_outcomes)
                elif isinstance(match.winning_outcomes, list):
                    winning_outcomes = match.winning_outcomes
            
            # Add result if available
            if match.result and match.result not in winning_outcomes:
                winning_outcomes.append(match.result)
            
            # Add under/over result if available
            if match.under_over_result and match.under_over_result not in winning_outcomes:
                winning_outcomes.append(match.under_over_result)
            
            return jsonify(winning_outcomes)
            
        finally:
            session.close()
            
    except Exception as e:
        logger.error(f"Failed to get winning outcomes for match {match_id}: {e}")
        return jsonify([])


@api_bp.route('/overlay/current-match', methods=['GET'])
@get_api_auth_decorator()
def get_overlay_current_match():
    """Get current live match info for web player overlay
    
    Returns information about the currently live match.
    """
    try:
        from ..database.models import MatchModel
        
        session = api_bp.db_manager.get_session()
        
        try:
            # Get current live match
            live_match = session.query(MatchModel).filter_by(status='live').first()
            
            if not live_match:
                return jsonify({"match": None, "message": "No live match"})
            
            match_data = {
                'id': live_match.id,
                'match_number': live_match.match_number,
                'fixture_id': live_match.fixture_id,
                'fighter1_township': live_match.fighter1_township,
                'fighter2_township': live_match.fighter2_township,
                'venue_kampala_township': live_match.venue_kampala_township,
                'status': live_match.status,
                'start_time': live_match.start_time.isoformat() if live_match.start_time else None,
                'result': live_match.result,
                'odds': {
                    'fighter1': float(live_match.odds_fighter1) if live_match.odds_fighter1 else 1.0,
                    'fighter2': float(live_match.odds_fighter2) if live_match.odds_fighter2 else 1.0,
                    'draw': float(live_match.odds_draw) if live_match.odds_draw else 3.0
                }
            }
            
            return jsonify({"match": match_data})
            
        finally:
            session.close()
            
    except Exception as e:
        logger.error(f"Failed to get current match: {e}")
        return jsonify({"match": None, "error": str(e)})


@api_bp.route('/overlay/template-config', methods=['GET'])
@get_api_auth_decorator()
def get_overlay_template_config():
    """Get template rotation configuration for web player overlay
    
    Equivalent to Qt WebChannel intro_templates_config.
    Returns template sequence and rotation timing.
    """
    try:
        from ..database.models import GameConfigModel
        import json
        
        session = api_bp.db_manager.get_session()
        
        try:
            # Get intro templates configuration from database
            intro_config = session.query(GameConfigModel).filter_by(
                config_key='intro_templates_config'
            ).first()
            
            if intro_config:
                try:
                    config = json.loads(intro_config.config_value)
                    templates = config.get('templates', [])
                    rotating_time = config.get('rotating_time', '00:15')
                    
                    logger.info(f"Loaded template config: {len(templates)} templates, rotating every {rotating_time}")
                    
                    return jsonify({
                        'templates': templates,
                        'rotating_time': rotating_time,
                        'default_show_time': config.get('default_show_time', '00:30')
                    })
                except (json.JSONDecodeError, TypeError) as e:
                    logger.warning(f"Invalid JSON in intro templates config: {e}")
            
            # Return default configuration
            return jsonify({
                'templates': [
                    {'name': 'fixtures', 'show_time': '00:15'},
                    {'name': 'match', 'show_time': '00:15'},
                    {'name': 'results', 'show_time': '00:15'}
                ],
                'rotating_time': '00:15',
                'default_show_time': '00:30'
            })
            
        finally:
            session.close()
            
    except Exception as e:
        logger.error(f"Failed to get template config: {e}")
        return jsonify({
            'templates': [],
            'rotating_time': '00:15',
            'default_show_time': '00:30',
            'error': str(e)
        })


# ==================== Wallet API Routes ====================

@api_bp.route('/wallet', methods=['GET'])
@get_api_auth_decorator()
def api_get_wallet():
    """Get wallet for authenticated user"""
    from .api import get_wallet
    return get_wallet()


@api_bp.route('/wallet/transactions', methods=['GET'])
@get_api_auth_decorator()
def api_get_wallet_transactions():
    """Get transaction history for authenticated user's wallet"""
    from .api import get_wallet_transactions
    return get_wallet_transactions()


@api_bp.route('/wallet/deposit', methods=['POST'])
@get_api_auth_decorator()
def api_deposit_to_wallet():
    """Deposit funds to authenticated user's wallet"""
    from .api import deposit_to_wallet
    return deposit_to_wallet()


@api_bp.route('/wallet/withdraw', methods=['POST'])
@get_api_auth_decorator()
def api_withdraw_from_wallet():
    """Withdraw funds from authenticated user's wallet"""
    from .api import withdraw_from_wallet
    return withdraw_from_wallet()


# ==================== Profile API Routes ====================

@api_bp.route('/profile', methods=['GET'])
@get_api_auth_decorator()
def api_get_profile():
    """Get profile for authenticated user"""
    from .api import get_profile
    return get_profile()


@api_bp.route('/profile/password', methods=['POST'])
@get_api_auth_decorator()
def api_change_password():
    """Change password for authenticated user"""
    from .api import change_password
    return change_password()


@api_bp.route('/profile/email', methods=['POST'])
@get_api_auth_decorator()
def api_request_email_change():
    """Request email change for authenticated user"""
    from .api import request_email_change
    return request_email_change()


@api_bp.route('/profile/email/verify', methods=['POST'])
@get_api_auth_decorator()
def api_verify_email_change():
    """Verify email change for authenticated user"""
    from .api import verify_email_change
    return verify_email_change()


@api_bp.route('/profile/avatar', methods=['POST'])
@get_api_auth_decorator()
def api_update_avatar():
    """Update avatar for authenticated user"""
    from .api import update_avatar
    return update_avatar()


# Web templates route - serves overlay templates for web player
@main_bp.route('/web-templates/<template_name>')
def serve_web_template(template_name):
    """Serve web-compatible overlay templates
    
    These templates are adapted from Qt templates to work in web browsers.
    They use postMessage API instead of Qt WebChannel.
    """
    import os
    from flask import send_from_directory
    
    try:
        # Security: Only allow alphanumeric template names
        if not template_name.replace('-', '').replace('_', '').isalnum():
            return jsonify({"error": "Invalid template name"}), 400
        
        # Ensure .html extension
        if not template_name.endswith('.html'):
            template_name += '.html'
        
        # First check for web-specific templates
        web_templates_dir = os.path.join(os.path.dirname(__file__), '..', '..', 'web', 'templates')
        web_template_path = os.path.join(web_templates_dir, template_name)
        
        if os.path.exists(web_template_path):
            return send_from_directory(web_templates_dir, template_name)
        
        # Fall back to Qt templates (they will be adapted via JavaScript)
        qt_templates_dir = os.path.join(os.path.dirname(__file__), '..', 'qt_player', 'templates')
        qt_template_path = os.path.join(qt_templates_dir, template_name)
        
        if os.path.exists(qt_template_path):
            return send_from_directory(qt_templates_dir, template_name)
        
        # Template not found
        return jsonify({"error": f"Template not found: {template_name}"}), 404
        
    except Exception as e:
        logger.error(f"Failed to serve web template: {e}")
        return jsonify({"error": str(e)}), 500
