"""macOS launchd service manager.

NOTE: With the embedded MLX Server, launchd services for individual model
profiles are no longer used. The MLX Manager itself can be installed as
a launchd service instead. This module is kept for backwards compatibility
and builds commands for mlx-manager serve.
"""

import plistlib
import shutil
import subprocess
import sys
from pathlib import Path

from mlx_manager.config import DEFAULT_PORT
from mlx_manager.models import ExecutionProfile
from mlx_manager.models.dto.system import LaunchdStatus
from mlx_manager.services.launchctl_utils import bootout, bootstrap, kickstart, kill_service


class LaunchdManager:
    """Manages launchd service configuration."""

    def __init__(self) -> None:
        self.launch_agents_dir = Path.home() / "Library" / "LaunchAgents"
        self.label_prefix = "com.mlx-manager"

    def get_label(self, profile: ExecutionProfile) -> str:
        """Get the launchd label for a profile."""
        # Sanitize profile name for use in label
        safe_name = profile.name.lower().replace(" ", "-").replace("_", "-")
        # Remove any other special characters
        safe_name = "".join(c for c in safe_name if c.isalnum() or c == "-")
        return f"{self.label_prefix}.{safe_name}"

    def get_plist_path(self, profile: ExecutionProfile) -> Path:
        """Get the plist file path for a profile."""
        return self.launch_agents_dir / f"{self.get_label(profile)}.plist"

    def generate_plist(self, profile: ExecutionProfile, port: int = DEFAULT_PORT) -> dict:
        """Generate a launchd plist dictionary for a profile.

        NOTE: With the embedded MLX Server, this generates a plist
        that runs mlx-manager serve. The profile's auto_start setting
        controls whether the manager starts at login.
        """
        label = self.get_label(profile)

        # Find mlx-manager executable using stable path resolution
        mlx_manager_path = shutil.which("mlx-manager")
        if not mlx_manager_path:
            mlx_manager_path = str(Path(sys.executable).parent / "mlx-manager")

        # Build command for mlx-manager serve
        program_args = [mlx_manager_path, "serve", "--port", str(port)]

        # Determine PATH from resolved executable
        exe_dir = str(Path(mlx_manager_path).parent)

        # Build plist dictionary
        plist = {
            "Label": label,
            "ProgramArguments": program_args,
            "RunAtLoad": profile.auto_start,
            "KeepAlive": {"SuccessfulExit": False, "Crashed": True},
            "StandardOutPath": f"/tmp/{label}.log",
            "StandardErrorPath": f"/tmp/{label}.err",
            "EnvironmentVariables": {
                "PATH": f"{exe_dir}:/usr/local/bin:/usr/bin:/bin",
                "HOME": str(Path.home()),
                "PYTHONUNBUFFERED": "1",
            },
            "ProcessType": "Interactive",
            "LowPriorityIO": False,
            "ThrottleInterval": 30,
        }

        return plist

    def install(self, profile: ExecutionProfile) -> str:
        """Install a launchd service for a profile."""
        # Ensure LaunchAgents directory exists
        self.launch_agents_dir.mkdir(parents=True, exist_ok=True)

        # Generate and write plist
        plist = self.generate_plist(profile)
        plist_path = self.get_plist_path(profile)
        label = self.get_label(profile)

        with open(plist_path, "wb") as f:
            plistlib.dump(plist, f)

        # Load the service using modern API
        bootstrap(str(plist_path), label)

        return str(plist_path)

    def uninstall(self, profile: ExecutionProfile) -> bool:
        """Uninstall a launchd service."""
        plist_path = self.get_plist_path(profile)

        if not plist_path.exists():
            return False

        # Unload the service using modern API
        label = self.get_label(profile)
        bootout(label)

        # Remove plist file
        plist_path.unlink(missing_ok=True)

        return True

    def is_installed(self, profile: ExecutionProfile) -> bool:
        """Check if a launchd service is installed."""
        return self.get_plist_path(profile).exists()

    def is_running(self, profile: ExecutionProfile) -> bool:
        """Check if a launchd service is running."""
        label = self.get_label(profile)

        result = subprocess.run(["launchctl", "list", label], capture_output=True, text=True)

        return result.returncode == 0

    def start(self, profile: ExecutionProfile) -> bool:
        """Start a launchd service."""
        label = self.get_label(profile)
        return kickstart(label)

    def stop(self, profile: ExecutionProfile) -> bool:
        """Stop a launchd service."""
        label = self.get_label(profile)
        return kill_service(label)

    def get_status(self, profile: ExecutionProfile) -> LaunchdStatus:
        """Get detailed status of a launchd service."""
        label = self.get_label(profile)
        plist_path = str(self.get_plist_path(profile))

        result = subprocess.run(["launchctl", "list", label], capture_output=True, text=True)

        if result.returncode != 0:
            return LaunchdStatus(
                installed=self.is_installed(profile),
                running=False,
                label=label,
                plist_path=plist_path,
            )

        # Parse output: PID\tStatus\tLabel
        lines = result.stdout.strip().split("\n")
        if len(lines) >= 1:
            parts = lines[0].split("\t")
            pid = int(parts[0]) if parts[0] != "-" else None

            return LaunchdStatus(
                installed=True,
                running=pid is not None,
                pid=pid,
                label=label,
                plist_path=plist_path,
            )

        return LaunchdStatus(
            installed=True,
            running=False,
            label=label,
            plist_path=plist_path,
        )


# Singleton instance
launchd_manager = LaunchdManager()
