package user

import (
	"context"
	"git.noahlan.cn/n-admin/n-admin-server/dal/errx"
	"git.noahlan.cn/n-admin/n-admin-server/rpc/core/ent"
	"git.noahlan.cn/n-admin/n-admin-server/rpc/core/ent/user"
	"git.noahlan.cn/n-admin/n-admin-server/rpc/core/internal/logic/user/meta"
	"git.noahlan.cn/n-admin/n-admin-server/rpc/core/internal/svc"
	"git.noahlan.cn/n-admin/n-admin-server/rpc/core/internal/utils/entx"
	"git.noahlan.cn/n-admin/n-admin-server/rpc/core/internal/utils/hander"
	"git.noahlan.cn/n-admin/n-admin-server/rpc/core/types/core"
	"git.noahlan.cn/noahlan/ntool-biz/core/nstatus"
	"git.noahlan.cn/noahlan/ntool-biz/core/nstatus/code"
	"git.noahlan.cn/noahlan/ntool-biz/core/nstatus/msg"
	"git.noahlan.cn/noahlan/ntool-biz/core/orm/nent/types"
	"git.noahlan.cn/noahlan/ntool/ncrypt"
	"git.noahlan.cn/noahlan/ntool/nlog"
	"git.noahlan.cn/noahlan/ntool/nrandom"
	"git.noahlan.cn/noahlan/ntool/nstr"
	"strings"
)

type CreateUserLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewCreateUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateUserLogic {
	return &CreateUserLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

// User Management
func (l *CreateUserLogic) CreateUser(in *core.UserInfo) (*core.BaseIDResp, error) {
	// 检查用户
	var createdUser *ent.User
	err := entx.WithTx(l.ctx, l.svcCtx.DB, func(tx *ent.Tx) error {
		err := l.checkUserExists(tx, in)
		if err != nil {
			return err
		}
		db := tx.User.Create().
			SetStatus(types.StatusPending).
			SetNillableEmail(in.Email).
			SetPassword(ncrypt.BcryptEncrypt(in.Password, 0)).
			SetNillablePhoneNumber(in.PhoneNumber)
		if in.Email != nil {
			email := strings.TrimSpace(in.GetEmail())
			if !nstr.IsEmail(email) {
				return nstatus.NewGrpcInternalErr("邮箱格式错误")
			}
		}
		if in.Username == "" {
			if in.GetPhoneNumber() != "" {
				db.SetUsername(in.GetPhoneNumber())
			} else {
				db.SetUsername(nrandom.RandString(16))
			}
		} else {
			db.SetUsername(in.Username)
		}
		if in.Nickname != nil {
			nickname := strings.TrimSpace(*in.Nickname)
			if nickname != "" {
				db.SetNickname(nickname)
			}
		}
		// metas
		if len(in.Metas) > 0 {
			userMetaLogic := meta.NewUserMetaLogic(l.ctx, l.svcCtx, tx)
			err := userMetaLogic.UpsetUserMeta(in.ID, in.Metas)
			if err != nil {
				nlog.Errorw("upsert usermeta err", nlog.Field("detail", in), nlog.Field("err", err))
				return nstatus.NewBizErrWithCode(errx.UpdateUserMetaErr)
			}
		}
		// insert
		createdUser, err = db.Save(l.ctx)
		if err != nil {
			return hander.HandleEntErr(err, in)
		}
		return nil
	})
	if err != nil {
		return nil, err
	}

	return &core.BaseIDResp{
		ID:   createdUser.ID,
		Code: code.StatusOK,
		Msg:  msg.CreateSuccess,
	}, nil
}

func (l *CreateUserLogic) checkUserExists(tx *ent.Tx, in *core.UserInfo) error {
	if in.Username != "" {
		exist, err := tx.User.Query().Where(user.UsernameEQ(in.Username)).Exist(l.ctx)
		if err != nil {
			return hander.HandleEntErr(err, in)
		}
		if exist {
			return nstatus.NewBizErrWithCode(errx.UsernameAlreadyExists)
		}
	}
	if in.GetPhoneNumber() != "" {
		exist, err := tx.User.Query().Where(user.PhoneNumberEQ(in.GetPhoneNumber())).Exist(l.ctx)
		if err != nil {
			return hander.HandleEntErr(err, in)
		}
		if exist {
			return nstatus.NewBizErrWithCode(errx.UserPhoneNumberAlreadyExists)
		}
	}
	if in.GetNickname() != "" {
		exist, err := tx.User.Query().Where(user.NicknameEQ(in.GetNickname())).Exist(l.ctx)
		if err != nil {
			return hander.HandleEntErr(err, in)
		}
		if exist {
			return nstatus.NewBizErrWithCode(errx.UserNicknameAlreadyExists)
		}
	}
	if in.GetEmail() != "" {
		exist, err := tx.User.Query().Where(user.EmailEQ(in.GetEmail())).Exist(l.ctx)
		if err != nil {
			return hander.HandleEntErr(err, in)
		}
		if exist {
			return nstatus.NewBizErrWithCode(errx.UserEmailAlreadyExists)
		}
	}
	if in.ID != 0 {
		exist, err := tx.User.Query().Where(user.IDEQ(in.ID)).Exist(l.ctx)
		if err != nil {
			return hander.HandleEntErr(err, in)
		}
		if exist {
			return nstatus.NewBizErrWithCode(errx.UserExists)
		}
	}

	return nil
}
