"""
Flask web dashboard application for MbetterClient
"""

import time
import logging
from pathlib import Path
from typing import Optional, Dict, Any
from flask import Flask, request, jsonify, render_template, redirect, url_for, session, g
from flask_login import LoginManager, login_required, current_user
from flask_jwt_extended import JWTManager, create_access_token, jwt_required as flask_jwt_required
from werkzeug.serving import make_server
import threading

from ..core.thread_manager import ThreadedComponent
from ..core.message_bus import MessageBus, Message, MessageType, MessageBuilder
from ..config.settings import WebConfig
from ..config.manager import ConfigManager
from ..database.manager import DatabaseManager
from ..utils.ssl_utils import get_ssl_certificate_paths, create_ssl_context
from .auth import AuthManager
from .api import DashboardAPI
from .routes import main_bp, auth_bp, api_bp
from .screen_cast_routes import screen_cast_bp

logger = logging.getLogger(__name__)


class WebDashboard(ThreadedComponent):
    """Flask web dashboard component"""
    
    def __init__(self, message_bus: MessageBus, db_manager: DatabaseManager,
                 config_manager: ConfigManager, settings: WebConfig):
        super().__init__("web_dashboard", message_bus)
        self.db_manager = db_manager
        self.config_manager = config_manager
        self.settings = settings
        
        # Flask app and server
        self.app: Optional[Flask] = None
        self.server: Optional = None
        self.ssl_context: Optional = None  # SSL context for HTTPS
        self.auth_manager: Optional[AuthManager] = None
        self.api: Optional[DashboardAPI] = None
        self.main_application = None  # Will be set by the main application

        # Timer state storage
        self.current_timer_state: Dict[str, Any] = {
            "running": False,
            "remaining_seconds": 0,
            "total_seconds": 0,
            "fixture_id": None,
            "match_id": None,
            "start_time": None
        }
        
        # Register message queue
        self.message_queue = self.message_bus.register_component(self.name)
        
        logger.info("WebDashboard initialized")
    
    def set_main_application(self, app):
        """Set reference to main application for component access"""
        self.main_application = app
    
    def initialize(self) -> bool:
        """Initialize Flask application"""
        try:
            # Create Flask app
            self.app = self._create_flask_app()
            
            # Initialize auth manager
            self.auth_manager = AuthManager(self.db_manager, self.app)
            
            # Initialize API
            self.api = DashboardAPI(self.db_manager, self.config_manager, self.message_bus)
            
            # Setup routes
            self._setup_routes()
            
            # Create HTTP server
            self._create_server()
            
            # Subscribe to messages
            self.message_bus.subscribe(self.name, MessageType.CONFIG_UPDATE, self._handle_config_update)
            self.message_bus.subscribe(self.name, MessageType.SYSTEM_STATUS, self._handle_system_status)
            self.message_bus.subscribe(self.name, MessageType.CUSTOM, self._handle_custom_message)
            
            logger.info("WebDashboard initialized successfully")
            return True
            
        except Exception as e:
            logger.error(f"WebDashboard initialization failed: {e}")
            return False
    
    def _create_flask_app(self) -> Flask:
        """Create and configure Flask application"""
        # Template and static directories
        template_dir = Path(__file__).parent / 'templates'
        static_dir = Path(__file__).parent / 'static'
        
        # Create Flask app
        app = Flask(__name__,
                   template_folder=str(template_dir),
                   static_folder=str(static_dir))
        
        # Configuration
        app.config.update({
            'SECRET_KEY': self.settings.secret_key,
            'JWT_SECRET_KEY': self.settings.jwt_secret_key,
            'JWT_ACCESS_TOKEN_EXPIRES': self.settings.jwt_expiration_hours * 3600,
            'SESSION_COOKIE_HTTPONLY': True,
            'SESSION_COOKIE_SECURE': self.settings.enable_ssl,  # True when using HTTPS
            'PERMANENT_SESSION_LIFETIME': self.settings.session_timeout_hours * 3600,
            'WTF_CSRF_ENABLED': True,
            'WTF_CSRF_TIME_LIMIT': None,
        })
        
        # Initialize extensions
        login_manager = LoginManager()
        login_manager.init_app(app)
        login_manager.login_view = 'auth.login'
        login_manager.login_message = 'Please log in to access this page.'
        
        jwt_manager = JWTManager(app)
        
        # User loader for Flask-Login
        @login_manager.user_loader
        def load_user(user_id):
            from .auth import AuthenticatedUser
            user_model = self.db_manager.get_user_by_id(int(user_id))
            if user_model:
                return AuthenticatedUser(
                    user_id=user_model.id,
                    username=user_model.username,
                    email=user_model.email,
                    is_admin=user_model.is_admin,
                    role=getattr(user_model, 'role', 'normal')
                )
            return None
        
        # JWT error handlers
        @jwt_manager.expired_token_loader
        def expired_token_callback(jwt_header, jwt_payload):
            return jsonify({'error': 'Token has expired'}), 401
        
        @jwt_manager.invalid_token_loader
        def invalid_token_callback(error):
            return jsonify({'error': 'Invalid token'}), 401
        
        @jwt_manager.unauthorized_loader
        def unauthorized_callback(error):
            return jsonify({'error': 'Authorization required'}), 401
        
        # Request context setup
        @app.before_request
        def before_request():
            g.db_manager = self.db_manager
            g.config_manager = self.config_manager
            g.message_bus = self.message_bus
            g.auth_manager = self.auth_manager
            g.api = self.api
            g.main_app = self.main_application  # Provide access to main app
        
        # Template context processors
        @app.context_processor
        def inject_globals():
            return {
                'app_name': 'MbetterClient',
                'app_version': '1.0.0',
                'current_time': time.time(),
            }
        
        # Error handlers
        @app.errorhandler(404)
        def not_found_error(error):
            return render_template('errors/404.html'), 404
        
        @app.errorhandler(500)
        def internal_error(error):
            return render_template('errors/500.html'), 500
        
        @app.errorhandler(403)
        def forbidden_error(error):
            return render_template('errors/403.html'), 403
        
        return app
    
    def _setup_routes(self):
        """Setup Flask routes"""
        # Pass dependencies to route modules
        main_bp.db_manager = self.db_manager
        main_bp.config_manager = self.config_manager
        main_bp.message_bus = self.message_bus
        
        auth_bp.auth_manager = self.auth_manager
        auth_bp.db_manager = self.db_manager
        
        api_bp.api = self.api
        api_bp.auth_manager = self.auth_manager
        api_bp.db_manager = self.db_manager
        api_bp.config_manager = self.config_manager
        api_bp.message_bus = self.message_bus
        
        # Pass dependencies to screen cast blueprint
        screen_cast_bp.message_bus = self.message_bus
        screen_cast_bp.db_manager = self.db_manager
        
        # Register blueprints
        self.app.register_blueprint(main_bp)
        self.app.register_blueprint(auth_bp, url_prefix='/auth')
        self.app.register_blueprint(api_bp, url_prefix='/api')
        self.app.register_blueprint(screen_cast_bp, url_prefix='/screen_cast')
    
    def _create_server(self):
        """Create HTTP/HTTPS server"""
        try:
            # Setup SSL context if enabled
            ssl_context = None
            protocol = "HTTP"
            
            if self.settings.enable_ssl:
                from ..config.settings import get_user_data_dir
                
                # Determine SSL certificate paths
                if self.settings.ssl_cert_path and self.settings.ssl_key_path:
                    # Use provided certificate paths
                    cert_path = self.settings.ssl_cert_path
                    key_path = self.settings.ssl_key_path
                    logger.info(f"Using provided SSL certificates: {cert_path}, {key_path}")
                else:
                    # Auto-generate or get existing certificates
                    user_data_dir = get_user_data_dir()
                    cert_path, key_path = get_ssl_certificate_paths(
                        user_data_dir,
                        auto_generate=self.settings.ssl_auto_generate
                    )
                
                # Create SSL context
                if cert_path and key_path:
                    ssl_context = create_ssl_context(cert_path, key_path)
                    if ssl_context:
                        self.ssl_context = ssl_context
                        protocol = "HTTPS"
                        logger.info(f"SSL enabled - server will use HTTPS")
                    else:
                        logger.error("Failed to create SSL context, falling back to HTTP")
                        self.settings.enable_ssl = False
                else:
                    logger.error("SSL certificates not available, falling back to HTTP")
                    self.settings.enable_ssl = False
            
            # Create server with or without SSL
            self.server = make_server(
                self.settings.host,
                self.settings.port,
                self.app,
                threaded=True,
                ssl_context=ssl_context
            )
            
            logger.info(f"{protocol} server created on {self.settings.host}:{self.settings.port}")
            
            if self.settings.enable_ssl:
                logger.info("⚠️  Using self-signed certificate - browsers will show security warning")
                logger.info("   You can safely proceed by accepting the certificate")
            
        except Exception as e:
            logger.error(f"Failed to create {protocol} server: {e}")
            raise
    
    def run(self):
        """Main run loop"""
        try:
            logger.info("WebDashboard thread started")
            
            # Send ready status
            protocol = "https" if self.settings.enable_ssl else "http"
            ready_message = MessageBuilder.system_status(
                sender=self.name,
                status="ready",
                details={
                    "host": self.settings.host,
                    "port": self.settings.port,
                    "ssl_enabled": self.settings.enable_ssl,
                    "url": f"{protocol}://{self.settings.host}:{self.settings.port}"
                }
            )
            self.message_bus.publish(ready_message)
            
            # Start HTTP server in separate thread
            server_thread = threading.Thread(
                target=self._run_server,
                name="WebServer",
                daemon=True
            )
            server_thread.start()
            
            # Message processing loop
            while self.running:
                try:
                    # Process messages
                    message = self.message_bus.get_message(self.name, timeout=1.0)
                    if message:
                        self._process_message(message)
                    
                    # Update heartbeat
                    self.heartbeat()
                    
                    time.sleep(0.1)
                    
                except Exception as e:
                    logger.error(f"WebDashboard run loop error: {e}")
                    time.sleep(1.0)
                    
            # Wait for server thread
            if server_thread.is_alive():
                server_thread.join(timeout=5.0)
                
        except Exception as e:
            logger.error(f"WebDashboard run failed: {e}")
        finally:
            logger.info("WebDashboard thread ended")
    
    def _run_server(self):
        """Run HTTP/HTTPS server with SSL error suppression"""
        try:
            protocol = "HTTPS" if self.settings.enable_ssl else "HTTP"
            logger.info(f"Starting {protocol} server on {self.settings.host}:{self.settings.port}")
            
            # Apply SSL error logging suppression
            if self.settings.enable_ssl:
                self._setup_ssl_error_suppression()
            
            self.server.serve_forever()
            
        except Exception as e:
            if self.running:  # Only log if not shutting down
                protocol = "HTTPS" if self.settings.enable_ssl else "HTTP"
                logger.error(f"{protocol} server error: {e}")
    
    def _setup_ssl_error_suppression(self):
        """Setup logging filter to suppress expected SSL connection errors"""
        import logging
        
        class SSLErrorFilter(logging.Filter):
            """Filter to suppress expected SSL connection errors from Werkzeug"""
            
            def filter(self, record):
                # Check if this is a Werkzeug error log
                if record.name == 'werkzeug' and record.levelno >= logging.ERROR:
                    message = record.getMessage()
                    
                    # Suppress specific SSL connection errors that are expected for long-polling
                    suppress_patterns = [
                        'Error on request:',
                        'BrokenPipeError: [Errno 32] Broken pipe',
                        'ssl.SSLError: [SSL: UNEXPECTED_EOF_WHILE_READING]',
                        'unexpected eof while reading',
                        'During handling of the above exception, another exception occurred:'
                    ]
                    
                    # If message contains any suppression pattern, filter it out
                    if any(pattern in message for pattern in suppress_patterns):
                        # Log as debug instead
                        debug_msg = f"SSL connection error (expected for long-polling): {message[:100]}..."
                        logging.getLogger(__name__).debug(debug_msg)
                        return False  # Suppress the original error log
                
                return True  # Allow all other log messages
        
        # Apply filter to werkzeug logger
        werkzeug_logger = logging.getLogger('werkzeug')
        ssl_filter = SSLErrorFilter()
        werkzeug_logger.addFilter(ssl_filter)
        
        logger.debug("Applied SSL error logging suppression filter to Werkzeug")
    
    def shutdown(self):
        """Shutdown web dashboard"""
        try:
            logger.info("Shutting down WebDashboard...")
            
            if self.server:
                self.server.shutdown()
            
        except Exception as e:
            logger.error(f"WebDashboard shutdown error: {e}")
    
    def _process_message(self, message: Message):
        """Process received message"""
        try:
            logger.debug(f"WebDashboard processing message: {message}")
            
            # Handle messages directly since some messages don't trigger subscription handlers
            if message.type == MessageType.CONFIG_UPDATE:
                self._handle_config_update(message)
            elif message.type == MessageType.SYSTEM_STATUS:
                self._handle_system_status(message)
            elif message.type == MessageType.CUSTOM:
                self._handle_custom_message(message)
                
        except Exception as e:
            logger.error(f"Failed to process message: {e}")
    
    def _handle_config_update(self, message: Message):
        """Handle configuration update message"""
        try:
            config_section = message.data.get("config_section")
            config_data = message.data.get("config_data")
            
            logger.info(f"Configuration update received for section: {config_section}")
            
            # Update configuration through config manager
            if config_section and config_data:
                self.config_manager.update_from_web(config_data)
            
        except Exception as e:
            logger.error(f"Failed to handle config update: {e}")
    
    def _handle_system_status(self, message: Message):
        """Handle system status message"""
        try:
            status = message.data.get("status")
            sender = message.sender

            logger.debug(f"System status from {sender}: {status}")

            # Store status for web interface
            # This could be cached or stored in database for display

        except Exception as e:
            logger.error(f"Failed to handle system status: {e}")

    def _handle_custom_message(self, message: Message):
        """Handle custom messages (like timer state responses)"""
        try:
            response = message.data.get("response")
            timer_update = message.data.get("timer_update")

            if response == "timer_state":
                # Update stored timer state
                timer_state = message.data.get("timer_state", {})
                self.current_timer_state.update(timer_state)
                logger.debug(f"Timer state updated: {timer_state}")

            elif response == "timer_stopped":
                # Reset timer state
                self.current_timer_state = {
                    "running": False,
                    "remaining_seconds": 0,
                    "total_seconds": 0,
                    "fixture_id": None,
                    "match_id": None,
                    "start_time": None
                }
                logger.debug("Timer stopped")

            elif timer_update:
                # Handle periodic timer updates from match_timer component
                self.current_timer_state.update(timer_update)
                logger.debug(f"Timer update received: {timer_update}")
                
                # Broadcast timer update to connected clients via global message bus
                try:
                    timer_update_message = Message(
                        type=MessageType.CUSTOM,
                        sender=self.name,
                        data={
                            "timer_update": timer_update,
                            "timestamp": time.time()
                        }
                    )
                    self.message_bus.publish(timer_update_message, broadcast=True)
                    logger.debug("Timer update broadcasted to clients")
                except Exception as broadcast_e:
                    logger.error(f"Failed to broadcast timer update: {broadcast_e}")

        except Exception as e:
            logger.error(f"Failed to handle custom message: {e}")
    
    def get_app_context(self):
        """Get Flask application context"""
        if self.app:
            return self.app.app_context()
        return None
    
    def send_video_command(self, command: str, **kwargs) -> bool:
        """Send video command through message bus"""
        try:
            if command == "play":
                message = MessageBuilder.video_play(
                    sender=self.name,
                    file_path=kwargs.get("file_path", ""),
                    template=kwargs.get("template", "news_template"),
                    overlay_data=kwargs.get("overlay_data", {})
                )
            elif command == "pause":
                message = Message(
                    type=MessageType.VIDEO_PAUSE,
                    sender=self.name,
                    data={}
                )
            elif command == "stop":
                message = Message(
                    type=MessageType.VIDEO_STOP,
                    sender=self.name,
                    data={}
                )
            elif command == "template_change":
                message = MessageBuilder.template_change(
                    sender=self.name,
                    template_name=kwargs.get("template_name", "news_template"),
                    template_data=kwargs.get("template_data", {})
                )
            else:
                logger.error(f"Unknown video command: {command}")
                return False
            
            # Send to qt_player
            message.recipient = "qt_player"
            self.message_bus.publish(message)
            
            logger.info(f"Video command sent: {command}")
            return True
            
        except Exception as e:
            logger.error(f"Failed to send video command: {e}")
            return False
    
    def get_system_status(self) -> Dict[str, Any]:
        """Get current system status"""
        try:
            # Request status from core
            status_request = Message(
                type=MessageType.CONFIG_REQUEST,
                sender=self.name,
                recipient="core",
                data={"section": "status"}
            )
            self.message_bus.publish(status_request)

            # For now, return basic status
            # In a full implementation, this would wait for response or cache status
            protocol = "https" if self.settings.enable_ssl else "http"
            return {
                "web_dashboard": "running",
                "host": self.settings.host,
                "port": self.settings.port,
                "ssl_enabled": self.settings.enable_ssl,
                "url": f"{protocol}://{self.settings.host}:{self.settings.port}",
                "timestamp": time.time()
            }

        except Exception as e:
            logger.error(f"Failed to get system status: {e}")
            return {"error": str(e)}

    def get_timer_state(self) -> Dict[str, Any]:
        """Get current timer state"""
        try:
            # Request fresh timer state from match_timer component
            if self.message_bus:
                request_message = Message(
                    type=MessageType.CUSTOM,
                    sender=self.name,
                    recipient="match_timer",
                    data={
                        "request": "get_timer_state",
                        "timestamp": time.time()
                    },
                    correlation_id=f"timer_state_{int(time.time() * 1000)}"
                )
                self.message_bus.publish(request_message)

            # Return current cached state
            return self.current_timer_state.copy()

        except Exception as e:
            logger.error(f"Failed to get timer state: {e}")
            return {
                "running": False,
                "remaining_seconds": 0,
                "total_seconds": 0,
                "fixture_id": None,
                "match_id": None,
                "start_time": None,
                "error": str(e)
            }


def create_app(db_manager: DatabaseManager, config_manager: ConfigManager, 
               settings: WebConfig) -> Flask:
    """Factory function to create Flask app (for testing)"""
    dashboard = WebDashboard(None, db_manager, config_manager, settings)
    dashboard.initialize()
    return dashboard.app