// Package displaycontroller steuert das physische Display per xset DPMS.
package displaycontroller

import (
	"fmt"
	"log/slog"
	"os/exec"
	"sync"
)

// Controller führt xset-Befehle aus und verfolgt den letzten Display-Zustand.
// onStateChange wird nach jeder erfolgreichen Ausführung mit dem neuen Zustand
// aufgerufen (z. B. um ihn per MQTT zu publizieren). Darf nil sein.
type Controller struct {
	display       string
	screenSlug    string
	onStateChange func(screenSlug, state string)
	mu            sync.Mutex
	currentState  string
}

// New erstellt einen Controller. display ist der Wert der DISPLAY-Env-Var (z. B. ":0").
func New(display, screenSlug string, onStateChange func(screenSlug, state string)) *Controller {
	return &Controller{
		display:       display,
		screenSlug:    screenSlug,
		onStateChange: onStateChange,
		currentState:  "unknown",
	}
}

// Execute führt eine Display-Aktion aus. Bekannte Aktionen: "display_on", "display_off".
func (c *Controller) Execute(action string) {
	switch action {
	case "display_on":
		if err := c.runXset("on"); err != nil {
			slog.Error("display_on failed", "err", err)
			return
		}
		c.setState("on")
	case "display_off":
		if err := c.runXset("off"); err != nil {
			slog.Error("display_off failed", "err", err)
			return
		}
		c.setState("off")
	default:
		slog.Warn("unknown display action", "action", action)
	}
}

// State gibt den zuletzt gesetzten Display-Zustand zurück: "on", "off" oder "unknown".
func (c *Controller) State() string {
	c.mu.Lock()
	defer c.mu.Unlock()
	return c.currentState
}

func (c *Controller) runXset(arg string) error {
	out, err := exec.Command("xset", "-display", c.display, "dpms", "force", arg).CombinedOutput()
	if err != nil {
		return fmt.Errorf("xset dpms force %s: %w (output: %s)", arg, err, out)
	}
	return nil
}

func (c *Controller) setState(state string) {
	c.mu.Lock()
	c.currentState = state
	c.mu.Unlock()
	if c.onStateChange != nil {
		go c.onStateChange(c.screenSlug, state)
	}
}
