package users

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/http"

	"git.schreifuchs.ch/schreifuchs/ng-blog/internal/auth"
	"git.schreifuchs.ch/schreifuchs/ng-blog/internal/model"
	"github.com/google/uuid"
	"github.com/gorilla/mux"
	"gorm.io/gorm"
)

// GetUsers retrieves all users from the database and returns them as a JSON response.
func (s *Service) GetUsers(w http.ResponseWriter, r *http.Request) {
	var users []model.User

	err := s.db.Find(&users).Error
	if err != nil {
		log.Printf("Error while getting users: %v", err)
		w.WriteHeader(http.StatusInternalServerError)
	}

	res, err := json.Marshal(&users)
	if err != nil {
		log.Printf("Error while marshaling users: %v", err)
		w.WriteHeader(http.StatusInternalServerError)
	}

	w.Write(res)
}

// SetUserRole handles updating a user's role based on a UUID from the request.
func (s *Service) SetUserRole(w http.ResponseWriter, r *http.Request) {
	var role model.Role
	userUUIDstr, ok := mux.Vars(r)["userUUID"]
	if !ok {
		w.WriteHeader(http.StatusNotFound)
		return
	}
	userUUID, err := uuid.Parse(userUUIDstr)
	if err != nil {
		w.WriteHeader(http.StatusNotFound)
		return
	}
	if err := json.NewDecoder(r.Body).Decode(&role); err != nil {
		fmt.Fprint(w, err.Error())
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	err = s.db.Model(&model.User{}).
		Where("uuid = ?", userUUID).
		Update("role", role).
		Error
	if err != nil {
		log.Printf("Error while update user role: %v", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

// DeleteUser handles the deletion of a user from the database, enforcing authorization checks.
func (s *Service) DeleteUser(w http.ResponseWriter, r *http.Request) {
	claims, ok := auth.ExtractClaims(r.Context())
	if !ok {
		log.Println("Error while extracting claims")
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	userUUIDstr, ok := mux.Vars(r)["userUUID"]
	if !ok {
		w.WriteHeader(http.StatusNotFound)
		return
	}
	userUUID, err := uuid.Parse(userUUIDstr)
	if err != nil {
		w.WriteHeader(http.StatusNotFound)
		return
	}

	if claims.Role != model.RoleAdmin && userUUIDstr != claims.Subject {
		w.WriteHeader(http.StatusForbidden)
		return
	}

	if err = s.db.Where("uuid = ?", userUUID).Delete(&model.User{}).Error; err != nil {

		if errors.Is(err, gorm.ErrCheckConstraintViolated) {
			fmt.Fprint(w, "Username is already in use")
			w.WriteHeader(http.StatusBadRequest)
			return
		}

		log.Printf("Error: %v", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}
