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"
	"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) (*models.User, error)
	DeleteUser(lastname string, id uint) error
}

type UserService struct {
	Repo     repositories.UserRepositoryInterface
	Licences repositories.LicenceInterface
}

func (service *UserService) DeleteUser(lastname string, id uint) error {
	if id == 0 || lastname == "" {
		return errors.ErrNoData
	}

	user, err := service.GetUserByID(id)
	if err != nil {
		return err
	}
	if user == nil {
		return errors.ErrUserNotFound
	}

	return service.Repo.DeleteUser(id)
}

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

	if user.ID == 0 {
		return nil, errors.ErrUserNotFound
	}

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

	// Validate subscription model
	selectedModel, err := repositories.GetSubscriptionByName(&user.Membership.SubscriptionModel.Name)
	if err != nil {
		return nil, errors.ErrSubscriptionNotFound
	}
	user.Membership.SubscriptionModel = *selectedModel
	user.Membership.SubscriptionModelID = selectedModel.ID

	existingUser, err := service.GetUserByID(user.ID)
	if err != nil {
		return nil, err
	}

	user.Membership.ID = existingUser.Membership.ID

	user.MembershipID = existingUser.MembershipID
	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 user.Licence.Status == 0 {
	// 	// This is a new drivers licence
	// 	user.Licence.Status = constants.UnverifiedStatus
	// }

	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) {

	setPassword(user.Password, user)

	user.Status = constants.UnverifiedStatus
	user.CreatedAt = time.Now()
	user.UpdatedAt = time.Now()
	user.PaymentStatus = constants.AwaitingPaymentStatus
	// if user.Licence == nil {
	// 	user.Licence = &models.Licence{Status: constants.UnverifiedStatus}
	// }
	user.BankAccount.MandateDateSigned = time.Now()
	id, err := service.Repo.CreateUser(user)

	if err != nil {
		return 0, "", err
	}

	user.ID = id

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

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

	// Check if user is already verified
	verified, err := service.Repo.IsVerified(&user.ID)
	if err != nil {
		return 0, "", err
	}
	if verified {
		return 0, "", 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 repositories.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) {
	if where == nil {
		where = map[string]interface{}{}
	}
	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 := repositories.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 setPassword(plaintextPassword string, u *models.User) error {
	hash, err := argon2id.CreateHash(plaintextPassword, argon2id.DefaultParams)
	if err != nil {
		return err
	}
	u.Password = hash
	return nil
}
