package rbac

import (
	"context"

	ddbDao "github.com/tidbcloud/account/internel/dao/ddb"
	"github.com/tidbcloud/account/internel/dao/entity"
	"gorm.io/gorm"
)

func (d *RbacDao) CreateRbacRolePrivilege(ctx context.Context, tx *gorm.DB, ddb *ddbDao.DynamoDBClient, roleID uint64, privilegeIDs []uint64) error {
	if len(privilegeIDs) == 0 {
		return nil
	}
	var rolePrivileges []entity.RbacRolePrivilege
	var privileges []entity.RbacPrivilege

	for _, privilegeID := range privilegeIDs {
		rolePrivilege := entity.RbacRolePrivilege{RbacRoleID: entity.ID(roleID), RbacPrivilegeID: entity.ID(privilegeID)}
		rolePrivileges = append(rolePrivileges, rolePrivilege)
	}

	err := entity.NewRbacPrivilegeQuerySet(tx).IDIn(entity.MakeIDList(privilegeIDs)...).All(&privileges)
	if err != nil {
		return err
	}

	err = tx.Transaction(func(tx *gorm.DB) error {
		err := tx.CreateInBatches(rolePrivileges, len(rolePrivileges)).Error
		if err != nil {
			return err
		}
		return ddbDao.CreateRolePrivilegesByRoleID(ctx, ddb, roleID, privileges)
	})
	if err != nil {
		return err
	}
	return nil
}

func (d *RbacDao) DeleteRbacRolePrivilege(ctx context.Context, tx *gorm.DB, ddb *ddbDao.DynamoDBClient, roleID uint64, privilegeID uint64) error {
	var privilege entity.RbacPrivilege
	err := tx.Transaction(func(tx *gorm.DB) error {
		err := entity.NewRbacRolePrivilegeQuerySet(tx).RbacRoleIDEq(entity.ID(roleID)).RbacPrivilegeIDEq(entity.ID(privilegeID)).Delete()
		if err != nil {
			return err
		}
		err = entity.NewRbacPrivilegeQuerySet(tx).IDEq(entity.ID(privilegeID)).One(&privilege)
		if err != nil {
			return err
		}
		return ddbDao.DeleteRolePrivilege(ctx, ddb, privilege.ResourceUri, roleID)
	})
	return err
}

func (d *RbacDao) ListRbacPrivilegesIDsByRoleID(ctx context.Context, tx *gorm.DB, roleIDs []uint64) ([]entity.ID, error) {
	var reqIds = entity.MakeIDList(roleIDs)
	var r []entity.RbacRolePrivilege
	err := entity.NewRbacRolePrivilegeQuerySet(tx).RbacRoleIDIn(reqIds...).All(&r)
	if err != nil {
		return nil, err
	}
	var ids []entity.ID

	for _, privilege := range r {
		ids = append(ids, privilege.RbacPrivilegeID)
	}
	return ids, nil
}
func (d *RbacDao) ListRbacRolePrivileges(ctx context.Context, tx *gorm.DB, filter RolePrivilegeFilter) ([]entity.RbacRolePrivilege, error) {

	var r []entity.RbacRolePrivilege
	if len(filter.IDs) == 0 && len(filter.RoleIDs) == 0 && len(filter.PrivilegeIDs) == 0 {
		return r, nil
	}
	qs := entity.NewRbacRolePrivilegeQuerySet(tx)

	if len(filter.IDs) > 0 {
		qs.IDIn(filter.IDs...)
	}
	if len(filter.RoleIDs) > 0 {
		qs.RbacRoleIDIn(filter.RoleIDs...)
	}
	if len(filter.PrivilegeIDs) > 0 {
		qs.RbacPrivilegeIDIn(filter.PrivilegeIDs...)
	}
	err := qs.All(&r)
	if err != nil {
		return nil, err
	}

	return r, nil
}

func (d *RbacDao) DeleteRbacRolePrivilegeByRoleID(ctx context.Context, tx *gorm.DB, ddb *ddbDao.DynamoDBClient, roleID uint64) error {
	var privileges []entity.RbacPrivilege
	privilegesIDs, err := d.ListRbacPrivilegesIDsByRoleID(ctx, tx, []uint64{roleID})
	if err != nil {
		return err
	}
	if len(privilegesIDs) == 0 {
		return nil
	}
	err = entity.NewRbacPrivilegeQuerySet(tx).IDIn(privilegesIDs...).All(&privileges)
	if err != nil {
		return err
	}
	err = tx.Transaction(func(tx *gorm.DB) error {
		err := entity.NewRbacRolePrivilegeQuerySet(tx).RbacRoleIDEq(entity.ID(roleID)).Delete()
		if err != nil {
			return err
		}
		return ddbDao.DeleteRolePrivilegesByRoleID(ctx, ddb, roleID, privileges)
	})
	return err
}

func (d *RbacDao) DeleteRbacRolePrivilegeByPrivilegeID(ctx context.Context, tx *gorm.DB, ddb *ddbDao.DynamoDBClient, privilegeID uint64) error {
	var rolePrivileges []entity.RbacRolePrivilege
	err := entity.NewRbacRolePrivilegeQuerySet(tx).RbacPrivilegeIDEq(entity.ID(privilegeID)).All(&rolePrivileges)
	if err != nil {
		return err
	}
	var roleIDs []uint64
	for _, rolePrivilege := range rolePrivileges {
		roleIDs = append(roleIDs, uint64(rolePrivilege.RbacRoleID))
	}
	privilege, err := d.GetRbacPrivilegeByID(ctx, tx, privilegeID)
	if err != nil {
		return err
	}
	err = tx.Transaction(func(tx *gorm.DB) error {
		err := entity.NewRbacRolePrivilegeQuerySet(tx).RbacPrivilegeIDEq(entity.ID(privilegeID)).Delete()
		if err != nil {
			return err
		}
		return ddbDao.DeleteRolePrivilegesByResourceUri(ctx, ddb, privilege.ResourceUri, roleIDs)
	})
	return err
}
