package monitor

import (
	"context"
	"log"

	"github.com/godbus/dbus/v5"

	"led-controller/config"
)

type Event int

const (
	DisplayOn Event = iota
	DisplayOff
)

type Monitor struct {
	cfg    *config.Config
	events chan<- Event
}

func New(cfg *config.Config, events chan<- Event) (*Monitor, error) {
	return &Monitor{cfg: cfg, events: events}, nil
}

func (m *Monitor) Run(ctx context.Context) {
	method := m.cfg.Monitor.Method

	if method == "screensaver" || method == "all" {
		go m.watchScreenSaver(ctx)
	}
	if method == "logind" || method == "all" {
		go m.watchLogind(ctx)
	}

	<-ctx.Done()
}

// watchScreenSaver listens on the session bus for org.freedesktop.ScreenSaver.ActiveChanged
// This covers GNOME, KDE, XFCE, and other freedesktop-compliant DEs.
func (m *Monitor) watchScreenSaver(ctx context.Context) {
	conn, err := dbus.ConnectSessionBus()
	if err != nil {
		log.Printf("screensaver monitor: cannot connect to session bus: %v", err)
		return
	}
	defer conn.Close()

	// Match both the freedesktop and GNOME screensaver interfaces
	rules := []string{
		"type='signal',interface='org.freedesktop.ScreenSaver',member='ActiveChanged'",
		"type='signal',interface='org.gnome.ScreenSaver',member='ActiveChanged'",
	}

	for _, rule := range rules {
		call := conn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule)
		if call.Err != nil {
			log.Printf("screensaver monitor: AddMatch failed for %q: %v", rule, call.Err)
		}
	}

	sigCh := make(chan *dbus.Signal, 16)
	conn.Signal(sigCh)

	log.Println("screensaver monitor: listening for ActiveChanged signals")

	for {
		select {
		case <-ctx.Done():
			return
		case sig := <-sigCh:
			if sig == nil {
				return
			}
			if sig.Name == "org.freedesktop.ScreenSaver.ActiveChanged" ||
				sig.Name == "org.gnome.ScreenSaver.ActiveChanged" {
				if len(sig.Body) < 1 {
					continue
				}
				active, ok := sig.Body[0].(bool)
				if !ok {
					continue
				}
				if active {
					log.Println("screensaver monitor: screen locked/blanked")
					m.events <- DisplayOff
				} else {
					log.Println("screensaver monitor: screen unlocked/unblanked")
					m.events <- DisplayOn
				}
			}
		}
	}
}

// watchLogind listens on the system bus for org.freedesktop.login1.Manager.PrepareForSleep.
// This catches system suspend/resume events.
func (m *Monitor) watchLogind(ctx context.Context) {
	conn, err := dbus.ConnectSystemBus()
	if err != nil {
		log.Printf("logind monitor: cannot connect to system bus: %v", err)
		return
	}
	defer conn.Close()

	rule := "type='signal',interface='org.freedesktop.login1.Manager',member='PrepareForSleep'"
	call := conn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule)
	if call.Err != nil {
		log.Printf("logind monitor: AddMatch failed: %v", call.Err)
		return
	}

	sigCh := make(chan *dbus.Signal, 16)
	conn.Signal(sigCh)

	log.Println("logind monitor: listening for PrepareForSleep signals")

	for {
		select {
		case <-ctx.Done():
			return
		case sig := <-sigCh:
			if sig == nil {
				return
			}
			if sig.Name == "org.freedesktop.login1.Manager.PrepareForSleep" {
				if len(sig.Body) < 1 {
					continue
				}
				preparing, ok := sig.Body[0].(bool)
				if !ok {
					continue
				}
				if preparing {
					log.Println("logind monitor: system preparing to sleep")
					m.events <- DisplayOff
				} else {
					log.Println("logind monitor: system woke up")
					m.events <- DisplayOn
				}
			}
		}
	}
}
