package rbac

import (
	"context"

	"gorm.io/gorm"

	"github.com/tidbcloud/account/internel/dao/role"

	"github.com/tidbcloud/account/internel/dao/entity"
)

func newRoleDao() role.RoleDaoIface {
	return role.NewDao()
}

func ChangeRoleTo(
	ctx context.Context, tx *gorm.DB,
	targetType entity.TargetType, targetID entity.ID, scopeType entity.ScopeType, scopeID entity.ID,
	role string, newRole string,
) (entity.RoleBinding, error) {
	dao := newRoleDao()
	return dao.ChangeRoleTo(
		ctx, tx,
		targetType, targetID.Uint64(), scopeType, scopeID.Uint64(),
		role, newRole)
}

// user roles on org

func IDListToUint64List(ids []entity.ID) []uint64 {
	result := make([]uint64, len(ids))
	for i, id := range ids {
		result[i] = id.Uint64()
	}
	return result
}

func LegacyGetUserRoleByOrg(ctx context.Context, tx *gorm.DB, userID entity.ID, orgID entity.ID) (*entity.RoleBinding, bool, error) {
	dao := newRoleDao()
	return dao.LegacyGetUserRoleByOrg(ctx, tx, userID.Uint64(), orgID.Uint64(), []string{entity.RoleBindingStatusActive})
}

func ListUserRolesByOrg(
	ctx context.Context, tx *gorm.DB,
	userID entity.ID,
	orgID entity.ID,
) ([]entity.RoleBinding, error) {
	dao := newRoleDao()
	return dao.ListUserRolesByUserIDAndOrg(ctx, tx, userID.Uint64(), orgID.Uint64(), []string{entity.RoleBindingStatusActive})
}

func ListUserRolesByOrgs(
	ctx context.Context, tx *gorm.DB,
	userID entity.ID,
	orgIDs []entity.ID,
) ([]entity.RoleBinding, error) {
	var orgIDList []uint64
	for _, orgID := range orgIDs {
		orgIDList = append(orgIDList, orgID.Uint64())
	}
	dao := newRoleDao()
	return dao.ListUserRolesByUserIDAndOrgs(ctx, tx, userID.Uint64(), orgIDList, []string{entity.RoleBindingStatusActive})
}

func AddOrgRoleForUser(
	ctx context.Context, tx *gorm.DB,
	userID entity.ID, orgID entity.ID, role string, status string,
) (*entity.RoleBinding, error) {
	dao := newRoleDao()
	return dao.CreateRole(
		ctx, tx, entity.RoleBindingTargetUser, userID.Uint64(), entity.RoleBindingScopeOrg, orgID.Uint64(), role, status,
	)
}

func RemoveOrgRoleForUser(
	ctx context.Context, tx *gorm.DB,
	userID entity.ID, orgID entity.ID, role string,
) (bool, error) {
	//bm := utils.Begin(ctx, "RemoveOrgRoleForUser", zap.Any("orgID", orgID), zap.Any("userID", userID))
	//defer bm.OnExit(ctx)
	dao := newRoleDao()
	return dao.DeleteRole(ctx, tx,
		entity.RoleBindingTargetUser, userID.Uint64(),
		entity.RoleBindingScopeOrg, orgID.Uint64(),
		role)
}

// user roles on project

func ListUserRolesByProject(
	ctx context.Context, tx *gorm.DB,
	userID entity.ID, projectID entity.ID,
) ([]entity.RoleBinding, error) {
	dao := newRoleDao()
	return dao.ListRolesByTargetAndScope(
		ctx, tx, entity.RoleBindingTargetUser, userID.Uint64(),
		entity.RoleBindingScopeProject, projectID.Uint64(), []string{entity.RoleBindingStatusActive},
	)
}

func ListUserRolesByProjects(ctx context.Context, tx *gorm.DB, userID entity.ID, projectIDs []entity.ID) ([]entity.RoleBinding, error) {
	dao := newRoleDao()
	return dao.ListRolesByTargetOnScopeList(
		ctx, tx, entity.RoleBindingTargetUser, userID.Uint64(),
		entity.RoleBindingScopeProject, IDListToUint64List(projectIDs), []string{entity.RoleBindingStatusActive, entity.RoleBindingStatusInactive},
	)
}

func AddProjectRoleForUser(ctx context.Context, tx *gorm.DB, userID entity.ID, projectID entity.ID, role string, status string) (*entity.RoleBinding, error) {
	dao := newRoleDao()
	return dao.CreateRole(
		ctx, tx, entity.RoleBindingTargetUser, userID.Uint64(), entity.RoleBindingScopeProject, projectID.Uint64(),
		role, status,
	)
}

func RemoveProjectRoleForUser(ctx context.Context, tx *gorm.DB, userID entity.ID, projectID entity.ID, role string) (bool, error) {
	dao := newRoleDao()
	return dao.DeleteRole(
		ctx, tx, entity.RoleBindingTargetUser, userID.Uint64(), entity.RoleBindingScopeProject, projectID.Uint64(), role,
	)
}

func HasActiveRole(ctx context.Context, tx *gorm.DB, targetType entity.TargetType, targetID uint64, scopeType entity.ScopeType, scopeID uint64, role string) (bool, error) {
	dao := newRoleDao()
	return dao.HasActiveRole(ctx, tx, targetType, targetID, scopeType, scopeID, role)
}
