"""
System service registration for skop-runner.

Supports:
  - macOS: launchd (~/Library/LaunchAgents)
  - Linux: systemd user units (~/.config/systemd/user)
  - Windows: Task Scheduler (logon-triggered scheduled task)
"""

import os
import shutil
import subprocess
import sys
import tempfile
from pathlib import Path

LABEL = "com.skoplabs.runner"
SERVICE_NAME = "skop-runner"
TASK_NAME = "SkopRunner"


def get_skop_runner_path() -> str:
    """Find the installed skop-runner binary, resolving symlinks (pipx)."""
    path = shutil.which("skop-runner")
    if not path:
        raise FileNotFoundError(
            "Could not find 'skop-runner' on PATH. "
            "Make sure it is installed (pip install skop-runner)."
        )
    return str(Path(path).resolve())


# ============================================
# INSTALL
# ============================================


def install_service() -> str:
    """Install and start the system service. Returns a status message."""
    runner_path = get_skop_runner_path()

    if sys.platform == "darwin":
        return _install_macos(runner_path)
    elif sys.platform.startswith("linux"):
        return _install_linux(runner_path)
    elif sys.platform == "win32":
        return _install_windows(runner_path)
    else:
        raise RuntimeError(
            f"System service is not supported on {sys.platform}.\n"
            "You can run 'skop-runner start' manually or set up a scheduled task."
        )


def _install_macos(runner_path: str) -> str:
    plist_dir = Path.home() / "Library" / "LaunchAgents"
    plist_dir.mkdir(parents=True, exist_ok=True)
    plist_path = plist_dir / f"{LABEL}.plist"

    log_dir = Path.home() / "Library" / "Logs"
    log_dir.mkdir(parents=True, exist_ok=True)

    # Capture current PATH so launchd can find conda, pyenv, etc.
    current_path = os.environ.get("PATH", "/usr/bin:/bin:/usr/sbin:/sbin")
    home_dir = str(Path.home())

    plist_content = f"""\
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>{LABEL}</string>
    <key>ProgramArguments</key>
    <array>
        <string>{runner_path}</string>
        <string>start</string>
    </array>
    <key>EnvironmentVariables</key>
    <dict>
        <key>PATH</key>
        <string>{current_path}</string>
        <key>HOME</key>
        <string>{home_dir}</string>
    </dict>
    <key>WorkingDirectory</key>
    <string>{home_dir}</string>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
    <key>ThrottleInterval</key>
    <integer>5</integer>
    <key>StandardOutPath</key>
    <string>{log_dir}/skop-runner.log</string>
    <key>StandardErrorPath</key>
    <string>{log_dir}/skop-runner.err</string>
</dict>
</plist>
"""
    # Unload first if already loaded (idempotent reinstall)
    if plist_path.exists():
        subprocess.run(
            ["launchctl", "unload", str(plist_path)],
            capture_output=True,
        )

    plist_path.write_text(plist_content)
    # Remove extended attributes that may prevent launchd from running the plist
    subprocess.run(["xattr", "-c", str(plist_path)], capture_output=True)
    subprocess.run(
        ["launchctl", "load", str(plist_path)],
        check=True,
        capture_output=True,
    )
    # Force-start via kickstart to bypass any throttling from previous failed attempts
    uid = os.getuid()
    subprocess.run(
        ["launchctl", "kickstart", f"gui/{uid}/{LABEL}"],
        capture_output=True,
    )
    return f"Service installed and started.\nLogs: {log_dir}/skop-runner.log"


def _install_linux(runner_path: str) -> str:
    unit_dir = Path.home() / ".config" / "systemd" / "user"
    unit_dir.mkdir(parents=True, exist_ok=True)
    unit_path = unit_dir / f"{SERVICE_NAME}.service"

    unit_content = f"""\
[Unit]
Description=Skop Runner
After=network-online.target
Wants=network-online.target

[Service]
ExecStart={runner_path} start
Restart=always
RestartSec=5
Environment=HOME={Path.home()}

[Install]
WantedBy=default.target
"""
    unit_path.write_text(unit_content)
    subprocess.run(
        ["systemctl", "--user", "daemon-reload"],
        check=True,
        capture_output=True,
    )
    subprocess.run(
        ["systemctl", "--user", "enable", "--now", SERVICE_NAME],
        check=True,
        capture_output=True,
    )
    return f"Service installed and started.\nLogs: journalctl --user -u {SERVICE_NAME} -f"


# ============================================
# UNINSTALL
# ============================================


def uninstall_service() -> str:
    """Stop and remove the system service. Returns a status message."""
    if sys.platform == "darwin":
        return _uninstall_macos()
    elif sys.platform.startswith("linux"):
        return _uninstall_linux()
    elif sys.platform == "win32":
        return _uninstall_windows()
    else:
        raise RuntimeError(f"System service is not supported on {sys.platform}.")


