"""
Flask routes for web dashboard
"""

import logging
import time
import json
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
    if detail.result in ['won', 'win']:
        return True

    # Check if match has extraction data
    if match:
        # 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 under_over_result
        if match.under_over_result and detail.outcome == match.under_over_result:
            return True

        # 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 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')
                        }

                        # 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


# 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_bet_detail_winning(detail, match, session):
                    results['won'] += 1
                    results['winnings'] += float(detail.amount) * float(odds)  # Use actual odds
                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 {}

                # 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

                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
                })
        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
            }
        
        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('/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/<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_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


# 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('/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')
        
        # Validate role if provided
        if role and role not in ['admin', 'normal', 'cashier']:
            return jsonify({"error": "Invalid role. Must be admin, normal, or cashier"}), 400
        
        result = api_bp.api.update_user(user_id, username, email, password, is_admin, role)
        return jsonify(result)
        
    except Exception as e:
        logger.error(f"API update user 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

        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 = []
            today = date.today()

            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
                fixture_status = calculate_fixture_status(fixture_matches, today)

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

                # 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]
                }

                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):
    """
    Calculate fixture status based on match statuses and timing.

    Args:
        matches: List of MatchModel objects for this fixture
        today: Today's date

    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 and at least one match is pending
    first_match = matches[0]
    if (first_match.start_time and
        first_match.start_time.date() == today and
        any(status == 'pending' for status in match_statuses)):
        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 pending matches from the correct fixture for cashier dashboard"""
    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

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

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

            # Find fixtures with start_time of today (get the LAST one, not first)
            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.desc())\
                .first()

            selected_fixture_id = None

            if fixtures_with_today_start:
                # Use the LAST fixture where matches start today
                selected_fixture_id = fixtures_with_today_start.fixture_id
                logger.info(f"Selected fixture {selected_fixture_id} - last fixture with matches starting today")
            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
                })

            # 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
            })

        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, and ZIP files"""
    try:
        from ..database.models import MatchModel, MatchOutcomeModel
        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()
            
            # Clear all match outcomes first (due to foreign key constraints)
            session.query(MatchOutcomeModel).delete()
            session.commit()
            
            # Clear all matches
            session.query(MatchModel).delete()
            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, {zip_files_removed} ZIP files")
            
            return jsonify({
                "success": True,
                "message": "Fixtures data reset successfully",
                "removed": {
                    "matches": matches_count,
                    "outcomes": outcomes_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('/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


# Currency Settings API routes
@api_bp.route('/currency-settings')
@get_api_auth_decorator()
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
        else:
            symbol = 'USh'
            position = 'before'

        return jsonify({
            "success": True,
            "settings": {
                "symbol": symbol,
                "position": position
            }
        })
    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')

        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:
            # Save currency configuration
            result = api_bp.config_manager.update_section("currency", {
                "symbol": symbol,
                "position": position
            })

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

                return jsonify({
                    "success": True,
                    "message": f"Currency settings updated: {symbol} ({position} amount)",
                    "settings": {
                        "symbol": symbol,
                        "position": position
                    }
                })
            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
                bet_details = session.query(BetDetailModel).filter_by(bet_id=bet.uuid).all()
                bet_data['details'] = [detail.to_dict() for detail in bet_details]

                # 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_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

            # 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'],
                    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()
                
                # 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_bet_detail_winning(detail, session.query(MatchModel).filter_by(id=detail.match_id).first(), session):
                    results['won'] += 1
                    # Get odds for this outcome
                    odds = 0.0
                    match = session.query(MatchModel).filter_by(id=detail.match_id).first()
                    if match:
                        outcomes_dict = match.get_outcomes_dict()
                        odds = outcomes_dict.get(detail.outcome, 0.0)
                    results['winnings'] += float(detail.amount) * float(odds)
                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

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

            # Get matches in 'bet' status from today (or all if none today)
            matches_query = session.query(MatchModel).filter(
                MatchModel.status == 'bet'
            ).order_by(MatchModel.match_number.asc())

            # Try to filter by today first using venue timezone
            venue_tz = get_venue_timezone(api_bp.db_manager)
            local_start = datetime.combine(today, datetime.min.time())
            local_end = datetime.combine(today, 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)

            today_matches = matches_query.filter(
                MatchModel.start_time >= start_datetime,
                MatchModel.start_time < end_datetime
            ).all()

            if today_matches:
                matches = today_matches
            else:
                # Fallback to all matches in bet status
                matches = matches_query.all()

            matches_data = []
            for match in matches:
                match_data = match.to_dict()
                
                # 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'}
                    ]
                
                match_data['outcomes'] = betting_outcomes
                match_data['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()

                # 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)

                # 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_bet_detail_winning(detail, session.query(MatchModel).filter_by(id=detail.match_id).first(), session):
                    results['won'] += 1
                    # Get odds for this outcome
                    odds = 0.0
                    match = session.query(MatchModel).filter_by(id=detail.match_id).first()
                    if match:
                        outcomes_dict = match.get_outcomes_dict()
                        odds = outcomes_dict.get(detail.outcome, 0.0)
                    results['winnings'] += float(detail.amount) * float(odds)
                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()

        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 daily summary for local date {date_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 extraction stats for the day
            extraction_stats = session.query(ExtractionStatsModel).filter(
                ExtractionStatsModel.match_datetime >= start_datetime,
                ExtractionStatsModel.match_datetime <= end_datetime
            ).all()

            total_payout = sum(float(stat.total_redistributed) for stat in extraction_stats)
            total_matches = len(extraction_stats)

            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()

        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}: 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).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_stats[match_id]['bets_count'] += 1
                match_stats[match_id]['payin'] += float(detail.amount)

                # Calculate potential payout if bet was won (use same logic as bet list)
                match = session.query(MatchModel).filter_by(id=match_id).first()
                if is_bet_detail_winning(detail, match, session):
                    if match:
                        outcomes_dict = match.get_outcomes_dict()
                        odds = outcomes_dict.get(detail.outcome, 0.0)
                        match_stats[match_id]['payout'] += float(detail.amount) * float(odds)

            # 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).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)

                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': detail.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()

        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}: 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)

            extraction_stats = session.query(ExtractionStatsModel).filter(
                ExtractionStatsModel.match_datetime >= start_datetime,
                ExtractionStatsModel.match_datetime <= end_datetime
            ).all()
            total_payout = sum(float(stat.total_redistributed) for stat in extraction_stats)

            # 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(extraction_stats)))
            ]

            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).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_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

