package user

import (
	context "context"

	"github.com/tidbcloud/account/central/utils"
	"github.com/tidbcloud/account/commons/database"
	"github.com/tidbcloud/account/internel/dao"
	"github.com/tidbcloud/account/internel/dao/entity"
	"github.com/tidbcloud/tiinf/errs"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
)

type AddUserToProjectReq struct {
	ProjectRole string
	ProjectId   uint64
	UserId      uint64

	OperatorId uint64 // this is dbbuser operatorId
}

func (s *UserSvc) AddUserToProject(ctx context.Context, req AddUserToProjectReq) error {
	// check req.projectRole
	if err := utils.IsValidUserProjectRole(req.ProjectRole); err != nil {

		tilog.Info(ctx, "AddUserToProject ", zap.Any("err", err))
		return err
	}
	// 1、check project  status
	project, _ := dao.GetDaoHub().Project.GetProjectByID(ctx, database.DBConn.DB.WithContext(ctx), req.ProjectId)
	if project == nil || project.Status != entity.ProjectStatusActive {
		return errs.NewBadRequest("invalid project_id", nil)
	}

	// 2、check user  status in project
	projectUser, err := s.GetRoleBindingUser(ctx, entity.RoleBindingScopeProject, req.ProjectId, req.UserId)
	if err != nil {
		return err
	}
	if projectUser.Status == string(entity.Active) || projectUser.Status == string(entity.Pending) {
		return errs.NewBadRequest("user already in the project", nil)
	}

	// 3、check user  status
	user, err := s.GetUserByID(ctx, req.UserId)
	if err != nil {

		return err
	}

	if user.Status != entity.UserStatusActive {
		return errs.NewBadRequest("user status is not active", nil)
	}

	// 4、Add user in roleBinding
	tx := database.DBConn.DB.WithContext(ctx)

	r, err := dao.GetDaoHub().Role.AddProjectRoleForUser(ctx, tx, user.ID.Uint64(), req.ProjectId, req.ProjectRole)
	if err != nil {
		tilog.Error(ctx, "AddProjectRoleForUser", zap.Error(err))
		return err
	}
	// 5、check user  in org
	orgUser, err := s.GetRoleBindingUser(ctx, entity.RoleBindingScopeOrg, project.TenantID.Uint64(), req.UserId)
	if err != nil {
		return err
	}
	// 6、user not in project ， add user to org
	if orgUser.Status != string(entity.Active) && orgUser.Status != string(entity.Pending) {
		param := AddUserToOrgReq{UserId: req.UserId, OrgId: project.TenantID.Uint64(), OrgRole: entity.RoleOrgMember, OperatorId: req.OperatorId}
		err := s.AddUserToOrg(ctx, param)
		if err != nil {
			tilog.Error(ctx, "AddUserToOrg", zap.Error(err))
		}
	}
	// 5 、create  dbuser in cluster
	err = AddDBUserToProject(ctx, req.UserId, req.ProjectId, req.UserId, user.Email)
	if err != nil {
		tilog.Error(ctx, "AddDBUserToOrg", zap.Error(err))
	}
	// 6、 notice to jsm
	go noticeToJsmWhenAddUserToProject(context.Background(), req.UserId, req.ProjectId, req.ProjectRole)

	tilog.Info(ctx, "AddUserToProject success", zap.Any("roleBinding", r))
	return nil
}

func AddDBUserToProject(ctx context.Context, userId, projectId, operatorId uint64, email string) error {

	tx := database.DBConn.DB.WithContext(ctx)
	err := dao.GetDaoHub().DBUser.CreateCommandUserAction(ctx, tx,
		operatorId, userId, email, 0, projectId, entity.CommandStatusWaiting, entity.CommandActionCreate, entity.RoleBindingTargetUser, entity.RoleBindingTargetUser)
	// create dbuser
	tilog.Info(ctx, "AddDBUserToProject", zap.Any("userId", userId), zap.Any("err", err))
	return err
}

func (s *UserSvc) RemoveUserFromProject(ctx context.Context, userId, dbUserOperatorId, projectId uint64) error {
	// 1、quer user status
	roleBindingUser, err := s.GetRoleBindingUser(ctx, entity.RoleBindingScopeProject, projectId, userId)
	if err != nil {
		return err
	}

	// 2、DeleteUserFromOrg
	_, err = dao.GetDaoHub().Role.DeleteRoleByTargetAndScope(ctx, database.DBConn.DB, entity.RoleBindingTargetUser, userId, entity.RoleBindingScopeProject, []uint64{projectId})
	if err != nil {
		tilog.Error(ctx, "DeleteRoleByTargetAndScope error", zap.Any("err", err))
		return err
	}

	// 3、 delete dbuser
	if roleBindingUser.Status == string(entity.Active) {
		err = DeleteDBUserFormProject(ctx, userId, projectId, userId, roleBindingUser.Email)
		tilog.Info(ctx, "create DeleteUser command result", zap.Any("success", err == nil), zap.Any("email", roleBindingUser.Email))
	}

	//4、 notice to jsm
	go noticeToJsmWhenRemoveUserFromProject(context.Background(), userId, projectId)

	//5、 publish event
	//go func() {
	//	_ = userevent.PublishRemoveUserFromOrgEvent(ctx, orgId, userId)
	//}()

	tilog.Info(ctx, "RemoveUserFromProject success", zap.Any("DeleteUser", roleBindingUser.Email))
	return nil
}

func DeleteDBUserFormProject(ctx context.Context, userId, projectId, operatorId uint64, email string) error {

	tx := database.DBConn.DB.WithContext(ctx)
	err := dao.GetDaoHub().DBUser.CreateCommandUserAction(ctx, tx,
		operatorId, userId, email, 0, projectId, entity.CommandStatusWaiting, entity.CommandActionDelete, entity.RoleBindingTargetUser, entity.RoleBindingTargetUser)
	// delete dbuser
	tilog.Info(ctx, "DeleteDBUserOfProject", zap.Any("userId", userId), zap.Any("projectId", projectId), zap.Any("err", err))
	return err
}
