package services

import (
	"net/http"
	"strings"

	"GoMembership/internal/constants"
	"GoMembership/internal/models"
	"GoMembership/internal/repositories"
	"GoMembership/internal/utils"
	"GoMembership/pkg/errors"
	"GoMembership/pkg/logger"

	"github.com/alexedwards/argon2id"
	"github.com/go-playground/validator/v10"
	"gorm.io/gorm"

	"time"
)

type UserServiceInterface interface {
	RegisterUser(user *models.User) (uint, string, error)
	GetUserByEmail(email string) (*models.User, error)
	GetUserByID(id uint) (*models.User, error)
	GetUsers(where map[string]interface{}) (*[]models.User, error)
	VerifyUser(token *string) (*models.User, error)
	UpdateUser(user *models.User, userRole int8) (*models.User, error)
}

type UserService struct {
	Repo repositories.UserRepositoryInterface
}

func (service *UserService) UpdateUser(user *models.User, userRole int8) (*models.User, error) {

	if err := validateUserData(user, userRole); err != nil {
		logger.Error.Printf("Failed to validate user data: %v", err)
		return nil, errors.ErrInvalidUserData
	}

	if user.Password != "" {
		setPassword(user.Password, user)
	}

	user.UpdatedAt = time.Now()

	updatedUser, err := service.Repo.UpdateUser(user)

	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, errors.ErrUserNotFound
		}
		if strings.Contains(err.Error(), "UNIQUE constraint failed") {
			return nil, errors.ErrDuplicateEntry
		}
		return nil, err
	}

	return updatedUser, nil
}

func (service *UserService) RegisterUser(user *models.User) (uint, string, error) {
	if err := validateUserData(user, user.RoleID); err != nil {
		return http.StatusNotAcceptable, "", err
	}

	setPassword(user.Password, user)

	user.Status = constants.UnverifiedStatus
	user.CreatedAt = time.Now()
	user.UpdatedAt = time.Now()
	id, err := service.Repo.CreateUser(user)

	if err != nil && strings.Contains(err.Error(), "UNIQUE constraint failed") {
		return http.StatusConflict, "", err
	} else if err != nil {
		return http.StatusInternalServerError, "", err
	}

	user.ID = id

	token, err := utils.GenerateVerificationToken()
	if err != nil {
		return http.StatusInternalServerError, "", err
	}

	logger.Info.Printf("TOKEN: %v", token)

	// Check if user is already verified
	verified, err := service.Repo.IsVerified(&user.ID)
	if err != nil {
		return http.StatusInternalServerError, "", err
	}
	if verified {
		return http.StatusAlreadyReported, "", errors.ErrAlreadyVerified
	}

	// Prepare the Verification record
	verification := models.Verification{
		UserID:            user.ID,
		VerificationToken: token,
	}

	if _, err = service.Repo.SetVerificationToken(&verification); err != nil {
		return http.StatusInternalServerError, "", err
	}

	return id, token, nil
}

func (service *UserService) GetUserByID(id uint) (*models.User, error) {

	return service.Repo.GetUserByID(&id)
}

func (service *UserService) GetUserByEmail(email string) (*models.User, error) {
	return service.Repo.GetUserByEmail(email)
}

func (service *UserService) GetUsers(where map[string]interface{}) (*[]models.User, error) {
	return service.Repo.GetUsers(where)
}

func (service *UserService) VerifyUser(token *string) (*models.User, error) {
	verification, err := service.Repo.GetVerificationOfToken(token)
	if err != nil {
		return nil, err
	}
	// Check if the user is already verified
	verified, err := service.Repo.IsVerified(&verification.UserID)
	if err != nil {
		return nil, err
	}
	user, err := service.Repo.GetUserByID(&verification.UserID)
	if err != nil {
		return nil, err
	}
	if verified {
		return user, errors.ErrAlreadyVerified
	}
	// Update user status to active
	t := time.Now()
	verification.EmailVerifiedAt = &t

	user.Status = constants.VerifiedStatus
	user.Verification = *verification
	user.ID = verification.UserID
	service.Repo.UpdateUser(user)

	return user, nil
}

func validateUserData(user *models.User, userRole int8) error {
	validate := validator.New()
	validate.RegisterValidation("safe_content", utils.ValidateSafeContent)
	if userRole == constants.Roles.Admin {
		validate.RegisterValidation("membershipField", utils.ValidateToTrue)
		validate.RegisterValidation("age", utils.ValidateToTrue)
		validate.RegisterValidation("bic", utils.ValidateToTrue)
		validate.RegisterValidation("subscriptionModel", utils.ValidateToTrue)
		validate.RegisterValidation("iban", utils.ValidateToTrue)

	} else {
		validate.RegisterValidation("membershipField", utils.ValidateRequiredMembershipField)
		validate.RegisterValidation("age", utils.AgeValidator)
		validate.RegisterValidation("bic", utils.BICValidator)
		validate.RegisterValidation("subscriptionModel", utils.SubscriptionModelValidator)
		validate.RegisterValidation("iban", utils.IBANValidator)
	}
	return validate.Struct(user)
}

func setPassword(plaintextPassword string, u *models.User) error {
	hash, err := argon2id.CreateHash(plaintextPassword, argon2id.DefaultParams)
	if err != nil {
		return err
	}
	u.Password = hash
	return nil
}
