package routes

import (
	"net/http"
	"strconv"

	"owo.codes/whats-this/api/lib/apierrors"
	"owo.codes/whats-this/api/lib/db"
	"owo.codes/whats-this/api/lib/middleware"
	"owo.codes/whats-this/api/lib/ratelimiter"

	"github.com/go-chi/render"
	"github.com/rs/zerolog/log"
	"github.com/spf13/viper"
)

// Maximum objects per page
const maxOffset = 100

// listObjectsResponse is the response format for ListObjects.
type listObjectsResponse struct {
	Success bool        `json:"success"`
	Data    []db.Object `json:"data"`
}

// ListObjects returns a paginated list of all objects owned by a user.
func ListObjects(w http.ResponseWriter, r *http.Request) {
	// Only authorized users can use this route
	user := middleware.GetAuthorizedUser(r)
	if user.ID == "" || user.IsBlocked {
		panic(apierrors.Unauthorized)
	}

	// Apply ratelimits
	bucket := middleware.GetBucket(r)
	err := bucket.TakeWithHeaders(w, viper.GetInt64("ratelimiter.listObjectsCost"))
	if err == ratelimiter.InsufficientTokens {
		panic(apierrors.InsufficientTokens)
	}
	if err != nil {
		panic(apierrors.InternalServerError)
	}

	// Determine offset and limit information
	query := r.URL.Query()
	l := query.Get("limit")
	limit, err := strconv.Atoi(l)
	if err != nil {
		panic(apierrors.InvalidOffsetOrLimit)
	}
	o := query.Get("offset")
	offset, err := strconv.Atoi(o)
	if err != nil {
		panic(apierrors.InvalidOffsetOrLimit)
	}
	if limit > maxOffset {
		panic(apierrors.OffsetTooLarge)
	}
	asc := false
	if query.Get("order") == "asc" {
		asc = true
	}

	// Get the data
	objects, err := db.ListObjectsByAssociatedUser(user.ID, asc, offset, limit)
	if err != nil {
		log.Error().Err(err).Msg("failed to list objects for user")
		panic(apierrors.InternalServerError)
	}
	associatedWithCurrentUser := true
	for i := 0; i < len(objects); i++ {
		objects[i].AssociatedWithCurrentUser = &associatedWithCurrentUser
	}

	// Return response
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	render.JSON(w, r, listObjectsResponse{true, objects})
}