package mongos

import (
	"context"
	"errors"
	"github.com/Pena-Co-Ltd/amocrm_templategen_back/dal/model"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo"
	"go.uber.org/zap"
	"golang.org/x/crypto/bcrypt"
	"time"
)

type User struct {
	coll   *mongo.Collection
	logger *zap.Logger
}

func InitUser(db *mongo.Database, logger *zap.Logger) *User {
	return &User{coll: db.Collection("user"), logger: logger}
}

func (d *User) Insert(ctx context.Context, record *model.User) (string, error) {
	now := time.Now()
	record.CreatedAt = now
	record.UpdatedAt = now
	record.IsDeleted = false

	// Find user by email
	found, err := d.GetByEmail(ctx, record.Email)
	if found != nil {
		err = errors.New("user already exists")
	}

	if err != nil {
		d.logger.Error("ErrorInsertUser", zap.Error(err))
		return "", err
	}

	gpass, err := bcrypt.GenerateFromPassword([]byte(record.Password), bcrypt.DefaultCost)
	if err != nil {
		return "", err
	}

	record.Password = string(gpass)

	result, err := d.coll.InsertOne(ctx, record)

	if err != nil {
		d.logger.Error("ErrorInsertUser", zap.Error(err))
		return "", err
	}

	return result.InsertedID.(primitive.ObjectID).Hex(), nil
}

func (d *User) GetByID(ctx context.Context, id string) (*model.User, error) {
	objID, err := primitive.ObjectIDFromHex(id)

	if err != nil {
		d.logger.Error("ErrorGetUser", zap.Error(err))
		return nil, err
	}

	filter := bson.M{"_id": objID, "is_deleted": false}

	var result model.User

	err = d.coll.FindOne(ctx, filter).Decode(&result)

	if err == mongo.ErrNoDocuments {
		return nil, nil
	} else {
		if err != nil {
			d.logger.Error("ErrorGetUser", zap.Error(err))
			return nil, err
		}
	}

	d.logger.Info("InfoGetUser", zap.String("id", result.ID))

	return &result, nil
}

func (d *User) GetByEmail(ctx context.Context, email string) (*model.User, error) {
	filter := bson.M{"email": email, "is_deleted": false}

	var result model.User

	err := d.coll.FindOne(ctx, filter).Decode(&result)

	if err == mongo.ErrNoDocuments {
		return nil, nil
	} else {
		if err != nil {
			d.logger.Error("ErrorGetUser", zap.Error(err))
			return nil, err
		}
	}

	d.logger.Info("InfoGetUser", zap.String("id", result.ID))

	return &result, nil
}

//func (d *User) GetByFilter(ctx context.Context, ) ([]model.User, error) {
//
//}

func (d *User) Update(ctx context.Context, record *model.User) error {
	if record.ID == "" {
		err := errors.New("got empty user id")
		d.logger.Error("ErrorUpdateUser", zap.Error(err))
		return err
	}

	objID, err := primitive.ObjectIDFromHex(record.ID)

	if err != nil {
		d.logger.Error("ErrorUpdateUser", zap.Error(err))
		return err
	}

	filter := bson.M{"_id": objID, "is_deleted": false}

	update := bson.M{"updated_at": time.Now()}

	if record.FullName != "" {
		update["username"] = record.FullName
	}

	if record.Email != "" {
		update["email"] = record.Email
	}

	if record.RoleID > 0 {
		update["role_id"] = record.RoleID
	}

	_, err = d.coll.UpdateOne(ctx, filter, bson.D{{"$set", update}})

	if err != nil {
		d.logger.Error("ErrorUpdateUser", zap.Error(err))
		return err
	}

	return nil
}

func (d *User) UpdateIsActivated(ctx context.Context, id string, isActivated bool) error {
	if id == "" {
		err := errors.New("got empty user id")
		d.logger.Error("ErrorUpdateUser", zap.Error(err))
		return err
	}

	objID, err := primitive.ObjectIDFromHex(id)

	if err != nil {
		d.logger.Error("ErrorUpdateUser", zap.Error(err))
		return err
	}

	filter := bson.M{"_id": objID, "is_deleted": false}

	update := bson.M{"updated_at": time.Now(), "is_activated": isActivated}

	_, err = d.coll.UpdateOne(ctx, filter, bson.D{{"$set", update}})
	if err != nil {
		d.logger.Error("ErrorUpdateUser", zap.Error(err))
		return err
	}

	return nil
}

func (d *User) Delete(ctx context.Context, id string) error {
	if id == "" {
		err := errors.New("got empty user id")
		d.logger.Error("ErrorDeleteUser", zap.Error(err))
		return err
	}

	objID, err := primitive.ObjectIDFromHex(id)

	if err != nil {
		d.logger.Error("ErrorDeleteUser", zap.Error(err))
		return err
	}

	filter := bson.M{"_id": objID, "is_deleted": false}

	update := bson.M{"updated_at": time.Now(), "is_deleted": true}

	_, err = d.coll.UpdateOne(ctx, filter, bson.D{{"$set", update}})
	if err != nil {
		d.logger.Error("ErrorDeleteUser", zap.Error(err))
		return err
	}

	return nil
}
