"""UART Daemon for continuous serial port monitoring.

This module provides a daemon process that monitors a serial port and
writes received data to log files. The daemon has a fixed 30-minute
lifetime and can be auto-restarted by any fwauto command.
"""

import os
import signal
import subprocess
import sys
import time
from datetime import datetime
from pathlib import Path


class UartDaemon:
    """UART monitoring daemon manager."""

    PID_FILE = ".fwauto/uart.pid"
    LOG_DIR = ".fwauto/logs"
    DEFAULT_TIMEOUT = 30 * 60  # 30 minutes in seconds

    def __init__(self, project_root: Path | None = None):
        """Initialize daemon manager.

        Args:
            project_root: Project root directory containing .fwauto/
        """
        self.project_root = project_root or Path.cwd()
        self.pid_file = self.project_root / self.PID_FILE
        self.log_dir = self.project_root / self.LOG_DIR

    def _get_pid(self) -> int | None:
        """Read PID from file if exists."""
        if not self.pid_file.exists():
            return None
        try:
            pid = int(self.pid_file.read_text().strip())
            return pid
        except (ValueError, OSError):
            return None

    def _is_process_running(self, pid: int) -> bool:
        """Check if process with given PID is running."""
        try:
            os.kill(pid, 0)
            return True
        except OSError:
            return False

    def is_running(self) -> bool:
        """Check if daemon is currently running."""
        pid = self._get_pid()
        if pid is None:
            return False
        if not self._is_process_running(pid):
            # Stale PID file, clean up
            self.pid_file.unlink(missing_ok=True)
            return False
        return True

    def start(self, port: str, baudrate: int = 115200) -> bool:
        """Start the UART daemon.

        Args:
            port: Serial port device (e.g., /dev/ttyUSB0)
            baudrate: Baud rate (default: 115200)

        Returns:
            True if daemon started successfully
        """
        if self.is_running():
            print(f"⚠️  UART daemon already running (PID: {self._get_pid()})")
            return False

        # Ensure log directory exists
        self.log_dir.mkdir(parents=True, exist_ok=True)

        # Start daemon process
        runner_module = "fwauto.uart_daemon_runner"
        cmd = [
            sys.executable,
            "-m",
            runner_module,
            "--port",
            port,
            "--baudrate",
            str(baudrate),
            "--project-root",
            str(self.project_root),
            "--timeout",
            str(self.DEFAULT_TIMEOUT),
        ]

        try:
            # Start as detached process
            process = subprocess.Popen(
                cmd,
                stdout=subprocess.DEVNULL,
                stderr=subprocess.DEVNULL,
                start_new_session=True,
            )

            # Write PID file
            self.pid_file.parent.mkdir(parents=True, exist_ok=True)
            self.pid_file.write_text(str(process.pid))

            print(f"✅ UART daemon started (PID: {process.pid})")
            print(f"   Port: {port} @ {baudrate} baud")
            print(f"   Log: {self.log_dir}/")
            print(f"   Timeout: {self.DEFAULT_TIMEOUT // 60} minutes")
            return True

        except Exception as e:
            print(f"❌ Failed to start daemon: {e}")
            return False

    def stop(self) -> bool:
        """Stop the UART daemon.

        Returns:
            True if daemon was stopped
        """
        pid = self._get_pid()
        if pid is None:
            print("ℹ️  UART daemon is not running")
            return True

        if not self._is_process_running(pid):
            # Clean up stale PID file
            self.pid_file.unlink(missing_ok=True)
            print("ℹ️  UART daemon is not running (cleaned stale PID)")
            return True

        try:
            os.kill(pid, signal.SIGTERM)
            # Wait a bit for graceful shutdown
            for _ in range(10):
                time.sleep(0.1)
                if not self._is_process_running(pid):
                    break
            else:
                # Force kill if still running
                os.kill(pid, signal.SIGKILL)

            self.pid_file.unlink(missing_ok=True)
            print(f"✅ UART daemon stopped (PID: {pid})")
            return True

        except OSError as e:
            print(f"❌ Failed to stop daemon: {e}")
            return False

    def restart(self, port: str, baudrate: int = 115200) -> bool:
        """Restart the UART daemon.

        Args:
            port: Serial port device
            baudrate: Baud rate

        Returns:
            True if daemon restarted successfully
        """
        self.stop()
        return self.start(port, baudrate)

    def status(self) -> dict:
        """Get daemon status.

        Returns:
            Dict with status information
        """
        pid = self._get_pid()
        running = self.is_running()

        result = {
            "running": running,
            "pid": pid if running else None,
            "pid_file": str(self.pid_file),
            "log_dir": str(self.log_dir),
        }

        if running and self.pid_file.exists():
            # Get process start time for uptime calculation
            try:
                stat = self.pid_file.stat()
                start_time = datetime.fromtimestamp(stat.st_mtime)
                uptime = datetime.now() - start_time
                result["start_time"] = start_time.isoformat()
                result["uptime_seconds"] = int(uptime.total_seconds())
            except OSError:
                pass

        # Find latest log file
        if self.log_dir.exists():
            log_files = sorted(self.log_dir.glob("uart_*.log"), reverse=True)
            if log_files:
                result["latest_log"] = str(log_files[0])
                result["log_size"] = log_files[0].stat().st_size

        return result

    def get_latest_log_path(self) -> Path | None:
        """Get path to the latest UART log file.

        Returns:
            Path to latest log or None if not found
        """
        if not self.log_dir.exists():
            return None
        log_files = sorted(self.log_dir.glob("uart_*.log"), reverse=True)
        return log_files[0] if log_files else None


def restart_daemon_if_running() -> None:
    """Restart UART daemon if it's currently running.

    This function is called at CLI entry point to reset the daemon timer.
    It's a no-op if daemon is not running.
    After restart, updates last_log_file_path in config.
    """
    try:
        # Find project root by searching for .fwauto/
        from .config import find_project_root, load_project_config

        project_root = find_project_root()
        if project_root is None:
            return

        daemon = UartDaemon(project_root)
        if not daemon.is_running():
            return

        # Get current config for port/baudrate
        config = load_project_config(project_root)
        port = config.log_serial_port
        baudrate = config.log_baudrate

        if port:
            # Restart daemon to reset timer
            daemon.restart(port, baudrate)

            # Wait briefly for daemon to create log file
            time.sleep(0.3)

            # Update last_log_file_path in config
            log_path = daemon.get_latest_log_path()
            if log_path:
                config.update_last_log_path(str(log_path))

    except Exception:
        # Silently ignore errors - this is a background operation
        pass
