#!/usr/bin/env python3
from flask import Flask, request, jsonify
from brother_ql.backends import backend_factory
from brother_ql.backends.helpers import discover, status, send
from brother_ql.raster import BrotherQLRaster
import logging
import sys
import time
from typing import Optional

# Configure logging
logging.basicConfig(
    level=logging.DEBUG,  # Changed to DEBUG for more detailed logging
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

class LabelPrinterReceiver:
    def __init__(self, printer_identifier: str = "usb://0x04f9:0x20a7"):
        """Initialize the label printer receiver.
        
        Args:
            printer_identifier: USB identifier for the printer
        """
        logger.info(f"Initializing LabelPrinterReceiver with printer identifier: {printer_identifier}")
        self.printer_identifier = printer_identifier
        self.backend = None
        self._print_lock = False
        self._last_print_time = None
        self._print_in_progress = False
        self._last_known_state = None
        self._last_state_time = None
        self._state_cache_timeout = 1.0  # seconds
        
    def connect_printer(self) -> bool:
        """Connect to the printer via USB.
        
        Returns:
            bool: True if connection was successful, False otherwise
        """
        logger.info("Attempting to connect to printer...")
        try:
            # Get the backend class from the factory
            backend_info = backend_factory('pyusb')
            logger.debug(f"Backend info: {backend_info}")
            self.backend = backend_info['backend_class'](device_specifier=self.printer_identifier)
            
            # Connect to the printer
            self.backend.write = self.backend.write  # This triggers the connection
            logger.info("Successfully connected to printer")
            return True
            
        except Exception as e:
            logger.error(f"Error connecting to printer: {e}")
            self.backend = None
            return False
            
    def is_printer_busy(self) -> bool:
        """Check if the printer is currently busy.
        
        Returns:
            bool: True if printer is busy, False otherwise
        """
        logger.debug("Checking printer status...")
        
        # Check if we have a recent cached state
        if (self._last_known_state is not None and 
            self._last_state_time is not None and 
            time.time() - self._last_state_time < self._state_cache_timeout):
            logger.debug("Using cached printer state")
            return self._last_known_state.get('phase_type') != 'Waiting to receive'
            
        try:
            if not self.backend:
                logger.warning("No backend connection, attempting to connect...")
                if not self.connect_printer():
                    logger.error("Failed to connect to printer")
                    return False
                    
            # Get actual printer status
            logger.debug("Requesting printer status...")
            status_info, raw_data = status(printer_identifier=self.printer_identifier)
            logger.debug(f"Raw status data: {raw_data}")
            logger.debug(f"Parsed status info: {status_info}")
            
            # Cache the state
            self._last_known_state = status_info
            self._last_state_time = time.time()
            
            # Check for any errors
            if status_info.get('errors'):
                logger.error(f"Printer errors detected: {status_info['errors']}")
                return False
                
            # Check printer phase
            phase_type = status_info.get('phase_type')
            logger.debug(f"Printer phase type: {phase_type}")
            
            if phase_type == 'Waiting to receive':
                logger.info("Printer is ready for next job")
                return False
            elif phase_type == 'Printing':
                logger.info("Printer is currently printing")
                return True
            else:
                logger.info(f"Printer is in unknown phase: {phase_type}")
                return True  # Assume busy for unknown phases
            
        except Exception as e:
            logger.error(f"Error getting printer status: {e}")
            # If we get an error, clear the cached state
            self._last_known_state = None
            self._last_state_time = None
            return False
            
    def handle_print_request(self) -> tuple:
        """Handle incoming print requests.
        
        Returns:
            tuple: (response message, HTTP status code)
        """
        logger.info("Received print request")
        if self._print_lock:
            logger.warning("Print lock is active, rejecting request")
            return {"message": "Another print job is being processed"}, 429
            
        try:
            logger.info("Acquiring print lock")
            self._print_lock = True
            self._print_in_progress = True
            self._last_print_time = time.time()
            
            # Get the print data from the request
            print_data = request.get_data()
            if not print_data:
                logger.error("No print data provided in request")
                return {"message": "No print data provided"}, 400
            logger.info(f"Received print data of size: {len(print_data)} bytes")
            
            # Use the send helper which handles the complete print lifecycle
            logger.info("Sending print data to printer...")
            try:
                result = send(
                    instructions=print_data,
                    printer_identifier=self.printer_identifier,
                    backend_identifier='pyusb',
                    blocking=True
                )
            except Exception as e:
                if "Device not found" in str(e):
                    logger.error("Printer device not found")
                    return {"message": "Printer device not found"}, 500
                raise
            
            logger.debug(f"Print result: {result}")
            
            # Update cached state with the result
            if 'printer_state' in result:
                self._last_known_state = result['printer_state']
                self._last_state_time = time.time()
            
            if result['outcome'] == 'error':
                logger.error(f"Print error: {result.get('printer_state', {}).get('errors', 'Unknown error')}")
                return {"message": f"Print error: {result.get('printer_state', {}).get('errors', 'Unknown error')}"}, 500
                
            if not result['did_print']:
                logger.warning("Print may not have completed successfully")
                return {"message": "Print sent but completion status unclear"}, 200
                
            logger.info("Print completed successfully")
            return {"message": "Print request processed successfully"}, 200
            
        except Exception as e:
            logger.error(f"Error processing print request: {e}")
            # Clear cached state on error
            self._last_known_state = None
            self._last_state_time = None
            return {"message": f"Error: {str(e)}"}, 500
        finally:
            logger.info("Releasing print lock")
            self._print_lock = False
            self._print_in_progress = False
            
    def start_server(self, host: str = "0.0.0.0", port: int = 8000):
        """Start the web server to receive print requests.
        
        Args:
            host: Host to bind the server to
            port: Port to listen on
        """
        logger.info(f"Starting print server on {host}:{port}")
        app = Flask(__name__)
        
        @app.route('/print', methods=['POST'])
        def print_endpoint():
            logger.info("Received print request at /print endpoint")
            response, status_code = self.handle_print_request()
            logger.info(f"Print request completed with status {status_code}: {response}")
            return jsonify(response), status_code
            
        @app.route('/status', methods=['GET'])
        def status_endpoint():
            logger.info("Received status request at /status endpoint")
            try:
                if not self.backend:
                    if not self.connect_printer():
                        logger.error("Failed to connect to printer for status check")
                        return jsonify({"status": "error", "message": "Could not connect to printer"}), 500
                
                # Get actual printer status with retry logic
                max_retries = 3
                retry_delay = 1  # seconds
                last_error = None
                
                for attempt in range(max_retries):
                    try:
                        logger.debug(f"Requesting printer status (attempt {attempt + 1}/{max_retries})...")
                        status_info, raw_data = status(printer_identifier=self.printer_identifier)
                        logger.debug(f"Raw status data: {raw_data}")
                        logger.debug(f"Parsed status info: {status_info}")
                        
                        # Check for any errors
                        if status_info.get('errors'):
                            logger.error(f"Printer errors detected: {status_info['errors']}")
                            return jsonify({
                                "status": "error",
                                "message": f"Printer errors: {status_info['errors']}"
                            }), 500
                        
                        # Check printer phase
                        phase_type = status_info.get('phase_type')
                        logger.debug(f"Printer phase type: {phase_type}")
                        
                        # Convert status info to JSON-serializable format
                        serializable_status = {
                            'status_type': status_info.get('status_type'),
                            'phase_type': phase_type,
                            'model_name': status_info.get('model_name'),
                            'media_type': status_info.get('media_type'),
                            'media_width': status_info.get('media_width'),
                            'media_length': status_info.get('media_length'),
                            'errors': status_info.get('errors', [])
                        }
                        
                        # Add media info if available
                        if 'identified_media' in status_info:
                            media = status_info['identified_media']
                            serializable_status['media'] = {
                                'identifier': media.identifier,
                                'tape_size': media.tape_size,
                                'form_factor': str(media.form_factor),
                                'color': str(media.color)
                            }
                        
                        if phase_type == 'Waiting to receive':
                            logger.info("Printer status: ready")
                            return jsonify({
                                "status": "ready",
                                "phase": phase_type,
                                "details": serializable_status
                            }), 200
                        elif phase_type == 'Printing':
                            logger.info("Printer status: busy")
                            return jsonify({
                                "status": "busy",
                                "phase": phase_type,
                                "details": serializable_status
                            }), 200
                        else:
                            logger.info(f"Printer is in unknown phase: {phase_type}")
                            return jsonify({
                                "status": "busy",
                                "phase": phase_type,
                                "details": serializable_status
                            }), 200
                            
                    except Exception as e:
                        last_error = e
                        if "Resource busy" in str(e):
                            logger.warning(f"Printer is busy, retrying in {retry_delay} seconds...")
                            time.sleep(retry_delay)
                            retry_delay *= 2  # Exponential backoff
                        else:
                            logger.error(f"Error checking printer status: {e}")
                            return jsonify({
                                "status": "error",
                                "message": str(e)
                            }), 500
                
                # If we've exhausted all retries
                logger.error(f"Failed to get printer status after {max_retries} attempts. Last error: {last_error}")
                return jsonify({
                    "status": "error",
                    "message": f"Printer is busy and not responding. Last error: {str(last_error)}"
                }), 503  # Service Unavailable
                
            except Exception as e:
                logger.error(f"Error checking printer status: {e}")
                return jsonify({
                    "status": "error",
                    "message": str(e)
                }), 500
            
        logger.info(f"Print server started successfully on {host}:{port}")
        app.run(host=host, port=port)
        
def main():
    # Create and start the printer receiver
    logger.info("Starting printer receiver...")
    receiver = LabelPrinterReceiver()
    receiver.start_server()
    
if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        logger.info("Shutting down print server")
        sys.exit(0) 