package controllers

import (
	"GoMembership/internal/config"
	"GoMembership/internal/constants"
	"GoMembership/internal/middlewares"
	"GoMembership/internal/models"
	"GoMembership/internal/services"
	"GoMembership/internal/utils"
	"GoMembership/internal/validation"
	"fmt"
	"strings"

	"net/http"

	"github.com/gin-gonic/gin"

	"GoMembership/pkg/errors"
	"GoMembership/pkg/logger"
)

type UserController struct {
	Service            services.UserServiceInterface
	EmailService       *services.EmailService
	ConsentService     services.ConsentServiceInterface
	BankAccountService services.BankAccountServiceInterface
	MembershipService  services.MembershipServiceInterface
	LicenceService     services.LicenceInterface
}

type RegistrationData struct {
	User models.User `json:"user"`
}

func (uc *UserController) CurrentUserHandler(c *gin.Context) {
	requestUser, err := uc.ExtractUserFromContext(c)
	if err != nil {
		utils.RespondWithError(c, err, "Error extracting user from context in CurrentUserHandler", http.StatusBadRequest, errors.Responses.Fields.User, errors.Responses.Keys.NoAuthToken)
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"user": requestUser.Safe(),
	})
}

func (uc *UserController) GetAllUsers(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.View) {
		utils.RespondWithError(c, errors.ErrNotAuthorized, fmt.Sprintf("Not allowed to handle all users. RoleID(%v)<Privilige(%v)", requestUser.RoleID, constants.Priviliges.View), http.StatusForbidden, errors.Responses.Fields.User, errors.Responses.Keys.Unauthorized)
		return
	}

	users, err := uc.Service.GetUsers(nil)
	if err != nil {
		utils.RespondWithError(c, err, "Error getting users in GetAllUsers", http.StatusInternalServerError, errors.Responses.Fields.User, errors.Responses.Keys.InternalServerError)
		return
	}

	// Create a slice to hold the safe user representations
	safeUsers := make([]map[string]interface{}, len(*users))

	// Convert each user to its safe representation
	for i, user := range *users {
		safeUsers[i] = user.Safe()
	}
	c.JSON(http.StatusOK, gin.H{
		"users": users,
	})
}

func (uc *UserController) UpdateHandler(c *gin.Context) {
	// 1. Extract and validate the user ID from the route
	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
	}

	var updateData RegistrationData
	if err := c.ShouldBindJSON(&updateData); err != nil {
		utils.HandleValidationError(c, err)
		return
	}
	user := updateData.User

	if !utils.HasPrivilige(requestUser, constants.Priviliges.Update) && user.ID != requestUser.ID {
		utils.RespondWithError(c, errors.ErrNotAuthorized, "Not allowed to update user", http.StatusForbidden, errors.Responses.Fields.User, errors.Responses.Keys.Unauthorized)
		return
	}
	existingUser, err := uc.Service.GetUserByID(user.ID)
	if err != nil {
		utils.RespondWithError(c, err, "Error finding an existing user", http.StatusNotFound, errors.Responses.Fields.User, errors.Responses.Keys.NotFound)
		return
	}
	user.MembershipID = existingUser.MembershipID
	user.Membership.ID = existingUser.Membership.ID
	if existingUser.Licence != nil {
		user.Licence.ID = existingUser.Licence.ID
	}
	user.LicenceID = existingUser.LicenceID
	user.BankAccount.ID = existingUser.BankAccount.ID
	user.BankAccountID = existingUser.BankAccountID

	if requestUser.RoleID <= constants.Priviliges.View {
		// deleting existing Users Password to prevent it from being recognized as changed in any case. (Incoming Password is empty if not changed)
		existingUser.Password = ""
		if err := utils.FilterAllowedStructFields(&user, existingUser, constants.MemberUpdateFields, ""); err != nil {
			if err.Error() == "Not authorized" {
				utils.RespondWithError(c, errors.ErrNotAuthorized, "Trying to update unauthorized fields", http.StatusUnauthorized, errors.Responses.Fields.User, errors.Responses.Keys.Unauthorized)
			} else {
				utils.RespondWithError(c, err, "Error filtering users input fields", http.StatusInternalServerError, errors.Responses.Fields.User, errors.Responses.Keys.InternalServerError)
			}
			return
		}
	}

	updatedUser, err := uc.Service.UpdateUser(&user)
	if err != nil {
		utils.HandleUserUpdateError(c, err)
		return
	}

	logger.Info.Printf("User %d updated successfully by user %d", updatedUser.ID, requestUser.ID)

	c.JSON(http.StatusAccepted, gin.H{"message": "User updated successfully", "user": updatedUser.Safe()})
}

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

	requestUser, err := uc.ExtractUserFromContext(c)
	if err != nil {
		utils.RespondWithError(c, err, "Error extracting user from context in DeleteUser", http.StatusBadRequest, errors.Responses.Fields.User, errors.Responses.Keys.NoAuthToken)
		return
	}

	type deleteData struct {
		User struct {
			ID       uint   `json:"id" binding:"required,numeric"`
			LastName string `json:"last_name"`
		} `json:"user"`
	}

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

	if !utils.HasPrivilige(requestUser, constants.Priviliges.Delete) && data.User.ID != requestUser.ID {
		utils.RespondWithError(c, errors.ErrNotAuthorized, "Not allowed to delete user", http.StatusForbidden, errors.Responses.Fields.User, errors.Responses.Keys.Unauthorized)
		return
	}

	logger.Error.Printf("Deleting user: %v", data.User)
	if err := uc.Service.DeleteUser(data.User.LastName, data.User.ID); err != nil {
		utils.HandleDeleteUserError(c, err)
		return
	}

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

