package rbac

import (
	"context"
	"errors"
	"fmt"

	"github.com/tidbcloud/tiinf/errs"

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

func (d *RbacDao) CreateRbacPrivilege(ctx context.Context, tx *gorm.DB, resourceUri string, url string, system string, description string) (*entity.RbacPrivilege, error) {
	rbacPrivilege := &entity.RbacPrivilege{ResourceUri: resourceUri, Description: description, Url: url, System: system}
	err := tx.Create(rbacPrivilege).Error
	if err != nil {
		return nil, err
	}

	return rbacPrivilege, nil
}

/*
	func (d *RbacDao) ListRbacPrivileges(ctx context.Context, tx *gorm.DB, param ListPrivilegeParam) (*ListPrivilegeResult, error) {
		tx = tx.Model(&entity.RbacPrivilege{})
		filter := param.Filter
		if len(filter.ID) > 0 {
			tx = tx.Where("id in (?) ", param.Filter.ID)
		}
		if filter.ResourceUri != "" {
			tx = tx.Where("resource_uri = ?", filter.ResourceUri)
		}
		if filter.Url != "" {
			tx = tx.Where("url = ?", filter.Url)
		}
		if filter.System != "" {
			tx = tx.Where("system = ?", filter.System)
		}
		if filter.Description != "" {
			tx = tx.Where("description = ?", filter.Description)
		}
		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 rbacPrivileges []entity.RbacPrivilege
		if err := tx.Find(&rbacPrivileges).Error; err != nil {
			return nil, err
		}

		result := ListPrivilegeResult{
			Total: total,
			List:  rbacPrivileges,
		}
		return &result, nil
	}
*/
func (d *RbacDao) ListRbacPrivileges(ctx context.Context, tx *gorm.DB, param ListPrivilegeParam) (*ListPrivilegeResult, error) {
	tx = tx.Model(&entity.RbacPrivilege{})
	filter := param.Filter
	if len(filter.ID) > 0 {
		tx = tx.Where("id in (?)", param.Filter.ID)
	}
	if len(filter.ResourceUri) > 0 {
		tx = tx.Where("resource_uri in (?)", filter.ResourceUri)
	}
	if len(filter.Url) > 0 {
		tx = tx.Where("url in (?)", filter.Url)
	}
	if len(filter.System) > 0 {
		tx = tx.Where("system in (?)", filter.System)
	}
	if len(filter.Description) > 0 {
		tx = tx.Where("description in (?)", filter.Description)
	}
	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 rbacPrivileges []entity.RbacPrivilege
	if err := tx.Find(&rbacPrivileges).Error; err != nil {
		return nil, err
	}

	result := ListPrivilegeResult{
		Total: total,
		List:  rbacPrivileges,
	}
	return &result, nil
}

func (d *RbacDao) GetRbacPrivilegeByID(ctx context.Context, tx *gorm.DB, id uint64) (*entity.RbacPrivilege, error) {
	var rbacPrivilege entity.RbacPrivilege
	err := entity.NewRbacPrivilegeQuerySet(tx).IDEq(entity.ID(id)).One(&rbacPrivilege)

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

func (d *RbacDao) GetRbacPrivilegeByResourceUri(ctx context.Context, tx *gorm.DB, resourceUri string) (*entity.RbacPrivilege, error) {
	var rbacPrivilege entity.RbacPrivilege
	err := entity.NewRbacPrivilegeQuerySet(tx).ResourceUriEq(resourceUri).One(&rbacPrivilege)

	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		return nil, errs.WrapGORMError(err, fmt.Sprintf("get rbac privilege failed: %+v", resourceUri))
	}
	return &rbacPrivilege, nil
}

func (d *RbacDao) UpdateRbacPrivilege(ctx context.Context, tx *gorm.DB, ddb *ddbDao.DynamoDBClient, id uint64, resourceUri string, url string, system string, description string) error {
	updater := entity.NewRbacPrivilegeQuerySet(tx).IDEq(entity.ID(id)).GetUpdater()
	if resourceUri != "" {
		updater.SetResourceUri(resourceUri)
	}
	if description != "" {
		updater.SetDescription(description)
	}
	if system != "" {
		updater.SetSystem(system)
	}
	if url != "" {
		updater.SetUrl(url)
	}
	err := tx.Transaction(func(tx *gorm.DB) error {
		var privilege entity.RbacPrivilege
		err := entity.NewRbacPrivilegeQuerySet(tx).IDEq(entity.ID(id)).One(&privilege)
		if err != nil {
			return err
		}
		err = updater.Update()
		if err != nil {
			return err
		}
		if resourceUri != "" && resourceUri != privilege.ResourceUri {
			var rolePrivileges []entity.RbacRolePrivilege
			var roleIDs []uint64
			err = entity.NewRbacRolePrivilegeQuerySet(tx).RbacPrivilegeIDEq(entity.ID(id)).All(&rolePrivileges)
			if err != nil {
				return err
			}
			for _, rolePrivilege := range rolePrivileges {
				roleIDs = append(roleIDs, rolePrivilege.RbacRoleID.Uint64())
			}
			return ddbDao.UpdateRbacRolePrivilegesByResourceUri(ctx, ddb, privilege.ResourceUri, resourceUri, privilege.ID.Uint64(), roleIDs)
		}
		return nil
	})

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

func (d *RbacDao) DeleteRbacPrivilege(ctx context.Context, tx *gorm.DB, ddb *ddbDao.DynamoDBClient, id uint64) error {
	var privilege entity.RbacPrivilege
	var rolePrivileges []entity.RbacRolePrivilege

	err := entity.NewRbacPrivilegeQuerySet(tx).IDEq(entity.ID(id)).One(&privilege)
	if err != nil {
		return err
	}
	err = entity.NewRbacRolePrivilegeQuerySet(tx).RbacPrivilegeIDEq(entity.ID(id)).All(&rolePrivileges)
	if err != nil {
		return err
	}

	var roleIDs []uint64
	for _, rolePrivilege := range rolePrivileges {
		roleIDs = append(roleIDs, rolePrivilege.RbacRoleID.Uint64())
	}

	err = tx.Transaction(func(tx *gorm.DB) error {
		err := privilege.Delete(tx)
		if err != nil {
			return err
		}
		return ddbDao.DeleteRolePrivilegesByResourceUri(ctx, ddb, privilege.ResourceUri, roleIDs)
	})
	if err != nil {
		return err
	}
	return nil
}

func (d *RbacDao) ListRbacPrivilegesByIDs(ctx context.Context, tx *gorm.DB, ids []entity.ID) ([]entity.RbacPrivilege, error) {
	var r []entity.RbacPrivilege
	if len(ids) == 0 {
		return r, nil
	}
	err := entity.NewRbacPrivilegeQuerySet(tx).IDIn(ids...).All(&r)
	if err != nil {
		return nil, err
	}
	return r, nil
}
