package repositories

import (
	"gorm.io/gorm"

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

	"gorm.io/gorm/clause"

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

type UserRepositoryInterface interface {
	CreateUser(user *models.User) (uint, error)
	UpdateUser(user *models.User) (*models.User, error)
	GetUsers(where map[string]interface{}) (*[]models.User, error)
	GetUserByEmail(email string) (*models.User, error)
	SetVerificationToken(verification *models.Verification) (uint, error)
	IsVerified(userID *uint) (bool, error)
	GetVerificationOfToken(token *string) (*models.Verification, error)
	DeleteUser(id uint) error
}

type UserRepository struct{}

func (ur *UserRepository) DeleteUser(id uint) error {
	return database.DB.Delete(&models.User{}, "id = ?", id).Error
}

func PasswordExists(userID *uint) (bool, error) {
	var user models.User
	result := database.DB.Select("password").First(&user, userID)
	if result.Error != nil {
		return false, result.Error
	}
	return user.Password != "", nil
}

func (ur *UserRepository) CreateUser(user *models.User) (uint, error) {
	result := database.DB.Create(user)
	if result.Error != nil {
		logger.Error.Printf("Create User error: %#v", result.Error)
		return 0, result.Error
	}
	return user.ID, nil
}

func (ur *UserRepository) UpdateUser(user *models.User) (*models.User, error) {
	if user == nil {
		return nil, errors.ErrNoData
	}

	err := database.DB.Transaction(func(tx *gorm.DB) error {
		// Check if the user exists in the database
		var existingUser models.User

		if err := tx.Preload(clause.Associations).
			Preload("Membership").
			Preload("Membership.SubscriptionModel").
			Preload("Licence").
			Preload("Licence.Categories").
			First(&existingUser, user.ID).Error; err != nil {
			return err
		}
		// Update the user's main fields
		result := tx.Session(&gorm.Session{FullSaveAssociations: true}).Omit("Password").Updates(user)
		if result.Error != nil {
			return result.Error
		}
		if result.RowsAffected == 0 {
			return errors.ErrNoRowsAffected
		}

		if user.Password != "" {
			if err := tx.Model(&models.User{}).
				Where("id = ?", user.ID).
				Update("Password", user.Password).Error; err != nil {
				return err
			}
		}

		// Update the Membership if provided
		if user.Membership.ID != 0 {
			if err := tx.Model(&existingUser.Membership).Updates(user.Membership).Error; err != nil {
				return err
			}
		}

		// Replace categories if Licence and Categories are provided
		if user.Licence != nil {
			if err := tx.Model(&user.Licence).Association("Categories").Replace(user.Licence.Categories); err != nil {
				return err
			}
		}

		return nil
	})

	if err != nil {
		return nil, err
	}

	var updatedUser models.User
	if err := database.DB.Preload("Licence.Categories").
		Preload("Membership").
		First(&updatedUser, user.ID).Error; err != nil {
		return nil, err
	}
	return &updatedUser, nil
}

func (ur *UserRepository) GetUsers(where map[string]interface{}) (*[]models.User, error) {
	var users []models.User
	result := database.DB.
		Preload(clause.Associations).
		Preload("Membership.SubscriptionModel").
		Preload("Licence.Categories").
		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 GetUserByID(userID *uint) (*models.User, error) {
	var user models.User
	result := database.DB.
		Preload(clause.Associations).
		Preload("Membership").
		Preload("Membership.SubscriptionModel").
		Preload("Licence.Categories").
		First(&user, userID)
	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 *uint) (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) GetVerificationOfToken(token *string) (*models.Verification, 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
	}
	return &emailVerification, nil
}

func (ur *UserRepository) SetVerificationToken(verification *models.Verification) (uint, error) {
	// 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 0, result.Error
	}

	return verification.ID, nil
}