def _uninstall_macos() -> str:
    plist_path = Path.home() / "Library" / "LaunchAgents" / f"{LABEL}.plist"
    if not plist_path.exists():
        return "Service is not installed."

    subprocess.run(
        ["launchctl", "unload", str(plist_path)],
        capture_output=True,
    )
    plist_path.unlink()
    return "Service stopped and removed."


def _uninstall_linux() -> str:
    unit_path = (
        Path.home() / ".config" / "systemd" / "user" / f"{SERVICE_NAME}.service"
    )
    if not unit_path.exists():
        return "Service is not installed."

    subprocess.run(
        ["systemctl", "--user", "disable", "--now", SERVICE_NAME],
        capture_output=True,
    )
    unit_path.unlink()
    subprocess.run(
        ["systemctl", "--user", "daemon-reload"],
        capture_output=True,
    )
    return "Service stopped and removed."


# ============================================
# STATUS
# ============================================


def get_service_status() -> str:
    """Return service status: 'running', 'stopped', or 'not installed'."""
    if sys.platform == "darwin":
        return _status_macos()
    elif sys.platform.startswith("linux"):
        return _status_linux()
    elif sys.platform == "win32":
        return _status_windows()
    else:
        return "not supported"


def _status_macos() -> str:
    plist_path = Path.home() / "Library" / "LaunchAgents" / f"{LABEL}.plist"
    if not plist_path.exists():
        return "not installed"

    result = subprocess.run(
        ["launchctl", "list", LABEL],
        capture_output=True,
        text=True,
    )
    if result.returncode != 0:
        return "stopped"

    # launchctl list <LABEL> returns a dict format.
    # If "PID" key is present, the process is running.
    if '"PID"' in result.stdout:
        return "running"
    return "stopped"


def _status_linux() -> str:
    unit_path = (
        Path.home() / ".config" / "systemd" / "user" / f"{SERVICE_NAME}.service"
    )
    if not unit_path.exists():
        return "not installed"

    result = subprocess.run(
        ["systemctl", "--user", "is-active", SERVICE_NAME],
        capture_output=True,
        text=True,
    )
    status = result.stdout.strip()
    if status == "active":
        return "running"
    return "stopped"


# ============================================
# WINDOWS (Task Scheduler)
# ============================================


def _install_windows(runner_path: str) -> str:
    from xml.sax.saxutils import escape as xml_escape
    safe_path = xml_escape(runner_path)
    task_xml = f"""\
<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
  <Triggers>
    <LogonTrigger>
      <Enabled>true</Enabled>
    </LogonTrigger>
  </Triggers>
  <Settings>
    <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
    <RestartOnFailure>
      <Interval>PT1M</Interval>
      <Count>3</Count>
    </RestartOnFailure>
    <ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
    <AllowStartOnDemand>true</AllowStartOnDemand>
  </Settings>
  <Actions>
    <Exec>
      <Command>{safe_path}</Command>
      <Arguments>start</Arguments>
    </Exec>
  </Actions>
</Task>"""

    # Write XML to temp file, import via schtasks, then delete temp file
    fd, xml_path = tempfile.mkstemp(suffix=".xml", prefix="skop-task-")
    try:
        with os.fdopen(fd, "w", encoding="utf-16") as f:
            f.write(task_xml)
        result = subprocess.run(
            ["schtasks", "/Create", "/TN", TASK_NAME, "/XML", xml_path, "/F"],
            capture_output=True, text=True,
        )
        if result.returncode != 0:
            raise RuntimeError(f"schtasks /Create failed: {result.stderr.strip()}")
    finally:
        try:
            os.unlink(xml_path)
        except OSError:
            pass

    # Start the task immediately
    subprocess.run(
        ["schtasks", "/Run", "/TN", TASK_NAME],
        capture_output=True,
    )
    return (
        f"Service installed and started via Task Scheduler.\n"
        f"Task name: {TASK_NAME}\n"
        f"View in Task Scheduler or run: schtasks /Query /TN {TASK_NAME}"
    )


def _uninstall_windows() -> str:
    result = subprocess.run(
        ["schtasks", "/Delete", "/TN", TASK_NAME, "/F"],
        capture_output=True, text=True,
    )
    if result.returncode != 0:
        if "cannot find" in result.stderr.lower() or "does not exist" in result.stderr.lower():
            return "Service is not installed."
        raise RuntimeError(f"schtasks /Delete failed: {result.stderr.strip()}")
    return "Service stopped and removed."


def _status_windows() -> str:
    result = subprocess.run(
        ["schtasks", "/Query", "/TN", TASK_NAME, "/FO", "CSV", "/NH"],
        capture_output=True, text=True,
    )
    if result.returncode != 0:
        return "not installed"
    output = result.stdout.strip()
    if "Running" in output:
        return "running"
    return "stopped"
