// server/backend/internal/httpapi/manage/override.go
package manage

import (
	"context"
	"encoding/json"
	"log/slog"
	"net/http"
	"time"

	"git.az-it.net/az/morz-infoboard/server/backend/internal/mqttnotifier"
	"git.az-it.net/az/morz-infoboard/server/backend/internal/reqcontext"
	"git.az-it.net/az/morz-infoboard/server/backend/internal/store"
)

// globalOverrideStore ist das Interface für Handler-Tests.
type globalOverrideStore interface {
	Get(ctx context.Context) (*store.GlobalOverride, error)
	Upsert(ctx context.Context, overrideType string, until time.Time) error
	Delete(ctx context.Context) error
}

// HandleGetGlobalOverride gibt den aktuellen globalen Override zurück (204 wenn keiner aktiv).
func HandleGetGlobalOverride(overrides globalOverrideStore) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		o, err := overrides.Get(r.Context())
		if err != nil {
			http.Error(w, "internal error", http.StatusInternalServerError)
			return
		}
		if o == nil || time.Now().After(o.Until) {
			w.WriteHeader(http.StatusNoContent)
			return
		}
		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(o) //nolint:errcheck
	}
}

// HandleSetGlobalOverride setzt den globalen Override und schickt sofort MQTT an alle Screens.
// Hinweis: Der Override wird global gespeichert und vom Reconciler auf alle Screens angewendet.
// Über authOnly haben alle eingeloggten Nutzer Zugriff; die sofortigen MQTT-Kommandos gehen
// jedoch nur an ihre zugänglichen Screens. Soll der Zugriff auf Admins beschränkt werden,
// authOnly durch authAdmin ersetzen.
func HandleSetGlobalOverride(overrides globalOverrideStore, screens *store.ScreenStore, notifier *mqttnotifier.Notifier) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		var body struct {
			Type  string    `json:"type"`
			Until time.Time `json:"until"`
		}
		if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
			http.Error(w, "invalid JSON", http.StatusBadRequest)
			return
		}
		if body.Type != "on" && body.Type != "off" {
			http.Error(w, `type must be "on" or "off"`, http.StatusBadRequest)
			return
		}
		if body.Until.IsZero() || !time.Now().Before(body.Until) {
			http.Error(w, "until must be in the future", http.StatusBadRequest)
			return
		}

		if err := overrides.Upsert(r.Context(), body.Type, body.Until); err != nil {
			slog.Error("set global override: upsert failed", "err", err)
			http.Error(w, "internal error", http.StatusInternalServerError)
			return
		}

		// Sofort MQTT an alle zugänglichen Screens schicken (falls screens/notifier vorhanden)
		if screens != nil && notifier != nil {
			u := reqcontext.UserFromContext(r.Context())
			var allScreens []*store.Screen
			if u != nil {
				switch u.Role {
				case "admin":
					allScreens, _ = screens.ListAll(r.Context())
				default:
					allScreens, _ = screens.GetAccessibleScreens(r.Context(), u.ID)
				}
			}
			action := "display_" + body.Type
			for _, sc := range allScreens {
				if err := notifier.SendDisplayCommand(sc.Slug, action); err != nil {
					slog.Warn("set global override: send command failed", "slug", sc.Slug, "err", err)
				}
			}
		}

		w.WriteHeader(http.StatusNoContent)
	}
}

// HandleDeleteGlobalOverride entfernt den globalen Override.
func HandleDeleteGlobalOverride(overrides globalOverrideStore) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if err := overrides.Delete(r.Context()); err != nil {
			slog.Error("delete global override: failed", "err", err)
			http.Error(w, "internal error", http.StatusInternalServerError)
			return
		}
		w.WriteHeader(http.StatusNoContent)
	}
}

// HandleSetScreenOverride setzt oder löscht den per-Screen-Override (on_until: null → löschen).
func HandleSetScreenOverride(screens *store.ScreenStore, schedules *store.ScreenScheduleStore) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		screenSlug := r.PathValue("screenSlug")
		screen, err := screens.GetBySlug(r.Context(), screenSlug)
		if err != nil {
			http.Error(w, "screen not found", http.StatusNotFound)
			return
		}
		if !requireScreenAccess(w, r, screen) {
			return
		}

		var body struct {
			OnUntil *time.Time `json:"on_until"`
		}
		if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
			http.Error(w, "invalid JSON", http.StatusBadRequest)
			return
		}
		if body.OnUntil != nil && !time.Now().Before(*body.OnUntil) {
			http.Error(w, "on_until must be in the future", http.StatusBadRequest)
			return
		}

		if err := schedules.SetOverrideOnUntil(r.Context(), screen.ID, body.OnUntil); err != nil {
			slog.Error("set screen override: db error", "screen_id", screen.ID, "err", err)
			http.Error(w, "internal error", http.StatusInternalServerError)
			return
		}
		w.WriteHeader(http.StatusNoContent)
	}
}
