package user

import (
	"encoding/base64"

	"git.ex.umbach.dev/Alex/roese-utils/rslogger"
	"git.ex.umbach.dev/Alex/roese-utils/rsutils"
	"git.ex.umbach.dev/LMS/libcore/models"
	"github.com/gofiber/fiber/v2"
	"github.com/google/uuid"
	"github.com/rs/zerolog/log"
	"golang.org/x/crypto/bcrypt"
	"lms.de/backend/modules/database"
	"lms.de/backend/modules/logger"
	"lms.de/backend/modules/structs"
	"lms.de/backend/modules/utils"
)

func UserLogin(c *fiber.Ctx) error {
	// swagger:operation POST /user/auth/login user userLogin
	// ---
	// summary: Login user
	// consumes:
	// - application/json
	// produces:
	// - application/json
	// parameters:
	// - name: body
	//   in: body
	//   schema:
	//     "$ref": "#/definitions/UserLoginRequest"
	// responses:
	//   '200':
	//     description: User logged in successfully
	//     schema:
	//       "$ref": "#/definitions/UserLoginResponse"
	//   '400':
	//     description: Invalid request body
	//   '401':
	//     description: Incorrect password or user deactivated
	//   '500':
	//     description: Failed to login user

	var body structs.UserLoginRequest

	if err := rsutils.BodyParserHelper(c, &body); err != nil {
		return c.SendStatus(fiber.StatusBadRequest)
	}

	decodedPassword, err := base64.StdEncoding.DecodeString(body.Password)

	if err != nil {
		log.Error().Msg("Failed to decode base64 password, err: " + err.Error())
		return c.SendStatus(fiber.StatusBadRequest)
	}

	if passwordValid := utils.IsPasswordLengthValid(string(decodedPassword)); !passwordValid {
		return c.SendStatus(fiber.StatusBadRequest)
	}

	var user models.User

	organizationId := c.Locals("organizationId").(string)

	if err := database.DB.Select("id", "disabled", "password").First(&user, "email = ? AND organization_id = ?", body.Email, organizationId).Error; err != nil {
		logger.AddSystemLog(rslogger.LogTypeError, "Failed to find user with email %s", body.Email)
		return c.SendStatus(fiber.StatusBadRequest)
	}

	if user.Id == "" {
		log.Error().Msg("User not found")
		return c.SendStatus(fiber.StatusBadRequest)
	}

	if err := bcrypt.CompareHashAndPassword([]byte(user.Password), decodedPassword); err != nil {
		log.Error().Msg("Incorrect password")
		return c.SendStatus(fiber.StatusBadRequest)
	}

	if user.Disabled {
		return c.SendStatus(fiber.StatusUnauthorized)
	}

	session, err := rsutils.GenerateSession()

	if err != nil {
		return c.SendStatus(fiber.StatusInternalServerError)
	}

	if err := database.DB.Create(&models.UserSession{
		Id:             uuid.New().String(),
		OrganizationId: organizationId,
		Session:        session,
		UserId:         user.Id,
		UserAgent:      string(c.Context().UserAgent()),
		ExpiresAt:      utils.GetSessionExpiresAtTime()}).Error; err != nil {
		logger.AddSystemLog(rslogger.LogTypeError, "Failed to create user session, err: "+err.Error())
		return c.SendStatus(fiber.StatusInternalServerError)
	}

	logger.AddSystemLog(rslogger.LogTypeInfo, "User %s has logged in", user.Id)

	return c.JSON(structs.UserLoginResponse{Session: session})
}
