#!/usr/bin/python3

import sys
import signal
from PySide6.QtWidgets import (
    QApplication, QSystemTrayIcon, QMenu,
    QWidgetAction, QWidget, QPushButton,
    QHBoxLayout
)
from PySide6.QtGui import QIcon, QCursor
from PySide6.QtCore import QTimer

import toml
import subprocess

config = toml.load(open("/etc/servicetray.toml"))
services = {}


# Handle Ctrl+C cleanly
def sigint_handler(*args):
    print("Ctrl+C pressed — exiting...")
    QApplication.quit()
signal.signal(signal.SIGINT, sigint_handler)
# REQUIRED so SIGINT is processed during the Qt event loop
signal.signal(signal.SIGTERM, sigint_handler)


def create_row_action(menu, callbacks):
    action = QWidgetAction(menu)

    container = QWidget()
    layout = QHBoxLayout(container)
    layout.setContentsMargins(0, 0, 0, 0)
    layout.setSpacing(0)

    label_text, label_cb = callbacks.pop(0)
    lbl = QPushButton(label_text)
    lbl.setStyleSheet("""
        QPushButton {
            font-size: 14px;
            border: none;
            background: transparent;
            padding: 4px 4px;
            margin: 0px;
            text-align: left;
        }
        QPushButton:hover {
            background: #308cc6;
        }
    """)
    lbl.clicked.connect(label_cb)
    layout.addWidget(lbl)
    s['lbl'] = lbl
    s['btns'] = []

    for btn_text, callback in callbacks:
        btn = QPushButton(btn_text)
        btn.setFixedWidth(25)
        btn.clicked.connect(callback)
        btn.setStyleSheet("""
            QPushButton {
                font-size: 14px;
                border: none;
                background: transparent;
                padding: 4px 2px;
                margin: 0px;
            }
            QPushButton:hover {
                background: #308cc6;
            }
        """)
        layout.addWidget(btn)
        s['btns'].append(btn)

    action.setDefaultWidget(container)
    return action


def systemctl(*args, user=False):
    if user:
        cmd = ['systemctl', '--user']
        cmd.extend(args)
        subprocess.run(cmd)
    else:
        cmd = ['sudo', '-n', 'servicetray_systemctl']
        cmd.extend(args)
        subprocess.run(cmd)

def service_startstop(service):
    s = services[service]
    if s['state'] == 'active':
        systemctl('stop', service, user=(s['type'] == 'user'))
    else: # inactive or failed
        systemctl('start', service, user=(s['type'] == 'user'))

def service_restart(service):
    s = services[service]
    systemctl('restart', service, user=(s['type'] == 'user'))

def service_kill(service):
    s = services[service]
    systemctl('kill', service, user=(s['type'] == 'user'))


def update_state(service):
    s = services[service]
    cmd = ['systemctl', 'is-active']
    if s['type'] == 'user':
        cmd.append('--user')
    cmd.append(service)
    s['state'] = subprocess.run(cmd, capture_output=True).stdout.decode().strip()


# Main app
app = QApplication(sys.argv)

tray = QSystemTrayIcon(QIcon("/usr/share/servicetray/gear.png"))
menu = QMenu()

green_circle = "🟢"
red_circle = "⭕"
stop_sign = "🛑"

state_icons = {
    "active": green_circle,
    "inactive": red_circle,
    "failed": stop_sign,
}

def mkcb(f, *args):
    def cb():
        f(*args)
        menu.close()
    return cb

# Create menu content
for service in config.get("services", []):
    sn = service['name']
    services[sn] = {"state": "inactive", "type": service.get('type', 'system')}
    s = services[sn]
    sc = mkcb(service_startstop, sn)
    rc = mkcb(service_restart, sn)
    rk = mkcb(service_kill, sn)
    fields = [(f"{red_circle} {sn}", sc)]
    if config.get('ui', {}).get('show_reload'):
        fields.append(("↻", rc))
    if config.get('ui', {}).get('show_kill'):
        fields.append(("⏻", rk))
    menu.addAction(create_row_action(menu, fields))

menu.addSeparator()
menu.addAction("Quit", app.quit)


# Open menu below tray icon on click
def on_tray_activated(reason):
    if reason in (QSystemTrayIcon.Trigger, QSystemTrayIcon.Context):  # left or right click
        geo = tray.geometry()
        if geo.isValid():
            pos = geo.bottomLeft()
        else:
            pos = QCursor.pos()

        # update ui state
        for sn, service in services.items():
            update_state(sn)
            ss = service['state']
            service['lbl'].setText(f"{state_icons[ss]} {sn}")
            for btn in service['btns']:
                btn.setDisabled((ss != 'active'))

        menu.exec(pos)

tray.activated.connect(on_tray_activated)
#tray.setContextMenu(menu)
tray.show()


# Allow Qt to process SIGINT
# Without this timer, Ctrl+C will not interrupt the Qt event loop.
def keep_alive():
    pass
timer = QTimer()
timer.timeout.connect(keep_alive)
timer.start(100)  # every 100ms Qt processes pending signals


app.exec()
