"""
Flask routes for web dashboard
"""

import logging
import time
from datetime import datetime
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 werkzeug.security import check_password_hash
from werkzeug.utils import secure_filename

from .auth import AuthenticatedUser
from ..core.message_bus import Message, MessageType

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


@main_bp.route('/video')
@login_required
def video_control_page():
    """Video control page"""
    try:
        return render_template('dashboard/video.html',
                             user=current_user,
                             page_title="Video Control")
    except Exception as e:
        logger.error(f"Video control page error: {e}")
        flash("Error loading video control", "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 {}

                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/updates'),
                    'api_token': api_config.get('api_token', ''),
                    'api_interval': api_config.get('api_interval', 1800),
                    '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)
                })
        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/updates',
                'api_token': '',
                'api_interval': 1800,
                '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
            }
        
        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:
        # Restrict cashier users from accessing fixtures 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/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:
        # Restrict cashier users from accessing fixture details 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/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:
        # Restrict cashier users from accessing match details 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/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


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


# API routes
@api_bp.route('/status')
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('/video/status')
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
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'])
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
def video_control():
    """Control video player"""
    try:
        data = request.get_json() or {}
        action = data.get('action')
        
        if not action:
            return jsonify({"error": "Action is required"}), 400
        
        # Remove action from data to avoid duplicate argument error
        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'])
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
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')
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
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')
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
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>')
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
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'])
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
@api_bp.auth_manager.require_admin if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
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'])
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
@api_bp.auth_manager.require_admin if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
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')
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
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', 20)  # Default 20 minutes
        else:
            match_interval = 20  # 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'])
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
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/test-connection', methods=['POST'])
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
@api_bp.auth_manager.require_admin if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
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')
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
@api_bp.auth_manager.require_admin if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
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'])
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
@api_bp.auth_manager.require_admin if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
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'])
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
@api_bp.auth_manager.require_admin if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
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'])
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
@api_bp.auth_manager.require_admin if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
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')
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
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'])
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
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'])
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
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')
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
@api_bp.auth_manager.require_admin if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
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'])
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
@api_bp.auth_manager.require_admin if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
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'])
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
def upload_video():
    """Upload video file"""
    try:
        if 'video' not in request.files:
            return jsonify({"error": "No video file provided"}), 400
        
        file = request.files['video']
        if file.filename == '':
            return jsonify({"error": "No file selected"}), 400
        
        template = request.form.get('template', 'news_template')
        
        result = api_bp.api.upload_video(file, template)
        return jsonify(result)
        
    except Exception as e:
        logger.error(f"API video upload error: {e}")
        return jsonify({"error": str(e)}), 500


@api_bp.route('/video/delete', methods=['POST'])
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
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'])
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
@api_bp.auth_manager.require_admin if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
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'])
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
@api_bp.auth_manager.require_admin if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
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'])
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
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'])
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
@api_bp.auth_manager.require_admin if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
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('/fixtures')
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
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')
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
def get_cashier_pending_matches():
    """Get pending matches from the first fixture for cashier dashboard"""
    try:
        from ..database.models import MatchModel
        from datetime import datetime, date

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

            # First, try to find fixtures where the first match start_time is 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:
                # Use fixture where first match starts today
                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
                    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} - all matches are pending")
                        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": True,
                    "matches": [],
                    "total": 0,
                    "fixture_id": None
                })

            # Get all matches from the selected fixture (not just pending ones)
            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


@api_bp.route('/cashier/start-games', methods=['POST'])
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
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>')
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
def get_fixture_details(fixture_id):
    """Get all matches in a fixture by fixture_id"""
    try:
        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'])
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
@api_bp.auth_manager.require_admin if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
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')
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
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", 1800),
                    "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'])
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
@api_bp.auth_manager.require_admin if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
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')
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
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', 20)  # Default 20 minutes
        else:
            match_interval = 20  # 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'])
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
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
                    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} - all matches are pending")
                        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')
