package httpapi

import (
	"log"
	"net/http"

	"git.az-it.net/az/morz-infoboard/server/backend/internal/config"
	"git.az-it.net/az/morz-infoboard/server/backend/internal/httpapi/manage"
	"git.az-it.net/az/morz-infoboard/server/backend/internal/httpapi/tenant"
	"git.az-it.net/az/morz-infoboard/server/backend/internal/mqttnotifier"
	"git.az-it.net/az/morz-infoboard/server/backend/internal/store"
)

// RouterDeps holds all dependencies needed to build the HTTP router.
type RouterDeps struct {
	StatusStore     playerStatusStore
	TenantStore     *store.TenantStore
	ScreenStore     *store.ScreenStore
	MediaStore      *store.MediaStore
	PlaylistStore   *store.PlaylistStore
	AuthStore       *store.AuthStore
	Notifier        *mqttnotifier.Notifier
	ScreenshotStore *ScreenshotStore
	ScheduleStore       *store.ScreenScheduleStore
	GlobalOverrideStore *store.GlobalOverrideStore
	Config              config.Config
	UploadDir       string
	Logger          *log.Logger
}

func NewRouter(deps RouterDeps) http.Handler {
	mux := http.NewServeMux()

	// ── Health ───────────────────────────────────────────────────────────
	mux.HandleFunc("GET /healthz", func(w http.ResponseWriter, _ *http.Request) {
		writeJSON(w, http.StatusOK, map[string]string{
			"status":  "ok",
			"service": "morz-infoboard-backend",
		})
	})

	// ── Root redirect ────────────────────────────────────────────────────
	mux.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path != "/" {
			http.NotFound(w, r)
			return
		}
		http.Redirect(w, r, "/login", http.StatusSeeOther)
	})

	// ── Status / diagnostic UI ───────────────────────────────────────────
	mux.HandleFunc("GET /status", handleStatusPage(deps.StatusStore))
	mux.HandleFunc("GET /status/{screenId}", handleScreenDetailPage(deps.StatusStore))

	// ── API meta ─────────────────────────────────────────────────────────
	mux.HandleFunc("GET /api/v1", func(w http.ResponseWriter, _ *http.Request) {
		writeJSON(w, http.StatusOK, map[string]any{
			"name":    "morz-infoboard-backend",
			"version": "dev",
			"tools": []string{
				"message-wall-resolve",
				"screen-status-list",
				"screen-status-detail",
				"player-status-ingest",
				"screen-status-delete",
			},
		})
	})
	mux.HandleFunc("GET /api/v1/meta", handleMeta)

	// ── Player status (existing) ──────────────────────────────────────────
	mux.HandleFunc("POST /api/v1/player/status", handlePlayerStatus(deps.StatusStore, deps.ScreenStore, deps.Config.MQTTBroker, deps.Config.MQTTUsername, deps.Config.MQTTPassword))
	mux.HandleFunc("POST /api/v1/player/screenshot", handlePlayerScreenshot(deps.ScreenshotStore))
	mux.HandleFunc("GET /api/v1/screens/status", handleListLatestPlayerStatuses(deps.StatusStore))
	mux.HandleFunc("GET /api/v1/screens/{screenId}/status", handleGetLatestPlayerStatus(deps.StatusStore))
	mux.HandleFunc("DELETE /api/v1/screens/{screenId}/status", handleDeletePlayerStatus(deps.StatusStore))

	// ── Message wall ──────────────────────────────────────────────────────
	mux.HandleFunc("POST /api/v1/tools/message-wall/resolve", handleResolveMessageWall)

	// ── Playlist management — only register if stores are wired up ────────
	if deps.TenantStore != nil {
		registerManageRoutes(mux, deps)
	}

	return mux
}

