package model

import (
	"time"

	"github.com/dchest/uniuri"
	"gorm.io/gorm"

	"github.com/pingcap-inc/clinic/pkg/db"
)

func InsertUserIfAbsent(externalIdentifier, userName, email string, internal bool) (string, error) {
	nu := &db.User{
		Model: gorm.Model{
			CreatedAt: time.Now(),
			UpdatedAt: time.Now(),
		},
		Email:              email,
		UserName:           userName,
		ExternalIdentifier: externalIdentifier,
		Internal:           internal,
	}

	err := db.Transaction(func(txn *gorm.DB) error {
		var ut db.User
		err := txn.Unscoped().Where("external_identifier=?", externalIdentifier).First(&ut).Error
		if err != nil && err != gorm.ErrRecordNotFound {
			return err
		}

		if ut.ID > 0 {
			if !ut.DeletedAt.Valid {
				nu.UserName = ut.UserName
				return nil
			}
			if err := txn.Unscoped().Where("external_identifier=?", externalIdentifier).Delete(&db.User{}).Error; err != nil {
				return err
			}
		}
		// insert user. add suffex to user_name if already exists
		err = txn.Unscoped().Where("user_name=?", userName).First(&ut).Error
		if err != nil && err != gorm.ErrRecordNotFound {
			return err
		}

		if ut.ID > 0 {
			nu.UserName = nu.UserName + uniuri.NewLen(6)
		}

		return txn.Create(nu).Error
	})
	if err != nil {
		return "", err
	}

	return nu.UserName, nil
}

func GetUserByUserName(userName string) (*db.User, error) {
	var ut db.User
	err := db.Transaction(func(txn *gorm.DB) error {
		return txn.Where("user_name=?", userName).First(&ut).Error
	})
	if err != nil {
		return nil, err
	}

	return &ut, nil
}

func GetUserByID(id uint) (*db.User, error) {
	var ut db.User
	err := db.Transaction(func(txn *gorm.DB) error {
		err := txn.Where("id=?", id).First(&ut).Error
		if err != nil && err != gorm.ErrRecordNotFound {
			return err
		}
		return nil
	})
	if err != nil {
		return nil, err
	}

	return &ut, nil
}

func GetAllInternalUsers() ([]db.User, error) {
	var uts []db.User
	err := db.Transaction(func(txn *gorm.DB) error {
		q := txn.Model(&db.User{})
		err := q.Where("internal=?", 1).Find(&uts).Error
		if err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		return nil, err
	}

	return uts, nil
}

func UserCount() (int64, error) {
	var (
		total int64
		err   error
	)

	err = db.Transaction(func(tx *gorm.DB) error {
		total, err = db.NewUserQuerySet(tx).Count()
		return err
	})

	return total, err
}

func DeleteUserByID(userID uint) error {
	return db.Transaction(func(tx *gorm.DB) error {
		_, err := db.NewUserQuerySet(tx).IDEq(userID).DeleteNumUnscoped()
		return err
	})
}
