package rbac

import (
	"context"
	"fmt"

	"go.uber.org/zap"

	"github.com/tidbcloud/tiinf/monitor/tilog"

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

func (d *RbacDao) CreateRbacRole(ctx context.Context, tx *gorm.DB, name string, description string, scopeType entity.ScopeType, displayName string) error {
	rbacRole := &entity.RbacRole{Name: name, Description: description, DisplayName: displayName, ScopeType: scopeType}
	err := tx.Create(rbacRole).Error
	if err != nil {
		return err
	}

	return nil
}

func (d *RbacDao) ListRbacRoles(ctx context.Context, tx *gorm.DB) ([]entity.RbacRole, error) {
	var rbacRoles []entity.RbacRole
	err := entity.NewRbacRoleQuerySet(tx).StatusEq(entity.Active).All(&rbacRoles)

	if err != nil {
		return nil, err
	}
	return rbacRoles, nil
}

func (d *RbacDao) GetRbacRoleByID(ctx context.Context, tx *gorm.DB, rbacRoleID uint64) (*entity.RbacRole, error) {
	var rbacRole entity.RbacRole
	err := entity.NewRbacRoleQuerySet(tx).IDEq(entity.ID(rbacRoleID)).One(&rbacRole)

	if err != nil {
		return nil, err
	}
	return &rbacRole, nil
}

func (d *RbacDao) GetRbacRoleByName(ctx context.Context, tx *gorm.DB, name string) (*entity.RbacRole, error) {
	var rbacRole entity.RbacRole
	err := entity.NewRbacRoleQuerySet(tx).StatusEq(entity.Active).NameEq(name).One(&rbacRole)

	if err != nil {
		return nil, err
	}
	return &rbacRole, nil
}

func (d *RbacDao) GetRbacRoleByNameIn(ctx context.Context, tx *gorm.DB, names []string) ([]entity.RbacRole, error) {
	var rbacRoles []entity.RbacRole
	err := entity.NewRbacRoleQuerySet(tx).StatusEq(entity.Active).NameIn(names...).All(&rbacRoles)

	if err != nil {
		tilog.Error(ctx, "GetRbacRoleByNameIn", zap.Error(err))
		return nil, err
	}
	return rbacRoles, nil
}

func (d *RbacDao) UpdateRbacRole(ctx context.Context, tx *gorm.DB, rbacRoleID uint64, name string, description string, scopeType entity.ScopeType, displayName string) error {
	updater := entity.NewRbacRoleQuerySet(tx).IDEq(entity.ID(rbacRoleID)).GetUpdater()
	if name != "" {
		updater.SetName(name)
	}
	if description != "" {
		updater.SetDescription(description)
	}
	if scopeType != "" {
		updater.SetScopeType(scopeType)
	}
	if displayName != "" {
		updater.SetDisplayName(displayName)
	}
	err := updater.Update()

	if err != nil {
		tilog.Error(ctx, "UpdateRbacRole", zap.Error(err))
		return err
	}
	return nil
}

func (d *RbacDao) DeleteRbacRole(ctx context.Context, tx *gorm.DB, rbacRoleID uint64) error {
	updater := entity.NewRbacRoleQuerySet(tx).IDEq(entity.ID(rbacRoleID)).GetUpdater()
	updater.SetStatus(entity.Delete)
	err := updater.Update()

	if err != nil {
		return err
	}
	return nil

}

func (d *RbacDao) ListRbacRolesByIDs(ctx context.Context, tx *gorm.DB, rbacRoleIDs []uint64) ([]entity.RbacRole, error) {
	var rbacRoles []entity.RbacRole
	ids := entity.MakeIDList(rbacRoleIDs)
	err := entity.NewRbacRoleQuerySet(tx).IDIn(ids...).StatusEq(entity.Active).All(&rbacRoles)

	if err != nil {
		return nil, err
	}
	return rbacRoles, nil
}

// TraverseRbacRoles traverse rbac roles tables by param and return result
// Do not support filter
func (d *RbacDao) TraverseRbacRoles(ctx context.Context, tx *gorm.DB, param *ListRoleParam) (*ListRoleResult, error) {
	tx = tx.Model(&entity.RbacRole{})
	var sortBy = "id"
	if param.SortBy != "" {
		sortBy = param.SortBy
	}
	var order = "desc"
	if param.Order != "" {
		order = param.Order
	}
	tx = tx.Order(fmt.Sprintf("%s %s", sortBy, order))

	var total int64
	if err := tx.Count(&total).Error; err != nil {
		return nil, err
	}

	if param.Limit > 0 && param.Offset >= 0 {
		tx = tx.Limit(param.Limit).Offset(param.Offset)
	}

	var roles []entity.RbacRole
	if err := tx.Find(&roles).Error; err != nil {
		return nil, err
	}

	return &ListRoleResult{
		Total: total,
		List:  roles,
	}, nil
}
