"""UART Daemon Runner - Standalone process for serial port monitoring.

This module is launched as a subprocess by UartDaemon.start().
It monitors a serial port and writes data to log files.
"""

import argparse
import signal
import sys
import time
from datetime import datetime
from pathlib import Path

import serial


class UartMonitor:
    """Serial port monitor that writes to log files."""

    def __init__(
        self,
        port: str,
        baudrate: int,
        project_root: Path,
        timeout: int,
    ):
        self.port = port
        self.baudrate = baudrate
        self.project_root = project_root
        self.timeout = timeout
        self.log_dir = project_root / ".fwauto" / "logs"
        self.running = True
        self.serial_conn: serial.Serial | None = None
        self.log_file: Path | None = None

    def _setup_signal_handlers(self) -> None:
        """Setup signal handlers for graceful shutdown."""
        signal.signal(signal.SIGTERM, self._handle_signal)
        signal.signal(signal.SIGINT, self._handle_signal)

    def _handle_signal(self, signum: int, frame: object) -> None:
        """Handle shutdown signals."""
        self.running = False

    def _create_log_file(self) -> Path:
        """Create a new log file with timestamp."""
        self.log_dir.mkdir(parents=True, exist_ok=True)
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        log_file = self.log_dir / f"uart_{timestamp}.log"
        return log_file

    def _open_serial(self) -> bool:
        """Open serial port connection."""
        try:
            self.serial_conn = serial.Serial(
                port=self.port,
                baudrate=self.baudrate,
                timeout=1.0,  # 1 second read timeout
            )
            return True
        except serial.SerialException as e:
            print(f"❌ Failed to open serial port {self.port}: {e}", file=sys.stderr)
            return False

    def _close_serial(self) -> None:
        """Close serial port connection."""
        if self.serial_conn and self.serial_conn.is_open:
            self.serial_conn.close()

    def run(self) -> int:
        """Run the monitor loop.

        Returns:
            Exit code (0 for success)
        """
        self._setup_signal_handlers()

        if not self._open_serial():
            return 1

        self.log_file = self._create_log_file()
        start_time = time.time()

        try:
            with open(self.log_file, "a", buffering=1) as f:  # Line buffered
                # Write header
                f.write(f"# UART Log: {self.port} @ {self.baudrate} baud\n")
                f.write(f"# Started: {datetime.now().isoformat()}\n")
                f.write(f"# Timeout: {self.timeout} seconds\n")
                f.write("#" + "=" * 60 + "\n")
                f.flush()

                while self.running:
                    # Check timeout
                    elapsed = time.time() - start_time
                    if elapsed >= self.timeout:
                        f.write(f"\n# Timeout reached ({self.timeout}s), stopping.\n")
                        break

                    # Read from serial
                    try:
                        if self.serial_conn and self.serial_conn.in_waiting:
                            data = self.serial_conn.read(self.serial_conn.in_waiting)
                            if data:
                                # Decode with error handling
                                text = data.decode("utf-8", errors="replace")
                                f.write(text)
                                f.flush()
                        else:
                            # Small sleep to prevent busy loop
                            time.sleep(0.01)
                    except serial.SerialException as e:
                        f.write(f"\n# Serial error: {e}\n")
                        break

                # Write footer
                f.write(f"\n# Stopped: {datetime.now().isoformat()}\n")

        finally:
            self._close_serial()

        return 0


def main() -> int:
    """Main entry point for daemon runner."""
    parser = argparse.ArgumentParser(description="UART Daemon Runner")
    parser.add_argument("--port", required=True, help="Serial port device")
    parser.add_argument("--baudrate", type=int, default=115200, help="Baud rate")
    parser.add_argument("--project-root", required=True, help="Project root directory")
    parser.add_argument("--timeout", type=int, default=1800, help="Timeout in seconds")

    args = parser.parse_args()

    monitor = UartMonitor(
        port=args.port,
        baudrate=args.baudrate,
        project_root=Path(args.project_root),
        timeout=args.timeout,
    )

    return monitor.run()


if __name__ == "__main__":
    sys.exit(main())
