package controllers

import (
	"GoMembership/internal/constants"
	"GoMembership/internal/utils"
	"GoMembership/pkg/errors"
	"fmt"
	"net/http"
	"strconv"

	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/go-playground/validator/v10"
)

func (uc *UserController) CreatePasswordHandler(c *gin.Context) {

	requestUser, err := uc.ExtractUserFromContext(c)
	if err != nil {
		utils.RespondWithError(c, err, "Error extracting user from context in UpdateHandler", http.StatusBadRequest, errors.Responses.Fields.User, errors.Responses.Keys.NoAuthToken)
		return
	}
	if !utils.HasPrivilige(requestUser, constants.Priviliges.AccessControl) {
		utils.RespondWithError(c, errors.ErrNotAuthorized, fmt.Sprintf("Not allowed to handle other users. RoleID(%v)<Privilige(%v)", requestUser.RoleID, constants.Priviliges.View), http.StatusForbidden, errors.Responses.Fields.User, errors.Responses.Keys.Unauthorized)
		return
	}

	// Expected data from the user
	var input struct {
		User struct {
			ID uint `json:"id" binding:"required,numeric"`
		} `json:"user"`
	}

	if err := c.ShouldBindJSON(&input); err != nil {
		utils.HandleValidationError(c, err)
		return
	}

	// find user
	db_user, err := uc.Service.GetUserByID(input.User.ID)
	if err != nil {
		utils.RespondWithError(c, err, "couldn't get user by id", http.StatusNotFound, errors.Responses.Fields.User, errors.Responses.Keys.NotFound)
		return
	}

	// create token
	token, err := uc.Service.HandlePasswordChangeRequest(db_user)
	if err != nil {
		utils.RespondWithError(c, err, "couldn't handle password change request", http.StatusInternalServerError, errors.Responses.Fields.General, errors.Responses.Keys.InternalServerError)
		return
	}

	// send email
	if err := uc.EmailService.SendGrantBackendAccessEmail(db_user, &token); err != nil {
		utils.RespondWithError(c, err, "Couldn't send grant backend access email", http.StatusInternalServerError, errors.Responses.Fields.General, errors.Responses.Keys.InternalServerError)
		return
	}

	c.JSON(http.StatusAccepted, gin.H{
		"message": "password_change_requested",
	})
}

func (uc *UserController) RequestPasswordChangeHandler(c *gin.Context) {

	// Expected data from the user
	var input struct {
		Email string `json:"email" binding:"required,email"`
	}

	if err := c.ShouldBindJSON(&input); err != nil {
		utils.HandleValidationError(c, err)
		return
	}
	// find user
	db_user, err := uc.Service.GetUserByEmail(input.Email)
	if err != nil {
		utils.RespondWithError(c, err, "couldn't get user by email", http.StatusNotFound, errors.Responses.Fields.User, errors.Responses.Keys.NotFound)
		return
	}

	// check if user may change the password
	if db_user.Status <= constants.DisabledStatus {
		utils.RespondWithError(c, errors.ErrNotAuthorized, "User password change request denied, user is disabled", http.StatusForbidden, errors.Responses.Fields.Login, errors.Responses.Keys.UserDisabled)
		return
	}

	// create token
	token, err := uc.Service.HandlePasswordChangeRequest(db_user)
	if err != nil {
		utils.RespondWithError(c, err, "couldn't handle password change request", http.StatusInternalServerError, errors.Responses.Fields.General, errors.Responses.Keys.InternalServerError)
		return
	}

	// send email
	if err := uc.EmailService.SendChangePasswordEmail(db_user, &token); err != nil {
		utils.RespondWithError(c, err, "Couldn't send change password email", http.StatusInternalServerError, errors.Responses.Fields.General, errors.Responses.Keys.InternalServerError)
		return
	}

	c.JSON(http.StatusAccepted, gin.H{
		"message": "password_change_requested",
	})
}

func (uc *UserController) ChangePassword(c *gin.Context) {
	// Expected data from the user
	var input struct {
		Password string `json:"password" binding:"required"`
		Token    string `json:"token" binding:"required"`
	}
	userIDint, err := strconv.Atoi(c.Param("id"))
	if err != nil {
		utils.RespondWithError(c, err, "Invalid user ID", http.StatusBadRequest, errors.Responses.Fields.User, errors.Responses.Keys.InvalidUserID)
		return
	}

	if err := c.ShouldBindJSON(&input); err != nil {
		utils.HandleValidationError(c, err)
		return
	}

	verification, err := uc.Service.VerifyUser(&input.Token, &constants.VerificationTypes.Password)
	if err != nil || uint(userIDint) != verification.UserID {
		utils.HandleVerifyUserError(c, err)
		return
	}

	user, err := uc.Service.GetUserByID(verification.UserID)
	if err != nil {
		utils.RespondWithError(c, err, "Couldn't find user", http.StatusNotFound, errors.Responses.Fields.User, errors.Responses.Keys.UserNotFoundWrongPassword)
		return
	}

	user.Status = constants.ActiveStatus
	user.Verification = *verification
	user.ID = verification.UserID
	user.Password = input.Password

	// Get Gin's binding validator engine with all registered validators
	validate := binding.Validator.Engine().(*validator.Validate)

	// Validate the populated user struct
	if err := validate.Struct(user); err != nil {
		utils.HandleValidationError(c, err)
		return
	}
	_, err = uc.Service.UpdateUser(user)
	if err != nil {
		utils.HandleUserUpdateError(c, err)
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"message": "password_changed",
	})
}
