package rbac

import (
	"context"
	"fmt"
	"github.com/samber/lo"
	"strings"
	"uauth/model"
)

func (u *Urbac) newScope(ctx context.Context, code, label, parent string) (*model.Scope, error) {
	s := &model.Scope{Code: code, Label: label, Parent: parent}
	if err := u.store.Session(ctx).Create(s).Error; err != nil {
		return nil, err
	}

	return s, nil
}

func (u *Urbac) GetScopeGroup(ctx context.Context, name string) (*model.Scope, error) {
	scope := new(model.Scope)
	err := u.store.Session(ctx).Where("name = ?", name).Take(scope).Error

	return scope, err
}

func (u *Urbac) newRole(ctx context.Context, code, label, parent string, privileges ...*model.Privilege) (*model.Role, error) {
	ps := lo.FilterMap(
		privileges,
		func(p *model.Privilege, _ int) (string, bool) {
			if p == nil {
				return "", false
			}

			return p.Code, p.Code != ""
		},
	)

	r := &model.Role{
		Code:           code,
		Label:          label,
		Parent:         parent,
		PrivilegeCodes: ps,
	}

	if err := u.store.Session(ctx).Create(r).Error; err != nil {
		return nil, err
	}

	return r, nil
}

func (u *Urbac) GetRole(ctx context.Context, name string) (*model.Role, error) {
	var r model.Role
	if err := u.store.Session(ctx).Take(&r, "name = ?", name).Error; err != nil {
		return nil, err
	}

	return &r, nil
}

func (u *Urbac) newPrivilege(ctx context.Context, code, label string, parent string, scope string) (*model.Privilege, error) {
	p := &model.Privilege{Code: code, Label: label, Parent: parent, Scope: scope}

	codes := strings.SplitN(code, ":", 4)
	if len(codes) != 4 {
		return nil, fmt.Errorf("invalid code format")
	}

	wailcard := false
	for _, item := range codes {
		if item == "*" {
			wailcard = true
		}

		if wailcard && item != "*" {
			return nil, fmt.Errorf("invalid code format")
		}

		if len(item) > 8 {
			return nil, fmt.Errorf("invalid code format: code snippet too long")
		}
	}

	if codes[0] != "*" {
		if _, err := u.GetScopeGroup(ctx, codes[0]); err != nil {
			return nil, err
		}
	}

	if err := u.store.Session(ctx).Create(p).Error; err != nil {
		return nil, err
	}

	return p, nil
}

func (u *Urbac) newUser(target *model.User) (*model.User, error) {
	result := u.store.Session().Create(target)
	if result.Error != nil {
		return nil, result.Error
	}

	if result.RowsAffected != 1 {
		return nil, fmt.Errorf("invalid rows affected")
	}

	return target, nil
}