package room

import (
	"context"
	"net/http"

	"github.com/rs/zerolog/log"

	"github.com/m1k1o/neko/server/pkg/auth"
	"github.com/m1k1o/neko/server/pkg/types"
	"github.com/m1k1o/neko/server/pkg/utils"
)

type RoomHandler struct {
	sessions types.SessionManager
	desktop  types.DesktopManager
	capture  types.CaptureManager

	privateModeImage []byte
}

func New(
	sessions types.SessionManager,
	desktop types.DesktopManager,
	capture types.CaptureManager,
) *RoomHandler {
	h := &RoomHandler{
		sessions: sessions,
		desktop:  desktop,
		capture:  capture,
	}

	// generate fallback image for private mode when needed
	sessions.OnSettingsChanged(func(session types.Session, new, old types.Settings) {
		if old.PrivateMode && !new.PrivateMode {
			log.Debug().Msg("clearing private mode fallback image")
			h.privateModeImage = nil
			return
		}

		if !old.PrivateMode && new.PrivateMode {
			img := h.desktop.GetScreenshotImage()
			bytes, err := utils.CreateJPGImage(img, 90)
			if err != nil {
				log.Err(err).Msg("could not generate private mode fallback image")
				return
			}

			log.Debug().Msg("using private mode fallback image")
			h.privateModeImage = bytes
		}
	})

	return h
}

func (h *RoomHandler) Route(r types.Router) {
	r.With(auth.AdminsOnly).Route("/settings", func(r types.Router) {
		r.Post("/", h.settingsSet)
		r.Get("/", h.settingsGet)
	})

	r.With(auth.AdminsOnly).Route("/broadcast", func(r types.Router) {
		r.Get("/", h.broadcastStatus)
		r.Post("/start", h.broadcastStart)
		r.Post("/stop", h.broadcastStop)
	})

	r.With(auth.CanAccessClipboardOnly).With(auth.HostsOnly).Route("/clipboard", func(r types.Router) {
		r.Get("/", h.clipboardGetText)
		r.Post("/", h.clipboardSetText)
		r.Get("/image.png", h.clipboardGetImage)

		// TODO: Refactor. xclip is failing to set propper target type
		// and this content is sent back to client as text in another
		// clipboard update. Therefore endpoint is not usable!
		//r.Post("/image", h.clipboardSetImage)

		// TODO: Refactor. If there would be implemented custom target
		// retrieval, this endpoint would be useful.
		//r.Get("/targets", h.clipboardGetTargets)
	})

	r.With(auth.CanHostOnly).Route("/keyboard", func(r types.Router) {
		r.Get("/map", h.keyboardMapGet)
		r.With(auth.HostsOnly).Post("/map", h.keyboardMapSet)

		r.Get("/modifiers", h.keyboardModifiersGet)
		r.With(auth.HostsOnly).Post("/modifiers", h.keyboardModifiersSet)
	})

	r.With(auth.CanHostOnly).Route("/control", func(r types.Router) {
		r.Get("/", h.controlStatus)
		r.Post("/request", h.controlRequest)
		r.Post("/release", h.controlRelease)

		r.With(auth.AdminsOnly).Post("/take", h.controlTake)
		r.With(auth.HostsOrAdminsOnly).Post("/give/{sessionId}", h.controlGive)
		r.With(auth.AdminsOnly).Post("/reset", h.controlReset)
	})

	r.With(auth.CanWatchOnly).Route("/screen", func(r types.Router) {
		r.Get("/", h.screenConfiguration)
		r.With(auth.AdminsOnly).Post("/", h.screenConfigurationChange)
		r.With(auth.AdminsOnly).Get("/configurations", h.screenConfigurationsList)

		r.Get("/cast.jpg", h.screenCastGet)
		r.With(auth.AdminsOnly).Get("/shot.jpg", h.screenShotGet)
	})

	r.With(h.uploadMiddleware).Route("/upload", func(r types.Router) {
		r.Post("/drop", h.uploadDrop)
		r.Post("/dialog", h.uploadDialogPost)
		r.Delete("/dialog", h.uploadDialogClose)
	})

}

func (h *RoomHandler) uploadMiddleware(w http.ResponseWriter, r *http.Request) (context.Context, error) {
	session, ok := auth.GetSession(r)
	if !ok || (!session.IsHost() && (!session.Profile().CanHost || !h.sessions.Settings().ImplicitHosting)) {
		return nil, utils.HttpForbidden("without implicit hosting, only host can upload files")
	}

	return nil, nil
}