func registerManageRoutes(mux *http.ServeMux, d RouterDeps) {
	uploadDir := d.UploadDir
	if uploadDir == "" {
		uploadDir = "/tmp/morz-uploads"
	}

	// Ensure notifier is never nil inside handlers (no-op when broker not configured).
	notifier := d.Notifier
	if notifier == nil {
		notifier = mqttnotifier.New("", "", "")
	}

	// Serve uploaded files. N5: Directory-Listing deaktiviert via neuteredFileSystem.
	mux.Handle("GET /uploads/", http.StripPrefix("/uploads/", http.FileServer(neuteredFileSystem{http.Dir(uploadDir)})))

	// Serve embedded static assets (Bulma CSS, SortableJS) — no external CDN needed.
	mux.HandleFunc("GET /static/bulma.min.css", manage.HandleStaticBulmaCSS())
	mux.HandleFunc("GET /static/Sortable.min.js", manage.HandleStaticSortableJS())

	// K1: CSRF-Schutz für alle state-ändernden Routen.
	csrf := CSRFProtect(d.Config.DevMode)

	// K1: Setzt den CSRF-Cookie bei GET-Requests, damit das JS-Inject-Script ihn lesen kann.
	setCSRF := func(h http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Method == http.MethodGet {
				SetCSRFCookie(w, r, d.Config.DevMode)
			}
			h.ServeHTTP(w, r)
		})
	}

	// ── Auth (no auth middleware required) ────────────────────────────────
	// K1: GET /login setzt CSRF-Cookie; POST /login und POST /logout werden per CSRF geprüft.
	mux.Handle("GET /login", http.HandlerFunc(manage.HandleLoginUI(d.AuthStore, d.ScreenStore, d.Config)))
	// N1: Rate-Limiting auf /login (max. 5 Versuche/Minute pro IP).
	mux.Handle("POST /login", RateLimitLogin(csrf(http.HandlerFunc(manage.HandleLoginPost(d.AuthStore, d.ScreenStore, d.Config)))))
	mux.Handle("POST /logout", csrf(http.HandlerFunc(manage.HandleLogoutPost(d.AuthStore, d.Config))))

	// Shorthand middleware combinators for this router.
	// Für GET-Routen: setCSRF setzt den Cookie; für POST-Routen: csrf validiert ihn.
	authOnly := func(h http.Handler) http.Handler {
		return chain(h, RequireAuth(d.AuthStore), setCSRF, csrf)
	}
	authAdmin := func(h http.Handler) http.Handler {
		return chain(h, RequireAuth(d.AuthStore), RequireAdmin, setCSRF, csrf)
	}
	authTenant := func(h http.Handler) http.Handler {
		return chain(h, RequireAuth(d.AuthStore), RequireTenantAccess, setCSRF, csrf)
	}
	// authScreen: wie authOnly, aber zusätzlich Screen-Zugriffsprüfung für screen_user.
	// Admins und Tenant-User werden von RequireScreenAccess durchgelassen.
	authScreen := func(h http.Handler) http.Handler {
		return chain(h, RequireAuth(d.AuthStore), RequireScreenAccess(d.ScreenStore), setCSRF, csrf)
	}
	// authScreenControl: wie authScreen, aber restricted-User werden mit 403 blockiert.
	// Für Endpunkte, die restricted-User nicht nutzen dürfen (Display, Zeitplan, Override).
	authScreenControl := func(h http.Handler) http.Handler {
		return chain(h, RequireAuth(d.AuthStore), RequireScreenAccess(d.ScreenStore), RequireNotRestricted, setCSRF, csrf)
	}
	// authOnlyControl: wie authOnly, aber restricted-User werden mit 403 blockiert.
	// Für globalen Override (kein spezifischer Screen).
	authOnlyControl := func(h http.Handler) http.Handler {
		return chain(h, RequireAuth(d.AuthStore), RequireNotRestricted, setCSRF, csrf)
	}

	// ── Admin UI ──────────────────────────────────────────────────────────
	mux.Handle("GET /admin",
		authAdmin(http.HandlerFunc(manage.HandleAdminUI(d.TenantStore, d.ScreenStore, d.AuthStore, d.Config))))
	mux.Handle("POST /admin/screens/provision",
		authAdmin(http.HandlerFunc(manage.HandleProvisionUI(d.TenantStore, d.ScreenStore))))
	mux.Handle("POST /admin/screens",
		authAdmin(http.HandlerFunc(manage.HandleCreateScreenUI(d.TenantStore, d.ScreenStore))))
	mux.Handle("POST /admin/screens/{screenId}/delete",
		authAdmin(http.HandlerFunc(manage.HandleDeleteScreenUI(d.ScreenStore))))

	// ── Screen-User-Verwaltung (nur Admin) ────────────────────────────────
	mux.Handle("POST /admin/users",
		authAdmin(http.HandlerFunc(manage.HandleCreateScreenUser(d.AuthStore))))
	mux.Handle("POST /admin/users/{userID}/delete",
		authAdmin(http.HandlerFunc(manage.HandleDeleteScreenUser(d.AuthStore))))
	mux.Handle("POST /admin/screens/{screenID}/users",
		authAdmin(http.HandlerFunc(manage.HandleAddUserToScreen(d.ScreenStore))))
	mux.Handle("POST /admin/screens/{screenID}/users/{userID}/remove",
		authAdmin(http.HandlerFunc(manage.HandleRemoveUserFromScreen(d.ScreenStore))))

	// ── Playlist management UI ────────────────────────────────────────────
	// authScreen enforces that screen_user only accesses their permitted screens.
	mux.Handle("GET /manage",
		authOnly(http.HandlerFunc(manage.HandleScreenOverview(d.ScreenStore, d.ScheduleStore, d.GlobalOverrideStore, notifier, d.Config))))
	mux.Handle("GET /manage/{screenSlug}",
		authScreen(http.HandlerFunc(manage.HandleManageUI(d.TenantStore, d.ScreenStore, d.ScheduleStore, d.MediaStore, d.PlaylistStore, d.Config, notifier))))
	mux.Handle("POST /manage/{screenSlug}/upload",
		authScreen(http.HandlerFunc(manage.HandleUploadMediaUI(d.MediaStore, d.ScreenStore, uploadDir))))
	mux.Handle("POST /manage/{screenSlug}/items",
		authScreen(http.HandlerFunc(manage.HandleAddItemUI(d.PlaylistStore, d.MediaStore, d.ScreenStore, notifier))))
	mux.Handle("POST /manage/{screenSlug}/items/{itemId}",
		authScreen(http.HandlerFunc(manage.HandleUpdateItemUI(d.PlaylistStore, d.ScreenStore, notifier))))
	mux.Handle("POST /manage/{screenSlug}/items/{itemId}/delete",
		authScreen(http.HandlerFunc(manage.HandleDeleteItemUI(d.PlaylistStore, d.ScreenStore, notifier))))
	mux.Handle("POST /manage/{screenSlug}/reorder",
		authScreen(http.HandlerFunc(manage.HandleReorderUI(d.PlaylistStore, d.ScreenStore, notifier))))
	mux.Handle("POST /manage/{screenSlug}/media/{mediaId}/delete",
		authScreen(http.HandlerFunc(manage.HandleDeleteMediaUI(d.MediaStore, d.ScreenStore, uploadDir, notifier))))

	// ── Screenshot API ────────────────────────────────────────────────────
	mux.Handle("GET /api/v1/screens/{screenId}/screenshot",
		authOnly(http.HandlerFunc(handleGetScreenshot(d.ScreenshotStore))))

	// ── Display control ───────────────────────────────────────────────────
	mux.Handle("POST /api/v1/screens/{screenSlug}/display",
		authScreenControl(http.HandlerFunc(manage.HandleDisplayCommand(notifier))))

	// ── Schedule control ──────────────────────────────────────────────────
	mux.Handle("POST /api/v1/screens/{screenSlug}/schedule",
		authScreenControl(http.HandlerFunc(manage.HandleUpdateSchedule(d.ScreenStore, d.ScheduleStore))))

	// ── Globaler Override ────────────────────────────────────────────────
	mux.Handle("GET /api/v1/global-override",
		authOnly(http.HandlerFunc(manage.HandleGetGlobalOverride(d.GlobalOverrideStore))))
	mux.Handle("POST /api/v1/global-override",
		authOnlyControl(http.HandlerFunc(manage.HandleSetGlobalOverride(d.GlobalOverrideStore, d.ScreenStore, notifier))))
	mux.Handle("DELETE /api/v1/global-override",
		authOnlyControl(http.HandlerFunc(manage.HandleDeleteGlobalOverride(d.GlobalOverrideStore))))

	// ── Per-Screen Override ───────────────────────────────────────────────
	mux.Handle("POST /api/v1/screens/{screenSlug}/override",
		authScreenControl(http.HandlerFunc(manage.HandleSetScreenOverride(d.ScreenStore, d.ScheduleStore))))

	// ── JSON API — screens ────────────────────────────────────────────────
	// Self-registration: no auth (player calls this on startup).
	mux.HandleFunc("POST /api/v1/screens/register",
		manage.HandleRegisterScreen(d.TenantStore, d.ScreenStore, d.Config))
	mux.Handle("GET /api/v1/tenants/{tenantSlug}/screens",
		authTenant(http.HandlerFunc(manage.HandleListScreens(d.TenantStore, d.ScreenStore))))
	mux.Handle("POST /api/v1/tenants/{tenantSlug}/screens",
		authTenant(http.HandlerFunc(manage.HandleCreateScreen(d.TenantStore, d.ScreenStore))))

	// ── JSON API — media ──────────────────────────────────────────────────
	mux.Handle("GET /api/v1/tenants/{tenantSlug}/media",
		authTenant(http.HandlerFunc(manage.HandleListMedia(d.TenantStore, d.MediaStore))))
	mux.Handle("POST /api/v1/tenants/{tenantSlug}/media",
		authTenant(http.HandlerFunc(manage.HandleUploadMedia(d.TenantStore, d.MediaStore, uploadDir))))
	mux.Handle("DELETE /api/v1/media/{id}",
		authOnly(http.HandlerFunc(manage.HandleDeleteMedia(d.MediaStore, uploadDir))))

	// ── JSON API — playlists ──────────────────────────────────────────────
	// Player fetches its playlist — no auth required.
	mux.HandleFunc("GET /api/v1/screens/{screenId}/playlist",
		manage.HandlePlayerPlaylist(d.ScreenStore, d.PlaylistStore))
	mux.Handle("GET /api/v1/playlists/{screenId}",
		authOnly(http.HandlerFunc(manage.HandleGetPlaylist(d.ScreenStore, d.PlaylistStore))))
	mux.Handle("POST /api/v1/playlists/{playlistId}/items",
		authOnly(http.HandlerFunc(manage.HandleAddItem(d.PlaylistStore, d.MediaStore, notifier))))
	mux.Handle("PATCH /api/v1/items/{itemId}",
		authOnly(http.HandlerFunc(manage.HandleUpdateItem(d.PlaylistStore, notifier))))
	mux.Handle("DELETE /api/v1/items/{itemId}",
		authOnly(http.HandlerFunc(manage.HandleDeleteItem(d.PlaylistStore, notifier))))
	mux.Handle("PUT /api/v1/playlists/{playlistId}/order",
		authOnly(http.HandlerFunc(manage.HandleReorder(d.PlaylistStore, notifier))))
	mux.Handle("PATCH /api/v1/playlists/{playlistId}/duration",
		authOnly(http.HandlerFunc(manage.HandleUpdatePlaylistDuration(d.PlaylistStore))))

	// ── Tenant self-service dashboard ─────────────────────────────────────
	mux.Handle("GET /tenant/{tenantSlug}/dashboard",
		authTenant(http.HandlerFunc(tenant.HandleTenantDashboard(d.TenantStore, d.ScreenStore, d.MediaStore, d.Config))))
	mux.Handle("POST /tenant/{tenantSlug}/upload",
		authTenant(http.HandlerFunc(tenant.HandleTenantUpload(d.TenantStore, d.MediaStore, uploadDir))))
	mux.Handle("POST /tenant/{tenantSlug}/media/{mediaId}/delete",
		authTenant(http.HandlerFunc(tenant.HandleTenantDeleteMedia(d.TenantStore, d.MediaStore, uploadDir))))
}
