package repositories

import (
	"time"

	"gorm.io/gorm"

	"GoMembership/internal/constants"
	"GoMembership/internal/database"

	"gorm.io/gorm/clause"

	"GoMembership/internal/models"
	"GoMembership/pkg/errors"
)

type UserRepositoryInterface interface {
	CreateUser(user *models.User) (int64, error)
	UpdateUser(userID int64, user *models.User) error
	GetUsers(where map[string]interface{}) (*[]models.User, error)
	GetUserByID(id int64) (*models.User, error)
	GetUserByEmail(email string) (*models.User, error)
	SetVerificationToken(user *models.User, token *string) (int64, error)
	IsVerified(userID *int64) (bool, error)
	VerifyUserOfToken(token *string) (*models.User, error)
}

type UserRepository struct{}

func (ur *UserRepository) CreateUser(user *models.User) (int64, error) {
	result := database.DB.Create(user)
	if result.Error != nil {
		return 0, result.Error
	}
	return user.ID, nil
}

func (ur *UserRepository) UpdateUser(userID int64, user *models.User) error {
	// logger.Info.Printf("Updating User: %#v\n", user)
	if user == nil {
		return errors.ErrNoData
	}
	result := database.DB.Session(&gorm.Session{FullSaveAssociations: true}).Updates(&user)
	if result.Error != nil {
		return result.Error
	}

	if result.RowsAffected == 0 {
		return errors.ErrNoRowsAffected
	}

	return nil
}

func (ur *UserRepository) GetUsers(where map[string]interface{}) (*[]models.User, error) {
	var users []models.User
	result := database.DB.
		Preload("Consents").
		Preload("BankAccount").
		Preload("Verification").
		Preload("Membership", func(db *gorm.DB) *gorm.DB {
			return db.Preload("SubscriptionModel")
		}).Where(where).Find(&users)
	if result.Error != nil {
		if result.Error == gorm.ErrRecordNotFound {
			return nil, gorm.ErrRecordNotFound
		}
		return nil, result.Error
	}
	return &users, nil
}

func (ur *UserRepository) GetUserByID(id int64) (*models.User, error) {
	var user models.User
	result := database.DB.
		Preload("Consents").
		Preload("BankAccount").
		Preload("Verification").
		Preload("Membership", func(db *gorm.DB) *gorm.DB {
			return db.Preload("SubscriptionModel")
		}).First(&user, id)
	if result.Error != nil {
		if result.Error == gorm.ErrRecordNotFound {
			return nil, gorm.ErrRecordNotFound
		}
		return nil, result.Error
	}
	return &user, nil
}

func (ur *UserRepository) GetUserByEmail(email string) (*models.User, error) {
	var user models.User
	result := database.DB.Where("email = ?", email).First(&user)
	if result.Error != nil {
		if result.Error == gorm.ErrRecordNotFound {
			return nil, gorm.ErrRecordNotFound
		}
		return nil, result.Error
	}
	return &user, nil
}

func (ur *UserRepository) IsVerified(userID *int64) (bool, error) {
	var user models.User
	result := database.DB.Select("status").First(&user, userID)
	if result.Error != nil {
		if result.Error == gorm.ErrRecordNotFound {
			return false, gorm.ErrRecordNotFound
		}
		return false, result.Error
	}
	return user.Status != constants.UnverifiedStatus, nil
}

func (ur *UserRepository) VerifyUserOfToken(token *string) (*models.User, error) {
	var emailVerification models.Verification
	result := database.DB.Where("verification_token = ?", token).First(&emailVerification)
	if result.Error != nil {
		if result.Error == gorm.ErrRecordNotFound {
			return nil, gorm.ErrRecordNotFound
		}
		return nil, result.Error
	}

	// Check if the user is already verified
	verified, err := ur.IsVerified(&emailVerification.UserID)
	if err != nil {
		return nil, err
	}
	user, err := ur.GetUserByID(emailVerification.UserID)
	if err != nil {
		return nil, err
	}
	if verified {
		return user, errors.ErrAlreadyVerified
	}
	// Update user status to active
	t := time.Now()
	emailVerification.EmailVerifiedAt = &t
	user.Status = constants.VerifiedStatus
	user.Verification = emailVerification

	err = ur.UpdateUser(emailVerification.UserID, user)
	if err != nil {
		return nil, err
	}

	return user, nil
}

func (ur *UserRepository) SetVerificationToken(user *models.User, token *string) (int64, error) {
	// Check if user is already verified
	verified, err := ur.IsVerified(&user.ID)
	if err != nil {
		return -1, err
	}
	if verified {
		return -1, errors.ErrAlreadyVerified
	}

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

	// Use GORM to insert or update the Verification record
	result := database.DB.Clauses(clause.OnConflict{
		Columns:   []clause.Column{{Name: "user_id"}},
		DoUpdates: clause.AssignmentColumns([]string{"verification_token", "created_at"}),
	}).Create(&verification)

	if result.Error != nil {
		return -1, result.Error
	}

	return verification.ID, nil
}
