package user

import (
	"backend/dbRequest"
	"backend/models"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"strconv"
	"strings"

	"github.com/gin-gonic/gin"
)

func (um *UserManager) AddRole(c *gin.Context) {
	if !um.databaseOpened(c) {
		return
	}

	body, err := io.ReadAll(c.Request.Body)
	if err != nil {
		c.JSON(http.StatusBadRequest, models.NewJsonErrorResponse(err))
		return
	}

	role := models.Role{}
	err = json.Unmarshal(body, &role)
	if err != nil {
		c.JSON(http.StatusBadRequest, models.NewJsonErrorResponse(err))
		return
	}

	if !role.IsValid() {
		c.JSON(http.StatusBadRequest, models.NewJsonErrorMessageResponse("user empty"))
		return
	}

	var exists bool

	if err := um.database.QueryRow(dbRequest.DBRoleLookup, role.Role).Scan(&exists); dbRequest.CheckDBError(c, role.Role, err) {
		return
	}

	if exists {
		c.JSON(http.StatusOK, models.NewJsonErrorMessageResponse(fmt.Sprintf("role '%s' exists already", role.Role)))
		return
	}

	jsonBytes, err := json.Marshal(role.Rights)
	if err != nil {
		c.JSON(http.StatusOK, models.NewJsonErrorMessageResponse(err.Error()))
		return
	}

	if _, err := um.database.Exec(dbRequest.NewRole, role.Role, string(jsonBytes)); dbRequest.CheckDBError(c, role.Role, err) {
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"message": fmt.Sprintf("role '%s' successfully added", role.Role),
	})
}

func (um *UserManager) GetRoleById(c *gin.Context) {
	if !um.databaseOpened(c) {
		return
	}

	if _, err := um.database.Exec(dbRequest.CreateRoleTable); err != nil {
		c.JSON(http.StatusBadRequest, models.NewJsonErrorResponse(err))
		return
	}

	var i int
	var err error

	id := c.Query("id")
	if id != "" {
		i, err = strconv.Atoi(id)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{
				"message": err.Error(),
			})
			return
		}
	}

	query := `SELECT id, role, rights FROM roles`
	var args any
	if i > 0 {
		query = `
		SELECT  id, role, rights  FROM users
		WHERE id = ? 
	`
		args = i
	}

	rows, err := um.database.Query(query, args)
	if err != nil {
		return
	}
	defer rows.Close()

	var roles []models.Role

	for rows.Next() {
		var id int
		var role, rightsString string
		if err = rows.Scan(&id, &role, &rightsString); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{
				"message": err.Error(),
			})
			return
		}

		var data struct {
			Rights []models.Rights `json:"rights"`
		}
		err := json.Unmarshal([]byte(rightsString), &data)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{
				"message": err.Error(),
			})
			return
		}

		roles = append(roles, models.Role{
			Id:     id,
			Role:   role,
			Rights: data.Rights,
		})
	}

	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"message": err.Error(),
		})
		return
	}
	c.JSON(http.StatusOK, roles)
}

func (um *UserManager) UpdateRole(c *gin.Context) {
	if !um.databaseOpened(c) {
		return
	}

	body, err := io.ReadAll(c.Request.Body)
	if err != nil {
		c.JSON(http.StatusBadRequest, models.NewJsonErrorResponse(err))
		return
	}

	role := models.Role{}
	err = json.Unmarshal(body, &role)
	if err != nil {
		c.JSON(http.StatusBadRequest, models.NewJsonErrorResponse(err))
		return
	}

	jsonBytes, err := json.Marshal(role)
	if err != nil {
		c.JSON(http.StatusOK, models.NewJsonErrorMessageResponse(err.Error()))
		return
	}

	if _, err := um.database.Exec(dbRequest.DBUpdateRole, string(jsonBytes), role.Role); err != nil {
		c.JSON(http.StatusBadRequest, models.NewJsonErrorResponse(err))
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"message": fmt.Sprintf("role rights '%s' successfully updated", role.Role),
	})
}

func (um *UserManager) DeleteRole(c *gin.Context) {
	if !um.databaseOpened(c) {
		return
	}

	queryRole := c.Query("role")

	if queryRole == "" || queryRole == "null" || queryRole == "undefined" {
		c.JSON(http.StatusBadRequest, gin.H{
			"message": "role query missing or wrong value: " + queryRole,
		})
		return
	}

	var request struct {
		Roles []string `json:"roles"`
	}

	err := c.BindJSON(&request)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"message": err.Error(),
		})
		return
	}

	if len(request.Roles) == 0 {
		c.JSON(http.StatusBadRequest, gin.H{
			"message": "no roles given to be deleted",
		})
		return
	}

	var ownRole string
	placeholders := make([]string, len(request.Roles))
	args := make([]any, len(request.Roles))
	for i, role := range request.Roles {
		if ownRole == role {
			ownRole = queryRole
			continue
		}

		placeholders[i] = "?"
		args[i] = role
	}

	query := fmt.Sprintf("DELETE FROM roles WHERE role IN (%s)", strings.Join(placeholders, ","))

	_, err = um.database.Exec(query, args...)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"message": err.Error(),
		})
		return
	}

	if ownRole != "" {
		c.JSON(http.StatusBadRequest, gin.H{
			"message": "can not delete logged in role id: " + ownRole,
			"role":    ownRole,
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"message": "role(s) deleted",
	})
}
