package repository

import (
	"errors"
	"sync"

	"github.com/google/uuid"
	"veza-backend-api/internal/models"
)

// UserRepositoryImpl implémentation en mémoire du repository des utilisateurs
type UserRepositoryImpl struct {
	users     map[string]*models.User
	emails    map[string]string
	usernames map[string]string // username -> userID mapping
	mutex     sync.RWMutex
}

// NewUserRepository crée une nouvelle instance du repository
func NewUserRepository() *UserRepositoryImpl {
	return &UserRepositoryImpl{
		users:     make(map[string]*models.User),
		emails:    make(map[string]string),
		usernames: make(map[string]string),
	}
}

// GetByID récupère un utilisateur par son ID
func (r *UserRepositoryImpl) GetByID(id string) (*models.User, error) {
	r.mutex.RLock()
	defer r.mutex.RUnlock()

	user, exists := r.users[id]
	if !exists {
		return nil, errors.New("user not found")
	}

	// Retourner une copie pour éviter les modifications accidentelles
	userCopy := *user
	return &userCopy, nil
}

// GetByEmail récupère un utilisateur par son email
func (r *UserRepositoryImpl) GetByEmail(email string) (*models.User, error) {
	r.mutex.RLock()
	defer r.mutex.RUnlock()

	userID, exists := r.emails[email]
	if !exists {
		return nil, errors.New("user not found")
	}

	user, exists := r.users[userID]
	if !exists {
		return nil, errors.New("user not found")
	}

	// Retourner une copie pour éviter les modifications accidentelles
	userCopy := *user
	return &userCopy, nil
}

// GetByUsername récupère un utilisateur par son username
func (r *UserRepositoryImpl) GetByUsername(username string) (*models.User, error) {
	r.mutex.RLock()
	defer r.mutex.RUnlock()

	userID, exists := r.usernames[username]
	if !exists {
		return nil, errors.New("user not found")
	}

	user, exists := r.users[userID]
	if !exists {
		return nil, errors.New("user not found")
	}

	// Retourner une copie pour éviter les modifications accidentelles
	userCopy := *user
	return &userCopy, nil
}

// Create crée un nouvel utilisateur
func (r *UserRepositoryImpl) Create(user *models.User) error {
	r.mutex.Lock()
	defer r.mutex.Unlock()

	// Vérifier si l'email existe déjà
	if _, exists := r.emails[user.Email]; exists {
		return errors.New("email already exists")
	}

	// Assigner un ID si vide
	if user.ID == uuid.Nil {
		user.ID = uuid.New()
	}

	// Créer une copie pour éviter les modifications accidentelles
	userCopy := *user
	// Forcer les valeurs par défaut
	userCopy.Role = "user"
	userCopy.FirstName = user.FirstName
	userCopy.LastName = user.LastName
	userCopy.Avatar = user.Avatar
	userCopy.Bio = user.Bio
	userCopy.IsActive = true
	userCopy.IsVerified = false
	userCopy.IsAdmin = false
	userIDStr := user.ID.String()
	r.users[userIDStr] = &userCopy
	r.emails[user.Email] = userIDStr
	r.usernames[user.Username] = userIDStr

	return nil
}

// Update met à jour un utilisateur existant
func (r *UserRepositoryImpl) Update(user *models.User) error {
	r.mutex.Lock()
	defer r.mutex.Unlock()

	userIDStr := user.ID.String()
	// Vérifier si l'utilisateur existe
	existingUser, exists := r.users[userIDStr]
	if !exists {
		return errors.New("user not found")
	}

	// Si l'email a changé, vérifier qu'il n'existe pas déjà
	if existingUser.Email != user.Email {
		if _, emailExists := r.emails[user.Email]; emailExists {
			return errors.New("email already exists")
		}

		// Mettre à jour les mappings
		delete(r.emails, existingUser.Email)
		r.emails[user.Email] = userIDStr
	}

	// Si le username a changé, mettre à jour le mapping
	if existingUser.Username != user.Username {
		// Vérifier que le nouveau username n'est pas déjà pris (par un autre utilisateur)
		if existingUserID, usernameExists := r.usernames[user.Username]; usernameExists && existingUserID != userIDStr {
			return errors.New("username already exists")
		}

		// Mettre à jour les mappings
		delete(r.usernames, existingUser.Username)
		r.usernames[user.Username] = userIDStr
	}

	// Créer une copie pour éviter les modifications accidentelles
	userCopy := *user
	r.users[userIDStr] = &userCopy

	return nil
}

// Delete supprime un utilisateur
func (r *UserRepositoryImpl) Delete(id string) error {
	r.mutex.Lock()
	defer r.mutex.Unlock()

	user, exists := r.users[id]
	if !exists {
		return errors.New("user not found")
	}

	// Supprimer les mappings
	delete(r.users, id)
	delete(r.emails, user.Email)
	delete(r.usernames, user.Username)

	return nil
}
