package services

import (
	"net/http"
	"strings"

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

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

	"time"
)

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

type UserService struct {
	Repo repositories.UserRepositoryInterface
}

func (service *UserService) RegisterUser(user *models.User) (int64, string, error) {
	if err := validateRegistrationData(user); 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)

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

	return id, token, nil
}

func (service *UserService) Update(user *models.User) (int64, string, error) {
	if err := validateRegistrationData(user); 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)

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

	return id, token, nil
}

func (service *UserService) GetUserByID(id int64) (*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) {
	user, err := service.Repo.VerifyUserOfToken(token)
	if err != nil {
		return nil, err
	}
	return user, nil
}

func validateRegistrationData(user *models.User) error {
	validate := validator.New()
	validate.RegisterValidation("age", utils.AgeValidator)
	validate.RegisterValidation("bic", utils.BICValidator)
	validate.RegisterValidation("iban", utils.IBANValidator)
	validate.RegisterValidation("subscriptionModel", utils.SubscriptionModelValidator)
	validate.RegisterValidation("membershipField", utils.ValidateRequiredMembershipField)

	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
}
