"""
Serial Bridge - SSE Server
Reads from COM port and streams data to React frontend via Server-Sent Events
No backend storage - direct streaming only
"""
import os
import logging
import serial
import serial.tools.list_ports
from dotenv import load_dotenv
from threading import Thread
from datetime import datetime
from flask import Flask, Response
from flask_cors import CORS, cross_origin
from queue import Queue
import json
import time

# Load environment variables
load_dotenv()

# Configure logging
logging.basicConfig(
    level=os.getenv('LOG_LEVEL', 'INFO'),
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('serial_bridge.log'),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)

app = Flask(__name__)
CORS(app)  # Enable CORS for all routes

# Global state
data_queue = Queue()
connected_clients = []
is_running = False
reader_thread = None


class SerialReader:
    def __init__(self, port, baudrate, timeout):
        self.port = port
        self.baudrate = baudrate
        self.timeout = timeout
        self.ser = None
        self.is_connected = False

    def connect(self):
        try:
            self.ser = serial.Serial(
                port=self.port,
                baudrate=self.baudrate,
                timeout=self.timeout
            )
            self.is_connected = True
            logger.info(f"[OK] Connected to {self.port} at {self.baudrate} baud")
            return True
        except Exception as e:
            logger.error(f"[ERROR] Failed to connect to {self.port}: {e}")
            self.is_connected = False
            return False

    def read_data(self):
        if not self.is_connected:
            return None
        
        try:
            if self.ser.in_waiting:
                line = self.ser.readline().decode('utf-8', errors='ignore').strip()
                if line:
                    return {
                        'port': self.port,
                        'data': line,
                        'timestamp': datetime.now().isoformat(),
                        'received_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                    }
        except Exception as e:
            logger.error(f"Error reading from {self.port}: {e}")
        
        return None

    def disconnect(self):
        if self.ser:
            self.ser.close()
        self.is_connected = False
        logger.info(f"[OK] Disconnected from {self.port}")


def read_serial_data():
    """Continuously read from serial port and queue data"""
    global is_running
    
    reader = SerialReader(
        port=os.getenv('COM_PORT', 'COM3'),
        baudrate=int(os.getenv('BAUD_RATE', '9600')),
        timeout=float(os.getenv('TIMEOUT', '1'))
    )
    
    if not reader.connect():
        logger.error("Failed to connect to COM port")
        return
    
    read_interval = float(os.getenv('READ_INTERVAL', '0.5'))
    
    while is_running:
        try:
            data = reader.read_data()
            if data:
                data_queue.put(data)
                logger.info(f"[RX] {data['port']}: {data['data']}")
            
            time.sleep(read_interval)
        
        except KeyboardInterrupt:
            logger.info("Serial reader interrupted")
            break
        except Exception as e:
            logger.error(f"Error in serial reader: {e}")
    
    reader.disconnect()


@app.route('/events')
@cross_origin()
def events():
    """SSE endpoint - streams serial data to connected clients"""
    
    def generate():
        client_id = id(object())
        connected_clients.append(client_id)
        
        logger.info(f"[CONNECT] Client connected. Total: {len(connected_clients)}")
        
        try:
            # Send connection message
            yield f"data: {json.dumps({'status': 'connected', 'message': 'Connected to serial bridge'})}\n\n"
            
            # Stream data from queue
            while is_running:
                try:
                    # Get data with timeout to allow graceful shutdown
                    data = data_queue.get(timeout=1)
                    yield f"data: {json.dumps(data)}\n\n"
                    logger.debug(f"[TX] Sent: {data['data']}")
                
                except:
                    # Queue timeout - send heartbeat
                    yield f": heartbeat\n\n"
        
        except GeneratorExit:
            # Client disconnected
            pass
        finally:
            if client_id in connected_clients:
                connected_clients.remove(client_id)
            logger.info(f"[DISCONNECT] Client disconnected. Total: {len(connected_clients)}")
    
    return Response(
        generate(),
        mimetype='text/event-stream',
        headers={
            'Cache-Control': 'no-cache',
            'X-Accel-Buffering': 'no',
            'Connection': 'keep-alive'
        }
    )


@app.route('/status')
def status():
    """Get current status"""
    return {
        'status': 'running' if is_running else 'stopped',
        'connected_clients': len(connected_clients),
        'com_port': os.getenv('COM_PORT', 'COM3'),
        'baud_rate': int(os.getenv('BAUD_RATE', '9600'))
    }


@app.route('/ports')
def list_ports():
    """List available COM ports"""
    ports = []
    for port, desc, hwid in serial.tools.list_ports.comports():
        ports.append({
            'port': port,
            'description': desc,
            'hwid': hwid
        })
    return {'ports': ports}


def start_app():
    """Start the application"""
    global is_running, reader_thread
    
    logger.info("=" * 60)
    logger.info("ScalesApp Serial Bridge - SSE Streaming")
    logger.info("=" * 60)
    logger.info(f"COM Port: {os.getenv('COM_PORT', 'COM3')}")
    logger.info(f"Baud Rate: {os.getenv('BAUD_RATE', '9600')}")
    logger.info(f"SSE Endpoint: http://127.0.0.1:5000/events")
    logger.info("=" * 60)
    
    is_running = True
    
    # Start serial reader thread
    reader_thread = Thread(target=read_serial_data, daemon=True)
    reader_thread.start()
    logger.info("[OK] Serial reader thread started\n")
    
    # Start Flask app
    debug = os.getenv('DEBUG', 'False').lower() == 'true'
    host = os.getenv('HOST', '127.0.0.1')
    app.run(host=host, port=5000, debug=debug, use_reloader=False)


def stop_app():
    """Stop the application"""
    global is_running
    is_running = False
    logger.info("\n[OK] Application stopped")


if __name__ == '__main__':
    try:
        start_app()
    except KeyboardInterrupt:
        stop_app()