def get_server_time():
    """Get current server time"""
    try:
        from datetime import datetime
        server_time = datetime.now()

        return jsonify({
            "success": True,
            "server_time": server_time.isoformat(),
            "timestamp": int(server_time.timestamp() * 1000)  # milliseconds since epoch
        })

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


@api_bp.route('/notifications')
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
def notifications():
    """Long polling endpoint for real-time notifications"""
    try:
        import time
        import threading
        from ..core.message_bus import MessageType

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

        # Create a queue for this client
        notification_queue = []
        notification_received = threading.Event()

        def message_handler(message):
            """Handle incoming messages for this client"""
            if message.type in [MessageType.START_GAME, MessageType.MATCH_START, MessageType.GAME_STATUS]:
                notification_data = {
                    "type": message.type.value,
                    "data": message.data,
                    "timestamp": message.timestamp,
                    "sender": message.sender
                }
                notification_queue.append(notification_data)
                notification_received.set()

        # Subscribe to relevant message types
        if api_bp.message_bus:
            api_bp.message_bus.subscribe_global(MessageType.START_GAME, message_handler)
            api_bp.message_bus.subscribe_global(MessageType.MATCH_START, message_handler)
            api_bp.message_bus.subscribe_global(MessageType.GAME_STATUS, message_handler)

        # Wait for notification or timeout
        notification_received.wait(timeout=timeout)

        # Unsubscribe from messages
        if api_bp.message_bus:
            api_bp.message_bus._global_handlers[MessageType.START_GAME].remove(message_handler)
            api_bp.message_bus._global_handlers[MessageType.MATCH_START].remove(message_handler)
            api_bp.message_bus._global_handlers[MessageType.GAME_STATUS].remove(message_handler)

        if notification_queue:
            # Return the first notification received
            notification = notification_queue[0]
            logger.info(f"Notification sent to client: {notification['type']}")
            return jsonify({
                "success": True,
                "notification": notification
            })
        else:
            # Timeout - return empty response
            return jsonify({
                "success": True,
                "notification": None
            })

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


# Extraction API routes
@api_bp.route('/extraction/outcomes')
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
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:
            session.close()

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


@api_bp.route('/extraction/associations')
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
def get_extraction_associations():
    """Get current extraction associations"""
    try:
        from ..database.models import ExtractionAssociationModel

        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'])
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
def save_extraction_associations():
    """Save extraction associations"""
    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

        session = api_bp.db_manager.get_session()
        try:
            # Clear existing associations
            session.query(ExtractionAssociationModel).delete()

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

            session.commit()

            logger.info(f"Saved {len(associations_data)} extraction 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/config')
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
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'])
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
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')
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
def get_betting_mode():
    """Get current user's betting mode"""
    try:
        from ..database.models import BettingModeModel

        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

        session = api_bp.db_manager.get_session()
        try:
            # Get user's betting mode
            betting_mode = session.query(BettingModeModel).filter_by(user_id=user_id).first()

            if betting_mode:
                mode = betting_mode.mode
            else:
                # Return default if not set
                mode = 'all_bets_on_start'

            return jsonify({
                "success": True,
                "betting_mode": mode,
                "is_default": betting_mode is None
            })

        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'])
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
def set_betting_mode():
    """Set current user's betting mode"""
    try:
        from ..database.models import BettingModeModel

        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

        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 already exists for user
            existing_mode = session.query(BettingModeModel).filter_by(user_id=user_id).first()

            if existing_mode:
                # Update existing mode
                existing_mode.mode = mode
                existing_mode.updated_at = datetime.utcnow()
            else:
                # Create new betting mode
                new_mode = BettingModeModel(
                    user_id=user_id,
                    mode=mode
                )
                session.add(new_mode)

            session.commit()

            logger.info(f"Updated betting mode for user {user_id}: {mode}")

            return jsonify({
                "success": True,
                "message": f"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


# Server-side Match Timer API endpoints
@api_bp.route('/match-timer/state')
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
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'])
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
@api_bp.auth_manager.require_admin if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
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