func (uc *UserController) ExtractUserFromContext(c *gin.Context) (*models.User, error) {

	tokenString, err := c.Cookie("jwt")
	if err != nil {
		return nil, err
	}
	_, claims, err := middlewares.ExtractContentFrom(tokenString)
	if err != nil {
		return nil, err
	}
	jwtUserID := uint((*claims)["user_id"].(float64))
	user, err := uc.Service.GetUserByID(jwtUserID)
	if err != nil {
		return nil, err
	}
	return user, nil
}

func (uc *UserController) LogoutHandler(c *gin.Context) {
	tokenString, err := c.Cookie("jwt")
	if err != nil {
		logger.Error.Printf("unable to get token from cookie: %#v", err)
	}

	middlewares.InvalidateSession(tokenString)

	c.SetCookie("jwt", "", -1, "/", "", true, true)
	c.JSON(http.StatusOK, gin.H{"message": "Logged out successfully"})
}

func (uc *UserController) LoginHandler(c *gin.Context) {
	var input struct {
		Email    string `json:"email"`
		Password string `json:"password"`
	}

	if err := c.ShouldBindJSON(&input); err != nil {
		utils.RespondWithError(c, err, "Invalid JSON or malformed request", http.StatusBadRequest, errors.Responses.Fields.General, errors.Responses.Keys.Invalid)
		return
	}

	user, err := uc.Service.GetUserByEmail(input.Email)
	if err != nil {
		utils.RespondWithError(c, err, "Login Error; user not found", http.StatusNotFound,
			errors.Responses.Fields.Login,
			errors.Responses.Keys.UserNotFoundWrongPassword)
		return
	}

	if user.Status <= constants.DisabledStatus {
		utils.RespondWithError(c, fmt.Errorf("User banned from login %v %v", user.FirstName, user.LastName),
			"Login Error; user is disabled",
			http.StatusNotAcceptable,
			errors.Responses.Fields.Login,
			errors.Responses.Keys.UserDisabled)
		return
	}

	ok, err := user.PasswordMatches(input.Password)
	if err != nil {
		utils.RespondWithError(c, err, "Login Error; password incorrect", http.StatusInternalServerError, errors.Responses.Fields.Login, errors.Responses.Keys.InternalServerError)
		return
	}
	if !ok {
		utils.RespondWithError(c, fmt.Errorf("%v %v(%v)", user.FirstName, user.LastName, user.Email),
			"Login Error; wrong password",
			http.StatusNotAcceptable,
			errors.Responses.Fields.Login,
			errors.Responses.Keys.UserNotFoundWrongPassword)
		return
	}

	logger.Error.Printf("jwtsecret: %v", config.Auth.JWTSecret)
	token, err := middlewares.GenerateToken(config.Auth.JWTSecret, user, "")
	if err != nil {
		utils.RespondWithError(c, err, "Error generating token in LoginHandler", http.StatusInternalServerError, errors.Responses.Fields.Login, errors.Responses.Keys.JwtGenerationFailed)
		return
	}

	utils.SetCookie(c, token)

	c.JSON(http.StatusOK, gin.H{
		"message": "Login successful",
		"user_id": user.ID,
	})
}

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

	var regData RegistrationData
	logger.Error.Printf("registering user...")
	if err := c.ShouldBindJSON(&regData); err != nil {
		utils.HandleValidationError(c, err)
		return
	}

	logger.Info.Printf("Registering user %v", regData.User.Email)
	selectedModel, err := uc.MembershipService.GetSubscriptionByName(&regData.User.Membership.SubscriptionModel.Name)
	if err != nil {
		utils.RespondWithError(c, err, "Error in Registeruser, couldn't get selected model", http.StatusNotFound, errors.Responses.Fields.SubscriptionModel, errors.Responses.Keys.InvalidSubscriptionModel)
		return
	}
	regData.User.Membership.SubscriptionModel = *selectedModel
	if selectedModel.RequiredMembershipField != "" {
		if err := validation.CheckParentMembershipID(regData.User.Membership); err != nil {
			utils.RespondWithError(c, err, "Error in RegisterUser, couldn't check parent membership id", http.StatusBadRequest, errors.Responses.Fields.ParentMemberShipID, errors.Responses.Keys.NotFound)
			return
		}
	}
	if regData.User.Membership.SubscriptionModel.Name == constants.SupporterSubscriptionModelName {
		regData.User.RoleID = constants.Roles.Supporter
	} else {
		regData.User.RoleID = constants.Roles.Member
	}

	// Register User
	id, token, err := uc.Service.RegisterUser(&regData.User)
	if err != nil {
		if strings.Contains(err.Error(), "UNIQUE constraint failed: users.email") {
			utils.RespondWithError(c, err, "Error in RegisterUser, couldn't register user", http.StatusConflict, errors.Responses.Fields.Email, errors.Responses.Keys.Duplicate)
		} else {
			utils.RespondWithError(c, err, "Error in RegisterUser, couldn't register user", http.StatusConflict, errors.Responses.Fields.General, errors.Responses.Keys.InternalServerError)
		}
		return
	}
	regData.User.ID = id

	// if this is a supporter don't send mails and he never did give any consent. So stop here
	if regData.User.RoleID == constants.Roles.Supporter {

		c.JSON(http.StatusCreated, gin.H{
			"message": "Supporter Registration successuful",
			"id":      regData.User.ID,
		})
		return
	}

	// Register Consents
	var consents = [2]models.Consent{
		{
			FirstName:   regData.User.FirstName,
			LastName:    regData.User.LastName,
			Email:       regData.User.Email,
			ConsentType: "TermsOfService",
		},
		{
			FirstName:   regData.User.FirstName,
			LastName:    regData.User.LastName,
			Email:       regData.User.Email,
			ConsentType: "Privacy",
		},
	}
	for _, consent := range consents {
		_, err = uc.ConsentService.RegisterConsent(&consent)
		if err != nil {
			utils.RespondWithError(c, err, "Error in RegisterUser, couldn't register consent", http.StatusInternalServerError, errors.Responses.Fields.General, errors.Responses.Keys.InternalServerError)
			return
		}
	}

	// Send notifications
	if err := uc.EmailService.SendVerificationEmail(&regData.User, &token); err != nil {
		utils.RespondWithError(c, err, "Error in RegisterUser, couldn't send verification email", http.StatusInternalServerError, errors.Responses.Fields.Email, errors.Responses.Keys.UndeliveredVerificationMail)
		// TODO Notify Admin
	}

	// Notify admin of new user registration
	if err := uc.EmailService.SendRegistrationNotification(&regData.User); err != nil {
		logger.Error.Printf("Failed to notify admin of new user(%v) registration: %v", regData.User.Email, err)
		// Proceed without returning error since user registration is successful
		// TODO Notify Admin
	}
	c.JSON(http.StatusCreated, gin.H{
		"message": "Registration successuful",
		"id":      regData.User.ID,
	})
}

func (uc *UserController) VerifyMailHandler(c *gin.Context) {
	token := c.Query("token")
	if token == "" {
		logger.Error.Println("Missing token to verify mail")
		c.HTML(http.StatusBadRequest, "verification_error.html", gin.H{"ErrorMessage": "Missing token"})
		return
	}

	verification, err := uc.Service.VerifyUser(&token, &constants.VerificationTypes.Email)
	if err != nil {
		c.HTML(http.StatusBadRequest, "verification_error.html", gin.H{"ErrorMessage": "Couldn't verify user"})
		return
	}

	user, err := uc.Service.GetUserByID(verification.UserID)
	if err != nil {
		c.HTML(http.StatusBadRequest, "verification_error.html", gin.H{"ErrorMessage": "Internal server error, couldn't verify user"})
		return
	}

	user.Status = constants.VerifiedStatus
	user.Verification = *verification
	user.ID = verification.UserID
	user.Password = ""

	uc.Service.UpdateUser(user)
	logger.Info.Printf("Verified User: %#v", user.Email)

	uc.EmailService.SendWelcomeEmail(user)
	c.HTML(http.StatusOK, "verification_success.html", gin.H{"FirstName": user.FirstName})
}
