package cross

import (
	"context"

	"github.com/tidbcloud/account/internel/dao/entity"
	roleDao "github.com/tidbcloud/account/internel/dao/role"
	"github.com/tidbcloud/account/internel/dao/user"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
	"gorm.io/gorm"
)

func createSystemUserEntityByOrgID(orgID uint64) *entity.User {
	email := user.MakeSystemEmail(orgID)
	user := entity.User{
		Email:     email,
		FirstName: "system",
		LastName:  "user",
		Status:    entity.UserStatusActive,
		TenantID:  entity.ID(orgID),
	}

	return &user
}

func (d *CrossDao) CreateSystemUserByOrgID(ctx context.Context, tx *gorm.DB, orgID uint64) (*entity.User, error) {
	_, err := d.Org.GetOrgByOrgID(ctx, tx, orgID)
	if err != nil {
		// if org not found, err is gorm.ErrRecordNotFound
		return nil, err
	}

	user := createSystemUserEntityByOrgID(orgID)
	err = user.Create(tx)
	if err != nil {
		return nil, err
	}

	_, err = d.Role.AddOrgRoleForUser(ctx, tx, user.ID.Uint64(), orgID, entity.RoleOrgMember)
	if err != nil {
		return nil, err
	}

	return user, nil
}

func (d *CrossDao) GetOrCreateSystemUserByOrgID(ctx context.Context, tx *gorm.DB, orgID uint64) (*entity.User, error) {
	user, err := d.User.GetSystemUserByOrgID(ctx, tx, orgID)
	if err != nil && err != gorm.ErrRecordNotFound {
		return nil, err
	}

	if user != nil {
		return user, nil
	}

	return d.CreateSystemUserByOrgID(ctx, tx, orgID)
}

func (d *CrossDao) ListUsersByProjectID(ctx context.Context, tx *gorm.DB, projectID uint64, roleStatus []string) ([]entity.User, error) {
	logger := tilog.WithContextL(ctx)

	bindings, err := d.Role.ListUserRolesByProject(ctx, tx, projectID, roleStatus)
	if err != nil {
		return nil, err
	}
	if len(bindings) == 0 {
		return nil, nil
	}

	userIDList := make([]uint64, len(bindings))
	for i, binding := range bindings {
		userIDList[i] = binding.TargetID.Uint64()
	}
	if len(userIDList) == 0 {
		return nil, nil
	}
	logger.Info("ListUsersByProjectID", zap.Uint64("projectID", projectID), zap.Any("roleStatus", roleStatus), zap.Uint64s("users", userIDList))

	return d.User.ListUsers(ctx, tx, userIDList, []entity.UserStatus{entity.UserStatusActive, entity.UserStatusPending})
}

func (d *CrossDao) ListUsersByOrgIDAndRole(ctx context.Context, tx *gorm.DB, orgID uint64, role string, status []string) ([]entity.User, error) {
	bindings, err := d.Role.ListRoleBindings(ctx, tx, roleDao.RoleBindingFilter{
		ScopeTypes: []entity.ScopeType{entity.RoleBindingScopeOrg},
		ScopeIDs:   []entity.ID{entity.ID(orgID)},
		TargetType: []entity.TargetType{entity.RoleBindingTargetUser},
		TargetIDs:  nil,
		Status:     status,
		Roles:      []string{role},
		RoleIDs:    nil,
	})
	if err != nil {
		return nil, err
	}

	if len(bindings) == 0 {
		return nil, nil
	}

	userIDList := make([]uint64, len(bindings))
	for i, binding := range bindings {
		userIDList[i] = binding.TargetID.Uint64()
	}

	return d.User.ListUsers(ctx, tx, userIDList, nil)
}
