package user

import (
	"context"
	"errors"
	"time"
	"tm/pkg/logger"
	orm "tm/pkg/mongo"
	"tm/pkg/response"

	"go.mongodb.org/mongo-driver/bson"
)

// Repository defines methods for user data access
type Repository interface {
	Login(ctx context.Context, id string) error
	Create(ctx context.Context, user *User) error
	Update(ctx context.Context, user *User) error
	Delete(ctx context.Context, id string) error
	GetByID(ctx context.Context, id string) (*User, error)
	GetByEmail(ctx context.Context, email string) (*User, error)
	GetByUsername(ctx context.Context, username string) (*User, error)
	Search(ctx context.Context, form *SearchUsersForm, pagination *response.Pagination) ([]User, int64, error)
}

// userRepository implements the Repository interface using the MongoDB ORM
type userRepository struct {
	ormRepo orm.Repository[User]
	logger  logger.Logger
}

// NewUserRepository creates a new user repository
func NewUserRepository(mongoManager *orm.ConnectionManager, logger logger.Logger) Repository {
	// Create indexes using the ORM's index management
	indexes := []orm.Index{
		*orm.CreateTextIndex("search_idx", "full_name", "email", "username"),
	}

	// Create indexes
	err := mongoManager.CreateIndexes("users", indexes)
	if err != nil {
		logger.Warn("Failed to create user indexes", map[string]interface{}{
			"error": err.Error(),
		})
	}

	// Create ORM repository
	ormRepo := orm.NewRepository[User](mongoManager.GetCollection("users"), logger)

	return &userRepository{
		ormRepo: ormRepo,
		logger:  logger,
	}
}

// Login updates the last login timestamp
func (r *userRepository) Login(ctx context.Context, id string) error {
	// Get user first
	user, err := r.GetByID(ctx, id)
	if err != nil {
		return err
	}

	// Update last login timestamp
	user.LastLoginAt = &[]int64{time.Now().Unix()}[0]
	user.SetUpdatedAt(time.Now().Unix())

	// Update in database
	err = r.ormRepo.Update(ctx, user)
	if err != nil {
		r.logger.Error("Failed to update last login", map[string]interface{}{
			"error":   err.Error(),
			"user_id": id,
		})
		return err
	}

	r.logger.Info("Last login updated successfully", map[string]interface{}{
		"user_id": id,
	})

	return nil
}

// Create creates a new user
func (r *userRepository) Create(ctx context.Context, user *User) error {
	// Set created timestamp using Unix timestamp
	now := time.Now().Unix()
	user.SetCreatedAt(now)

	// Use ORM to create user
	err := r.ormRepo.Create(ctx, user)
	if err != nil {
		r.logger.Error("Failed to create user", map[string]interface{}{
			"error":   err.Error(),
			"email":   user.Email,
			"user_id": user.ID,
		})
		return err
	}

	r.logger.Info("User created successfully", map[string]interface{}{
		"user_id": user.ID,
		"email":   user.Email,
		"role":    user.Role,
	})

	return nil
}

// Update updates a user
func (r *userRepository) Update(ctx context.Context, user *User) error {
	// Set updated timestamp using Unix timestamp
	user.SetUpdatedAt(time.Now().Unix())

	// Use ORM to update user
	err := r.ormRepo.Update(ctx, user)
	if err != nil {
		r.logger.Error("Failed to update user", map[string]interface{}{
			"error":   err.Error(),
			"user_id": user.ID,
		})
		return err
	}

	r.logger.Info("User updated successfully", map[string]interface{}{
		"user_id": user.ID,
		"email":   user.Email,
	})

	return nil
}

// GetByID retrieves a user by ID
func (r *userRepository) GetByID(ctx context.Context, id string) (*User, error) {
	user, err := r.ormRepo.FindByID(ctx, id)
	if err != nil {
		if errors.Is(err, orm.ErrDocumentNotFound) {
			return nil, errors.New("user not found")
		}
		r.logger.Error("Failed to get user by ID", map[string]interface{}{
			"error":   err.Error(),
			"user_id": id,
		})
		return nil, err
	}

	return user, nil
}

// GetByEmail retrieves a user by email
func (r *userRepository) GetByEmail(ctx context.Context, email string) (*User, error) {
	user, err := r.ormRepo.FindOne(ctx, bson.M{"email": email})
	if err != nil {
		if errors.Is(err, orm.ErrDocumentNotFound) {
			return nil, errors.New("user not found")
		}
		r.logger.Error("Failed to get user by email", map[string]interface{}{
			"error": err.Error(),
			"email": email,
		})
		return nil, err
	}

	return user, nil
}

// GetByUsername retrieves a user by username
func (r *userRepository) GetByUsername(ctx context.Context, username string) (*User, error) {
	user, err := r.ormRepo.FindOne(ctx, bson.M{"username": username})
	if err != nil {
		if errors.Is(err, orm.ErrDocumentNotFound) {
			return nil, errors.New("user not found")
		}
		r.logger.Error("Failed to get user by username", map[string]interface{}{
			"error":    err.Error(),
			"username": username,
		})
		return nil, err
	}

	return user, nil
}

// Delete deletes a user (soft delete by setting status to inactive)
func (r *userRepository) Delete(ctx context.Context, id string) error {
	// Get user first
	_, err := r.GetByID(ctx, id)
	if err != nil {
		r.logger.Error("Failed to delete user", map[string]interface{}{
			"error":   err.Error(),
			"user_id": id,
		})
		if errors.Is(err, orm.ErrDocumentNotFound) {
			return errors.New("user not found")
		}

		return err
	}

	// Delete in database
	err = r.ormRepo.Delete(ctx, id)
	if err != nil {
		r.logger.Error("Failed to delete user", map[string]interface{}{
			"error":   err.Error(),
			"user_id": id,
		})
		return err
	}

	r.logger.Info("User deleted successfully", map[string]interface{}{
		"user_id": id,
	})

	return nil
}

// List retrieves users with pagination
func (r *userRepository) Search(ctx context.Context, form *SearchUsersForm, pagination *response.Pagination) ([]User, int64, error) {
	filter := bson.M{}

	if form.Search != nil {
		filter["$or"] = bson.A{
			bson.M{"full_name": bson.M{"$regex": form.Search, "$options": "i"}},
			bson.M{"email": bson.M{"$regex": form.Search, "$options": "i"}},
			bson.M{"username": bson.M{"$regex": form.Search, "$options": "i"}},
		}
	}

	if form.Status != nil {
		filter["status"] = form.Status
	}

	if form.Role != nil {
		filter["role"] = form.Role
	}

	// Build sort
	sort := "created_at"
	if pagination.SortBy != "" {
		sort = pagination.SortBy
	}

	sortOrder := "desc"
	if pagination.SortOrder != "" {
		sortOrder = pagination.SortOrder
	}

	// Build pagination
	paginationBuilder := orm.NewPaginationBuilder().
		Limit(pagination.Limit).
		Skip(pagination.Offset).
		SortBy(sort, sortOrder).
		Build()

	// Use ORM to find users
	result, err := r.ormRepo.FindAll(ctx, filter, paginationBuilder)
	if err != nil {
		r.logger.Error("Failed to list users", map[string]interface{}{
			"error": err.Error(),
		})
		return nil, 0, err
	}

	return result.Items, result.TotalCount, nil
}
