/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>
*/

package engine

import (
	"fmt"
	"reflect"
	"testing"
	"time"

	"github.com/cgrates/birpc/context"
	"github.com/cgrates/cgrates/config"
	"github.com/cgrates/cgrates/utils"
	"github.com/ericlagergren/decimal"
)

func TestLibFiltersGetFloat64OptsReturnConfigOpt(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{},
	}
	dynOpts := []*config.DynamicFloat64Opt{
		// tenant will not be recognized, will ignore this opt
		config.NewDynamicFloat64Opt([]string{"*string:~*req.Account:1001"}, "cgrates.net", 3, nil),
		// filter will not pass, will ignore this opt
		config.NewDynamicFloat64Opt([]string{"*string:~*req.Account:1002"}, "cgrates.org", 4, nil),
		config.NewDynamicFloat64Opt([]string{"*string:~*req.Account:1001"}, "cgrates.org", 5, nil),
	}

	expected := 5.
	if rcv, err := GetFloat64Opts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		utils.OptsResourcesUnits); err != nil {
		t.Error(err)
	} else if rcv != expected {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
	}
}

func TestLibFiltersGetFloat64OptsFilterCheckErr(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{},
	}
	dynOpts := []*config.DynamicFloat64Opt{
		// function will return error after trying to parse the filter

		config.NewDynamicFloat64Opt([]string{"*string.invalid:filter"}, "cgrates.org", 4, nil),
	}

	experr := `inline parse error for string: <*string.invalid:filter>`
	if _, err := GetFloat64Opts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		utils.OptsResourcesUnits); err == nil ||
		err.Error() != experr {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
	}
}

func TestLibFiltersGetFloat64OptsReturnDefaultOpt(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{},
	}
	dynOpts := []*config.DynamicFloat64Opt{
		// filter will not pass, will ignore this opt
		config.NewDynamicFloat64Opt([]string{"*string:~*req.Account:1002"}, "cgrates.org", 4, nil),
		config.NewDynamicFloat64Opt(nil, utils.EmptyString, config.ResourcesUnitsDftOpt, nil),
	}

	if rcv, err := GetFloat64Opts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		utils.OptsResourcesUnits); err != nil {
		t.Error(err)
	} else if rcv != config.ResourcesUnitsDftOpt {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", config.ResourcesUnitsDftOpt, rcv)
	}
}

func TestLibFiltersGetFloat64OptsReturnOptFromAPIOpts(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{
			utils.OptsResourcesUnits: 6,
		},
	}
	dynOpts := []*config.DynamicFloat64Opt{
		// will never get to this opt because it will return once it
		// finds the one set in APIOpts
		config.NewDynamicFloat64Opt([]string{"*string:~*req.Account:1001"}, "cgrates.org", 5, nil),
	}

	expected := 6.
	if rcv, err := GetFloat64Opts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		"nonExistingAPIOpt", utils.OptsResourcesUnits); err != nil {
		t.Error(err)
	} else if rcv != expected {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
	}
}

func TestLibFiltersGetStringOptsReturnConfigOpt(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{},
	}
	dynOpts := []*config.DynamicStringOpt{
		// tenant will not be recognized, will ignore this opt

		config.NewDynamicStringOpt([]string{"*string:~*req.Account:1001"}, "cgrates.net", "value1", nil),
		// filter will not pass, will ignore this opt
		config.NewDynamicStringOpt([]string{"*string:~*req.Account:1002"}, "cgrates.net", "value2", nil),
		config.NewDynamicStringOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", "value3", nil),
	}

	expected := "value3"
	if rcv, err := GetStringOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		config.ResourcesUsageIDDftOpt, utils.OptsResourcesUsageID); err != nil {
		t.Error(err)
	} else if rcv != expected {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
	}
}

func TestLibFiltersGetStringOptsFilterCheckErr(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{},
	}
	dynOpts := []*config.DynamicStringOpt{
		// function will return error after trying to parse the filter
		config.NewDynamicStringOpt([]string{"*string.invalid:filter"}, "cgrates.org", "value2", nil),
	}

	experr := `inline parse error for string: <*string.invalid:filter>`
	if _, err := GetStringOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		config.ResourcesUsageIDDftOpt, utils.OptsResourcesUsageID); err == nil ||
		err.Error() != experr {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
	}
}

func TestLibFiltersGetStringOptsReturnDefaultOpt(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{},
	}
	dynOpts := []*config.DynamicStringOpt{
		// filter will not pass, will ignore this opt
		config.NewDynamicStringOpt([]string{"*string:~*req.Account:1002"}, "cgrates.org", "value2", nil),
	}

	if rcv, err := GetStringOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		config.ResourcesUsageIDDftOpt, utils.OptsResourcesUsageID); err != nil {
		t.Error(err)
	} else if rcv != config.ResourcesUsageIDDftOpt {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", config.ResourcesUsageIDDftOpt, rcv)
	}
}

func TestLibFiltersGetStringOptsReturnOptFromAPIOpts(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{
			utils.OptsResourcesUsageID: "value4",
		},
	}
	dynOpts := []*config.DynamicStringOpt{
		// will never get to this opt because it will return once it
		// finds the one set in APIOpts
		config.NewDynamicStringOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", "value3", nil),
	}

	expected := "value4"
	if rcv, err := GetStringOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		config.ResourcesUsageIDDftOpt, "nonExistingAPIOpt", utils.OptsResourcesUsageID); err != nil {
		t.Error(err)
	} else if rcv != expected {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
	}
}

func TestLibFiltersGetDurationOptsReturnConfigOpt(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{},
	}
	dynOpts := []*config.DynamicDurationOpt{
		// tenant will not be recognized, will ignore this opt
		config.NewDynamicDurationOpt([]string{"*string:~*req.Account:1001"}, "cgrates.net", time.Millisecond, nil),
		// filter will not pass, will ignore this opt
		config.NewDynamicDurationOpt([]string{"*string:~*req.Account:1002"}, "cgrates.net", time.Second, nil),
		config.NewDynamicDurationOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", time.Minute, nil),
	}

	expected := time.Minute
	if rcv, err := GetDurationOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		utils.OptsResourcesUsageTTL); err != nil {
		t.Error(err)
	} else if rcv != expected {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
	}
}

func TestLibFiltersGetDurationOptsFilterCheckErr(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{},
	}
	dynOpts := []*config.DynamicDurationOpt{
		// function will return error after trying to parse the filter
		config.NewDynamicDurationOpt([]string{"*string.invalid:filter"}, "cgrates.org", time.Second, nil),
	}

	experr := `inline parse error for string: <*string.invalid:filter>`
	if _, err := GetDurationOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		utils.OptsResourcesUsageTTL); err == nil ||
		err.Error() != experr {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
	}
}

func TestLibFiltersGetDurationOptsReturnDefaultOpt(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{},
	}
	dynOpts := []*config.DynamicDurationOpt{
		// filter will not pass, will ignore this opt
		config.NewDynamicDurationOpt([]string{"*string:~*req.Account:1002"}, "cgrates.org", time.Second, nil),
		config.NewDynamicDurationOpt(nil, "", config.ResourcesUsageTTLDftOpt, nil),
	}

	if rcv, err := GetDurationOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		utils.OptsResourcesUsageTTL); err != nil {
		t.Error(err)
	} else if rcv != config.ResourcesUsageTTLDftOpt {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", config.ResourcesUsageTTLDftOpt, rcv)
	}
}

func TestLibFiltersGetDurationPointerOptsReturnOptFromAPIOpts(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{
			utils.OptsResourcesUsageTTL: time.Hour,
		},
	}
	dynOpts := []*config.DynamicDurationPointerOpt{
		// will never get to this opt because it will return once it
		// finds the one set in APIOpts
		config.NewDynamicDurationPointerOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", utils.DurationPointer(time.Minute), nil),
	}

	expected := time.Hour
	if rcv, err := GetDurationPointerOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		"nonExistingAPIOpt", utils.OptsResourcesUsageTTL); err != nil {
		t.Error(err)
	} else if *rcv != expected {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
	}
}
func TestLibFiltersGetDurationPointerOptsReturnConfigOpt(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{},
	}
	dynOpts := []*config.DynamicDurationPointerOpt{
		// tenant will not be recognized, will ignore this opt
		config.NewDynamicDurationPointerOpt([]string{"*string:~*req.Account:1001"}, "cgrates.net", utils.DurationPointer(time.Millisecond), nil),
		// filter will not pass, will ignore this opt
		config.NewDynamicDurationPointerOpt([]string{"*string:~*req.Account:1002"}, "cgrates.net", utils.DurationPointer(time.Second), nil),
		config.NewDynamicDurationPointerOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", utils.DurationPointer(time.Minute), nil),
	}

	expected := time.Minute
	if rcv, err := GetDurationPointerOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		utils.OptsResourcesUsageTTL); err != nil {
		t.Error(err)
	} else if *rcv != expected {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
	}
}

func TestLibFiltersGetDurationPointerOptsFilterCheckErr(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{},
	}
	dynOpts := []*config.DynamicDurationPointerOpt{
		// function will return error after trying to parse the filter
		config.NewDynamicDurationPointerOpt([]string{"*string.invalid:filter"}, "cgrates.org", utils.DurationPointer(time.Second), nil),
	}

	experr := `inline parse error for string: <*string.invalid:filter>`
	if _, err := GetDurationPointerOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		utils.OptsResourcesUsageTTL); err == nil ||
		err.Error() != experr {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
	}
}

func TestLibFiltersGetDurationPointerOptsReturnDefaultOpt(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{},
	}
	dynOpts := []*config.DynamicDurationPointerOpt{
		// filter will not pass, will ignore this opt
		config.NewDynamicDurationPointerOpt([]string{"*string:~*req.Account:1002"}, "cgrates.org", utils.DurationPointer(time.Second), nil),
		config.NewDynamicDurationPointerOpt(nil, "", utils.DurationPointer(config.ResourcesUsageTTLDftOpt), nil),
	}

	if rcv, err := GetDurationPointerOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		utils.OptsResourcesUsageTTL); err != nil {
		t.Error(err)
	} else if *rcv != config.ResourcesUsageTTLDftOpt {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", config.ResourcesUsageTTLDftOpt, rcv)
	}
}

func TestLibFiltersGetDurationOptsReturnOptFromAPIOpts(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{
			utils.OptsResourcesUsageTTL: time.Hour,
		},
	}
	dynOpts := []*config.DynamicDurationOpt{
		// will never get to this opt because it will return once it
		// finds the one set in APIOpts
		config.NewDynamicDurationOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", time.Minute, nil),
	}

	expected := time.Hour
	if rcv, err := GetDurationOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		"nonExistingAPIOpt", utils.OptsResourcesUsageTTL); err != nil {
		t.Error(err)
	} else if rcv != expected {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
	}
}

func TestLibFiltersGetIntOptsReturnConfigOpt(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{},
	}
	dynOpts := []*config.DynamicIntOpt{
		// tenant will not be recognized, will ignore this opt
		config.NewDynamicIntOpt([]string{"*string:~*req.Account:1002"}, "cgrates.net", 3, nil),
		// filter will not pass, will ignore this opt
		config.NewDynamicIntOpt([]string{"*string:~*req.Account:1002"}, "cgrates.org", 4, nil),
		config.NewDynamicIntOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", 5, nil),
	}

	expected := 5
	if rcv, err := GetIntOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		utils.OptsAttributesProcessRuns); err != nil {
		t.Error(err)
	} else if rcv != expected {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
	}
}

func TestLibFiltersGetIntOptsFilterCheckErr(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{},
	}
	dynOpts := []*config.DynamicIntOpt{
		// function will return error after trying to parse the filter
		config.NewDynamicIntOpt([]string{"*string.invalid:filter"}, "cgrates.org", 4, nil),
	}

	experr := `inline parse error for string: <*string.invalid:filter>`
	if _, err := GetIntOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		utils.OptsAttributesProcessRuns); err == nil ||
		err.Error() != experr {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
	}
}

func TestLibFiltersGetIntOptsReturnDefaultOpt(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{},
	}
	dynOpts := []*config.DynamicIntOpt{
		// filter will not pass, will ignore this opt
		config.NewDynamicIntOpt([]string{"*string:~*req.Account:1002"}, "cgrates.org", 4, nil),
		config.NewDynamicIntOpt(nil, utils.EmptyString, config.AttributesProcessRunsDftOpt, nil),
	}

	if rcv, err := GetIntOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		utils.OptsAttributesProcessRuns); err != nil {
		t.Error(err)
	} else if rcv != config.AttributesProcessRunsDftOpt {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", config.AttributesProcessRunsDftOpt, rcv)
	}
}

func TestLibFiltersGetIntOptsReturnOptFromAPIOptsOK(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{
			utils.OptsAttributesProcessRuns: 6,
		},
	}
	dynOpts := []*config.DynamicIntOpt{
		// will never get to this opt because it will return once it
		// finds the one set in APIOpts
		config.NewDynamicIntOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", 5, nil),
	}

	expected := 6
	if rcv, err := GetIntOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		"nonExistingAPIOpt", utils.OptsAttributesProcessRuns); err != nil {
		t.Error(err)
	} else if rcv != expected {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
	}
}

func TestLibFiltersGetIntOptsReturnOptFromAPIOptsErr(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{
			utils.OptsAttributesProcessRuns: true,
		},
	}
	dynOpts := []*config.DynamicIntOpt{
		// will never get to this opt because it will return once it
		// finds the one set in APIOpts
		config.NewDynamicIntOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", 5, nil),
	}

	experr := `cannot convert field<bool>: true to int`
	if _, err := GetIntOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		"nonExistingAPIOpt", utils.OptsAttributesProcessRuns); err == nil || err.Error() != experr {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
	}
}

func TestLibFiltersGetTimeOptsReturnConfigOpt(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{},
	}
	dynOpts := []*config.DynamicStringOpt{
		// tenant will not be recognized, will ignore this opt
		config.NewDynamicStringOpt([]string{"*string:~*req.Account:1001"}, "cgrates.net", "2022-03-07T15:04:05", nil),
		// filter will not pass, will ignore this opt
		config.NewDynamicStringOpt([]string{"*string:~*req.Account:1002"}, "cgrates.org", "*daily", nil),
		config.NewDynamicStringOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", "*monthly", nil),
	}

	expected := time.Now().AddDate(0, 1, 0)
	if rcv, err := GetTimeOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		cfg.GeneralCfg().DefaultTimezone, config.RatesStartTimeDftOpt, utils.OptsRatesStartTime); err != nil {
		t.Error(err)
	} else if !dateEqual(rcv, expected) {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
	}
}

func TestLibFiltersGetTimeOptsFilterCheckErr(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{},
	}
	dynOpts := []*config.DynamicStringOpt{
		// function will return error after trying to parse the filter
		config.NewDynamicStringOpt([]string{"*string.invalid:filter"}, "cgrates.org", "*daily", nil),
	}

	experr := `inline parse error for string: <*string.invalid:filter>`
	if _, err := GetTimeOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		cfg.GeneralCfg().DefaultTimezone, config.RatesStartTimeDftOpt, utils.OptsRatesStartTime); err == nil ||
		err.Error() != experr {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
	}
}

func TestLibFiltersGetTimeOptsReturnDefaultOpt(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{},
	}
	expected, err := utils.ParseTimeDetectLayout(config.RatesStartTimeDftOpt, cfg.GeneralCfg().DefaultTimezone)
	if err != nil {
		t.Error(err)
	}
	dynOpts := []*config.DynamicStringOpt{
		// filter will not pass, will ignore this opt
		config.NewDynamicStringOpt([]string{"*string:~*req.Account:1002"}, "cgrates.org", "*daily", nil),
		config.NewDynamicStringOpt(nil, "", config.RatesStartTimeDftOpt, nil),
	}

	if rcv, err := GetTimeOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		cfg.GeneralCfg().DefaultTimezone, config.RatesStartTimeDftOpt, utils.OptsRatesStartTime); err != nil {
		t.Error(err)
	} else if !dateEqual(rcv, expected) {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
	}
}

func TestLibFiltersGetTimeOptsReturnOptFromAPIOpts(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{
			utils.OptsRatesStartTime: "*yearly",
		},
	}
	dynOpts := []*config.DynamicStringOpt{
		// will never get to this opt because it will return once it
		// finds the one set in APIOpts
		config.NewDynamicStringOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", "*monthly", nil),
	}

	expected := time.Now().AddDate(1, 0, 0)
	if rcv, err := GetTimeOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		cfg.GeneralCfg().DefaultTimezone, config.RatesStartTimeDftOpt, "nonExistingAPIOpt", utils.OptsRatesStartTime); err != nil {
		t.Error(err)
	} else if !dateEqual(rcv, expected) {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
	}
}

func dateEqual(date1, date2 time.Time) bool {
	y1, m1, d1 := date1.Date()
	y2, m2, d2 := date2.Date()
	return y1 == y2 && m1 == m2 && d1 == d2
}

func TestLibFiltersGetBoolOptsReturnConfigOpt(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{},
	}
	dynOpts := []*config.DynamicBoolOpt{
		// tenant will not be recognized, will ignore this opt
		config.NewDynamicBoolOpt([]string{"*string:~*req.Account:1001"}, "cgrates.net", false, nil),
		// filter will not pass, will ignore this opt
		config.NewDynamicBoolOpt([]string{"*string:~*req.Account:1002"}, "cgrates.org", false, nil),
		config.NewDynamicBoolOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", true, nil),
	}

	expected := true
	if rcv, err := GetBoolOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		utils.MetaProfileIgnoreFilters); err != nil {
		t.Error(err)
	} else if rcv != expected {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
	}
}

func TestLibFiltersGetBoolOptsFilterCheckErr(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{},
	}
	dynOpts := []*config.DynamicBoolOpt{
		// function will return error after trying to parse the filter
		config.NewDynamicBoolOpt([]string{"*string.invalid:filter"}, "cgrates.org", false, nil),
	}

	experr := `inline parse error for string: <*string.invalid:filter>`
	if _, err := GetBoolOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		utils.MetaProfileIgnoreFilters); err == nil ||
		err.Error() != experr {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
	}
}

func TestLibFiltersGetBoolOptsReturnDefaultOpt(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{},
	}
	dynOpts := []*config.DynamicBoolOpt{
		// filter will not pass, will ignore this opt
		config.NewDynamicBoolOpt([]string{"*string:~*req.Account:1002"}, "cgrates.org", true, nil),
	}

	if rcv, err := GetBoolOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		utils.MetaProfileIgnoreFilters); err != nil {
		t.Error(err)
	} else if rcv != config.ThresholdsProfileIgnoreFiltersDftOpt {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", config.ThresholdsProfileIgnoreFiltersDftOpt, rcv)
	}
}

func TestLibFiltersGetBoolOptsReturnOptFromAPIOpts(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{
			utils.MetaProfileIgnoreFilters: true,
		},
	}
	dynOpts := []*config.DynamicBoolOpt{
		// will never get to this opt because it will return once it
		// finds the one set in APIOpts
		config.NewDynamicBoolOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", false, nil),
	}

	expected := true
	if rcv, err := GetBoolOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		"nonExistingAPIOpt", utils.MetaProfileIgnoreFilters); err != nil {
		t.Error(err)
	} else if rcv != expected {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
	}
}

func TestLibFiltersGetInterfaceOptsReturnConfigOpt(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{},
	}
	dynOpts := []*config.DynamicInterfaceOpt{
		// tenant will not be recognized, will ignore this opt
		{
			FilterIDs: []string{"*string:~*req.Account:1001"},
			Tenant:    "cgrates.net",
			Value:     1,
		},
		// filter will not pass, will ignore this opt
		{
			FilterIDs: []string{"*string:~*req.Account:1002"},
			Tenant:    "cgrates.org",
			Value:     "value2",
		},
		{
			FilterIDs: []string{"*string:~*req.Account:1001"},
			Tenant:    "cgrates.org",
			Value:     "value3",
		},
	}

	expected := "value3"
	if rcv, err := GetInterfaceOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		config.RoutesMaxCostDftOpt, utils.OptsRoutesMaxCost); err != nil {
		t.Error(err)
	} else if rcv != expected {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
	}
}

func TestLibFiltersGetInterfaceOptsFilterCheckErr(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{},
	}
	dynOpts := []*config.DynamicInterfaceOpt{
		// function will return error after trying to parse the filter
		{
			FilterIDs: []string{"*string.invalid:filter"},
			Tenant:    "cgrates.org",
			Value:     2,
		},
	}

	experr := `inline parse error for string: <*string.invalid:filter>`
	if _, err := GetInterfaceOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		config.RoutesMaxCostDftOpt, utils.OptsRoutesMaxCost); err == nil ||
		err.Error() != experr {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
	}
}

func TestLibFiltersGetInterfaceOptsReturnDefaultOpt(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{},
	}
	dynOpts := []*config.DynamicInterfaceOpt{
		// filter will not pass, will ignore this opt
		{
			FilterIDs: []string{"*string:~*req.Account:1002"},
			Tenant:    "cgrates.org",
			Value:     2,
		},
		{
			Value: config.RoutesMaxCostDftOpt,
		},
	}

	if rcv, err := GetInterfaceOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		utils.OptsRoutesMaxCost); err != nil {
		t.Error(err)
	} else if rcv != config.RoutesMaxCostDftOpt {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", utils.ToJSON(config.RoutesMaxCostDftOpt), rcv)
	}
}

func TestLibFiltersGetInterfaceOptsReturnOptFromAPIOpts(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{
			utils.OptsRoutesMaxCost: 4,
		},
	}
	dynOpts := []*config.DynamicInterfaceOpt{
		// will never get to this opt because it will return once it
		// finds the one set in APIOpts
		{
			FilterIDs: []string{"*string:~*req.Account:1001"},
			Tenant:    "cgrates.org",
			Value:     "value3",
		},
	}

	expected := 4
	if rcv, err := GetInterfaceOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		config.RoutesMaxCostDftOpt, "nonExistingAPIOpt", utils.OptsRoutesMaxCost); err != nil {
		t.Error(err)
	} else if rcv != expected {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
	}
}

func TestLibFiltersGetStringSliceOptsReturnConfigOpt(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{},
	}
	dynOpts := []*config.DynamicStringSliceOpt{
		// tenant will not be recognized, will ignore this opt
		{
			FilterIDs: []string{"*string:~*req.Account:1001"},
			Tenant:    "cgrates.net",
			Values:    []string{"value1"},
		},
		// filter will not pass, will ignore this opt
		{
			FilterIDs: []string{"*string:~*req.Account:1002"},
			Tenant:    "cgrates.org",
			Values:    []string{"value2"},
		},
		{
			FilterIDs: []string{"*string:~*req.Account:1001"},
			Tenant:    "cgrates.org",
			Values:    []string{"value3"},
		},
	}

	expected := []string{"value3"}
	if rcv, err := GetStringSliceOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		config.AttributesProfileIDsDftOpt, utils.OptsAttributesProfileIDs); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(rcv, expected) {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
	}
}

func TestLibFiltersGetStringSliceOptsFilterCheckErr(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{},
	}
	dynOpts := []*config.DynamicStringSliceOpt{
		// function will return error after trying to parse the filter
		{
			FilterIDs: []string{"*string.invalid:filter"},
			Tenant:    "cgrates.org",
			Values:    []string{"value2"},
		},
	}

	experr := `inline parse error for string: <*string.invalid:filter>`
	if _, err := GetStringSliceOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		config.AttributesProfileIDsDftOpt, utils.OptsAttributesProfileIDs); err == nil ||
		err.Error() != experr {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
	}
}

func TestLibFiltersGetStringSliceOptsReturnDefaultOpt(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{},
	}
	dynOpts := []*config.DynamicStringSliceOpt{
		// filter will not pass, will ignore this opt
		{
			FilterIDs: []string{"*string:~*req.Account:1002"},
			Tenant:    "cgrates.org",
			Values:    []string{"value2"},
		},
	}

	if rcv, err := GetStringSliceOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		config.AttributesProfileIDsDftOpt, utils.OptsAttributesProfileIDs); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(rcv, config.AttributesProfileIDsDftOpt) {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", config.AttributesProfileIDsDftOpt, rcv)
	}
}

func TestLibFiltersGetStringSliceOptsReturnOptFromAPIOpts(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{
			utils.OptsAttributesProfileIDs: []string{"value4"},
		},
	}
	dynOpts := []*config.DynamicStringSliceOpt{
		// will never get to this opt because it will return once it
		// finds the one set in APIOpts
		{
			FilterIDs: []string{"*string:~*req.Account:1001"},
			Tenant:    "cgrates.org",
			Values:    []string{"value3"},
		},
	}

	expected := []string{"value4"}
	if rcv, err := GetStringSliceOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		config.AttributesProfileIDsDftOpt, "nonExistingAPIOpt", utils.OptsAttributesProfileIDs); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(rcv, expected) {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
	}
}

func TestLibFiltersGetDecimalBigOptsReturnConfigOpt(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{},
	}
	strOpts := []*config.DynamicInterfaceOpt{
		{
			FilterIDs: []string{"*string:~*req.Account:1001"},
			Tenant:    "cgrates.net",
			Value:     "42",
		},
		// filter will not pass, will ignore this opt
		{
			FilterIDs: []string{"*string:~*req.Account:1002"},
			Tenant:    "cgrates.org",
			Value:     "-1",
		},
		{
			FilterIDs: []string{"*string:~*req.Account:1001"},
			Tenant:    "cgrates.org",
			Value:     "1234",
		},
	}
	dynOpts, _ := config.IfaceToDecimalBigDynamicOpts(strOpts)

	expected := decimal.New(1234, 0)
	if rcv, err := GetDecimalBigOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		utils.OptsRatesUsage); err != nil {
		t.Error(err)
	} else if rcv.Cmp(expected) != 0 {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
	}
}

func TestDynamicDecimalBigOptsDynVal(t *testing.T) {
	tests := []struct {
		name    string
		dynOpts []*config.DynamicInterfaceOpt
		expVal  int64
		expErr  error
	}{
		{
			name: "DynOptsVal",
			dynOpts: []*config.DynamicInterfaceOpt{
				{
					Tenant: "cgrates.org",
					Value:  "~*opts.*usage",
				},
			},
			expVal: int64(time.Second * 27),
		},
		{
			name: "DynReqVal",
			dynOpts: []*config.DynamicInterfaceOpt{
				{
					Tenant: "cgrates.org",
					Value:  "~*req.Usage",
				},
			},
			expVal: int64(time.Second * 12),
		},
		{
			name: "StaticVal",
			dynOpts: []*config.DynamicInterfaceOpt{
				{
					Tenant: "cgrates.org",
					Value:  4334,
				},
			},
			expVal: 4334,
		},
		{
			name: "NotFound",
			dynOpts: []*config.DynamicInterfaceOpt{
				{
					Tenant: "cgrates.org",
					Value:  "~*req.RandomField",
				},
			},
			expErr: utils.ErrNotFound,
		},
		{
			name: "ValueNotConvertedCorrectly",
			dynOpts: []*config.DynamicInterfaceOpt{
				{
					Tenant: "cgrates.org",
					Value:  "~*req.Usage2",
				},
			},
			expErr: fmt.Errorf("can't convert <twenty-five> to decimal"),
		},
	}

	ev := &utils.CGREvent{
		Tenant: utils.CGRateSorg,
		ID:     "testIDEvent",
		Event: map[string]any{
			utils.AccountField: "1001",
			utils.Usage:        "12s",
			"Usage2":           "twenty-five",
		},
		APIOpts: map[string]any{
			utils.MetaUsage: "27s",
		},
	}
	fs := NewFilterS(config.CgrConfig(), nil, nil)

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			dynOpts, err := config.IfaceToDecimalBigDynamicOpts(tt.dynOpts)
			if err != nil {
				t.Error(err)
				return
			}
			out, err := GetDecimalBigOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fs, dynOpts, utils.OptsRatesUsage)
			if tt.expErr != nil {
				if err == nil {
					t.Error("expected err,received nil")
				}
				if err.Error() != tt.expErr.Error() {
					t.Errorf("expected error %v,received %v", tt.expErr, err)
				}
				return
			}
			if err != nil {
				t.Errorf("unexpected err %v", err)
			}
			val, _ := out.Int64()
			if tt.expVal != val {
				t.Errorf("expected %d,received %d", tt.expVal, val)
			}
		})
	}
}
func TestDynamicIntOptsDynVal(t *testing.T) {
	tests := []struct {
		name    string
		dynOpts []*config.DynamicInterfaceOpt
		expVal  int
		expErr  error
	}{
		{
			name: "DynOptsVal",
			dynOpts: []*config.DynamicInterfaceOpt{
				{
					Tenant: "cgrates.org",
					Value:  "~*opts.*destination",
				},
			},
			expVal: 6548454,
		},
		{
			name: "DynReqVal",
			dynOpts: []*config.DynamicInterfaceOpt{
				{
					Tenant: "cgrates.org",
					Value:  "~*req.Destination",
				},
			},
			expVal: 34534353,
		},
		{
			name: "StaticVal",
			dynOpts: []*config.DynamicInterfaceOpt{
				{
					Tenant: "cgrates.org",
					Value:  5346633,
				},
			},
			expVal: 5346633,
		},
		{
			name: "NotFound",
			dynOpts: []*config.DynamicInterfaceOpt{
				{
					Tenant: "cgrates.org",
					Value:  "~*req.RandomField",
				},
			},
			expErr: utils.ErrNotFound,
		},
		{
			name: "ValueNotConvertedCorrectly",
			dynOpts: []*config.DynamicInterfaceOpt{
				{
					Tenant: "cgrates.org",
					Value:  "~*req.Usage2",
				},
			},
			expErr: fmt.Errorf("strconv.Atoi: parsing \"twenty-five\": invalid syntax"),
		},
	}

	ev := &utils.CGREvent{
		Tenant: utils.CGRateSorg,
		ID:     "testIDEvent",
		Event: map[string]any{
			utils.AccountField: "1001",
			utils.Destination:  34534353,
			"Usage2":           "twenty-five",
		},
		APIOpts: map[string]any{
			utils.MetaDestination: 6548454,
		},
	}
	fs := NewFilterS(config.CgrConfig(), nil, nil)

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			dynOpts, err := config.IfaceToIntDynamicOpts(tt.dynOpts)
			if err != nil {
				t.Error(err)
				return
			}
			out, err := GetIntOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fs, dynOpts, utils.OptsRatesUsage)
			if tt.expErr != nil {
				if err == nil {
					t.Error("expected err,received nil")
				}
				if err.Error() != tt.expErr.Error() {
					t.Errorf("expected error %v,received %v", tt.expErr, err)
				}
				return
			}
			if err != nil {
				t.Errorf("unexpected err %v", err)
			}
			if tt.expVal != out {
				t.Errorf("expected %d,received %d", tt.expVal, out)
			}
		})
	}
}

func TestDynamicFloat64OptsDynVal(t *testing.T) {
	tests := []struct {
		name    string
		dynOpts []*config.DynamicInterfaceOpt
		expVal  float64
		expErr  error
	}{
		{
			name: "DynOptsVal",
			dynOpts: []*config.DynamicInterfaceOpt{
				{
					Tenant: "cgrates.org",
					Value:  "~*opts.*acc",
				},
			},
			expVal: 3434,
		},
		{
			name: "DynReqVal",
			dynOpts: []*config.DynamicInterfaceOpt{
				{
					Tenant: "cgrates.org",
					Value:  "~*req.*acd",
				},
			},
			expVal: 2213.,
		},
		{
			name: "StaticVal",
			dynOpts: []*config.DynamicInterfaceOpt{
				{
					Tenant: "cgrates.org",
					Value:  23.1,
				},
			},
			expVal: 23.1,
		},
		{
			name: "NotFound",
			dynOpts: []*config.DynamicInterfaceOpt{
				{
					Tenant: "cgrates.org",
					Value:  "~*req.RandomField",
				},
			},
			expErr: utils.ErrNotFound,
		},
		{
			name: "ValueNotConvertedCorrectly",
			dynOpts: []*config.DynamicInterfaceOpt{
				{
					Tenant: "cgrates.org",
					Value:  "~*req.Usage2",
				},
			},
			expErr: fmt.Errorf("strconv.ParseFloat: parsing \"twenty-five\": invalid syntax"),
		},
	}

	ev := &utils.CGREvent{
		Tenant: utils.CGRateSorg,
		ID:     "testIDEvent",
		Event: map[string]any{
			utils.AccountField: "1001",
			"*acd":             2213,
			"Usage2":           "twenty-five",
		},
		APIOpts: map[string]any{
			"*acc": 3434,
		},
	}
	fs := NewFilterS(config.CgrConfig(), nil, nil)

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			dynOpts, err := config.InterfaceToFloat64DynamicOpts(tt.dynOpts)
			if err != nil {
				t.Error(err)
				return
			}
			out, err := GetFloat64Opts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fs, dynOpts, utils.OptsRatesUsage)
			if tt.expErr != nil {
				if err == nil {
					t.Error("expected err,received nil")
				}
				if err.Error() != tt.expErr.Error() {
					t.Errorf("expected error %v,received %v", tt.expErr, err)
				}
				return
			}
			if err != nil {
				t.Errorf("unexpected err %v", err)
			}
			if tt.expVal != out {
				t.Errorf("expected %f,received %f", tt.expVal, out)
			}
		})
	}
}

func TestDynamicBoolOptsDynVal(t *testing.T) {
	tests := []struct {
		name    string
		dynOpts []*config.DynamicInterfaceOpt
		expVal  bool
		expErr  error
	}{
		{
			name: "DynOptsVal",
			dynOpts: []*config.DynamicInterfaceOpt{
				{
					Tenant: "cgrates.org",
					Value:  "~*opts.Field2",
				},
			},
			expVal: true,
		},
		{
			name: "DynReqVal",
			dynOpts: []*config.DynamicInterfaceOpt{
				{
					Tenant: "cgrates.org",
					Value:  "~*req.Field1",
				},
			},
			expVal: false,
		},
		{
			name: "StaticVal",
			dynOpts: []*config.DynamicInterfaceOpt{
				{
					Tenant: "cgrates.org",
					Value:  true,
				},
			},
			expVal: true,
		},
		{
			name: "NotFound",
			dynOpts: []*config.DynamicInterfaceOpt{
				{
					Tenant: "cgrates.org",
					Value:  "~*req.RandomField",
				},
			},
			expErr: utils.ErrNotFound,
		},
		{
			name: "ValueNotConvertedCorrectly",
			dynOpts: []*config.DynamicInterfaceOpt{
				{
					Tenant: "cgrates.org",
					Value:  "~*req.Usage2",
				},
			},
			expErr: fmt.Errorf("strconv.ParseBool: parsing \"twenty-five\": invalid syntax"),
		},
	}

	ev := &utils.CGREvent{
		Tenant: utils.CGRateSorg,
		ID:     "testIDEvent",
		Event: map[string]any{
			utils.AccountField: "1001",
			"Field1":           false,
			"Usage2":           "twenty-five",
		},
		APIOpts: map[string]any{
			"Field2": true,
		},
	}
	fs := NewFilterS(config.CgrConfig(), nil, nil)

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			dynOpts, err := config.IfaceToBoolDynamicOpts(tt.dynOpts)
			if err != nil {
				t.Error(err)
				return
			}
			out, err := GetBoolOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fs, dynOpts, utils.OptsRatesUsage)
			if tt.expErr != nil {
				if err == nil {
					t.Error("expected err,received nil")
				}
				if err.Error() != tt.expErr.Error() {
					t.Errorf("expected error %v,received %v", tt.expErr, err)
				}
				return
			}
			if err != nil {
				t.Errorf("unexpected err %v", err)
			}
			if tt.expVal != out {
				t.Errorf("expected %v,received %v", tt.expVal, out)
			}
		})
	}
}

func TestDynamicDurationOptsDynVal(t *testing.T) {
	tests := []struct {
		name    string
		dynOpts []*config.DynamicInterfaceOpt
		expVal  time.Duration
		expErr  error
	}{
		{
			name: "DynOptsVal",
			dynOpts: []*config.DynamicInterfaceOpt{
				{
					Tenant: "cgrates.org",
					Value:  "~*opts.Usage",
				},
			},
			expVal: time.Second * 10,
		},
		{
			name: "DynReqVal",
			dynOpts: []*config.DynamicInterfaceOpt{
				{
					Tenant: "cgrates.org",
					Value:  "~*req.*acd",
				},
			},
			expVal: 3500000,
		},
		{
			name: "StaticVal",
			dynOpts: []*config.DynamicInterfaceOpt{
				{
					Tenant: "cgrates.org",
					Value:  1000000000,
				},
			},
			expVal: 1 * time.Second,
		},
		{
			name: "NotFound",
			dynOpts: []*config.DynamicInterfaceOpt{
				{
					Tenant: "cgrates.org",
					Value:  "~*req.RandomField",
				},
			},
			expErr: utils.ErrNotFound,
		},
		{
			name: "ValueNotConvertedCorrectly",
			dynOpts: []*config.DynamicInterfaceOpt{
				{
					Tenant: "cgrates.org",
					Value:  "~*req.Usage2",
				},
			},
			expErr: fmt.Errorf("time: invalid duration \"twenty-five\""),
		},
	}

	ev := &utils.CGREvent{
		Tenant: utils.CGRateSorg,
		ID:     "testIDEvent",
		Event: map[string]any{
			utils.AccountField: "1001",
			"*acd":             3500000,
			"Usage2":           "twenty-five",
		},
		APIOpts: map[string]any{
			"Usage": "10s",
		},
	}
	fs := NewFilterS(config.CgrConfig(), nil, nil)

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			dynOpts, err := config.IfaceToDurationDynamicOpts(tt.dynOpts)
			if err != nil {
				t.Error(err)
				return
			}
			out, err := GetDurationOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fs, dynOpts, utils.OptsRatesUsage)
			if tt.expErr != nil {
				if err == nil {
					t.Error("expected err,received nil")
				}
				if err.Error() != tt.expErr.Error() {
					t.Errorf("expected error %v,received %v", tt.expErr, err)
				}
				return
			}
			if err != nil {
				t.Errorf("unexpected err %v", err)
			}
			if tt.expVal != out {
				t.Errorf("expected %v,received %v", tt.expVal, out)
			}
		})
	}
}

func TestDynamicDurationPointerOptsDynVal(t *testing.T) {
	tests := []struct {
		name    string
		dynOpts []*config.DynamicInterfaceOpt
		expVal  *time.Duration
		expErr  error
	}{
		{
			name: "DynOptsVal",
			dynOpts: []*config.DynamicInterfaceOpt{
				{
					Tenant: "cgrates.org",
					Value:  "~*opts.Usage",
				},
			},
			expVal: utils.DurationPointer(time.Second * 10),
		},
		{
			name: "DynReqVal",
			dynOpts: []*config.DynamicInterfaceOpt{
				{
					Tenant: "cgrates.org",
					Value:  "~*req.*acd",
				},
			},
			expVal: utils.DurationPointer(3500000),
		},
		{
			name: "StaticVal",
			dynOpts: []*config.DynamicInterfaceOpt{
				{
					Tenant: "cgrates.org",
					Value:  1000000000,
				},
			},
			expVal: utils.DurationPointer(1 * time.Second),
		},
		{
			name: "NotFound",
			dynOpts: []*config.DynamicInterfaceOpt{
				{
					Tenant: "cgrates.org",
					Value:  "~*req.RandomField",
				},
			},
			expErr: utils.ErrNotFound,
		},
		{
			name: "ValueNotConvertedCorrectly",
			dynOpts: []*config.DynamicInterfaceOpt{
				{
					Tenant: "cgrates.org",
					Value:  "~*req.Usage2",
				},
			},
			expErr: fmt.Errorf("time: invalid duration \"twenty-five\""),
		},
	}

	ev := &utils.CGREvent{
		Tenant: utils.CGRateSorg,
		ID:     "testIDEvent",
		Event: map[string]any{
			utils.AccountField: "1001",
			"*acd":             3500000,
			"Usage2":           "twenty-five",
		},
		APIOpts: map[string]any{
			"Usage": "10s",
		},
	}
	fs := NewFilterS(config.CgrConfig(), nil, nil)

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			dynOpts, err := config.IfaceToDurationPointerDynamicOpts(tt.dynOpts)
			if err != nil {
				t.Error(err)
				return
			}
			out, err := GetDurationPointerOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fs, dynOpts, utils.OptsRatesUsage)
			if tt.expErr != nil {
				if err == nil {
					t.Error("expected err,received nil")
				}
				if err.Error() != tt.expErr.Error() {
					t.Errorf("expected error %v,received %v", tt.expErr, err)
				}
				return
			}
			if err != nil {
				t.Errorf("unexpected err %v", err)
			}
			if *tt.expVal != *out {
				t.Errorf("expected %v,received %v", tt.expVal, out)
			}
		})
	}
}

func TestDynamicStringOptsDynVal(t *testing.T) {
	tests := []struct {
		name    string
		dynOpts []*config.DynamicInterfaceOpt
		expVal  string
		expErr  error
	}{
		{
			name: "DynOptsVal",
			dynOpts: []*config.DynamicInterfaceOpt{
				{
					Tenant: "cgrates.org",
					Value:  "~*opts.*context",
				},
			},
			expVal: "chargers",
		},
		{
			name: "DynReqVal",
			dynOpts: []*config.DynamicInterfaceOpt{
				{
					Tenant: "cgrates.org",
					Value:  "~*req.Supplier",
				},
			},
			expVal: "Supplier1",
		},
		{
			name: "StaticVal",
			dynOpts: []*config.DynamicInterfaceOpt{
				{
					Tenant: "cgrates.org",
					Value:  "value",
				},
			},
			expVal: "value",
		},
	}

	ev := &utils.CGREvent{
		Tenant: utils.CGRateSorg,
		ID:     "testIDEvent",
		Event: map[string]any{
			utils.AccountField: "1001",
			"Supplier":         "Supplier1",
		},
		APIOpts: map[string]any{
			"*context": "chargers",
		},
	}
	fs := NewFilterS(config.CgrConfig(), nil, nil)

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			dynOpts, err := config.InterfaceToDynamicStringOpts(tt.dynOpts)
			if err != nil {
				t.Error(err)
				return
			}
			out, err := GetStringOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fs, dynOpts)
			if tt.expErr != nil {
				if err == nil {
					t.Error("expected err,received nil")
				}
				if err.Error() != tt.expErr.Error() {
					t.Errorf("expected error %v,received %v", tt.expErr, err)
				}
				return
			}
			if err != nil {
				t.Errorf("unexpected err %v", err)
			}
			if tt.expVal != out {
				t.Errorf("expected %v,received %v", tt.expVal, out)
			}
		})
	}
}

func TestAttrDynamicOptsFromJson(t *testing.T) {
	cfgJSONStr := `{
		"attributes": {								
			"enabled": true,	
			"stats_conns": ["*internal"],			
			"resources_conns": ["*internal"],		
			"accounts_conns": ["*internal"],			
			"prefix_indexed_fields": ["*req.index1","*req.index2"],		
			"string_indexed_fields": ["*req.index1"],
			"exists_indexed_fields": ["*req.index1","*req.index2"],		
			"notexists_indexed_fields": ["*req.index1"],
			"opts": {
				"*processRuns": [
						{
							"Value": "~*req.ProcessRuns",
							"FilterIDs": ["*string:~*req.Account:1001"]
						},
						{
							"Value": 11,
							"FilterIDs": ["*string:~*req.Account:1003"]
						},
					],
		        "*profileRuns": [			
		        	{
		        		"FilterIDs": ["*string:~*req.Account:1001"],
		        		"Value": "~*opts.ProfileRuns"
		        	}
		        ],
		        "*profileIgnoreFilters": [ 		
		        	{
		        		"FilterIDs": ["*string:~*req.Account:1001"],
		        		"Value": "~*req.IgnoreFilters"
		        	}
		         ] 
			},					
			},		
		}`

	cgrCfg, err := config.NewCGRConfigFromJSONStringWithDefaults(cfgJSONStr)
	if err != nil {
		t.Error(err)
	}
	ev := &utils.CGREvent{
		Tenant: utils.CGRateSorg,
		ID:     "testIDEvent",
		Event: map[string]any{
			utils.AccountField: "1001",
			"ProcessRuns":      4,
			"IgnoreFilters":    true,
		},
		APIOpts: map[string]any{
			"ProfileRuns": 5,
		},
	}
	ev2 := &utils.CGREvent{
		Tenant: utils.CGRateSorg,
		ID:     "testIDEvent",
		Event: map[string]any{
			utils.AccountField: "1002",
			"ProcessRuns":      4,
			"IgnoreFilters":    true,
		},
		APIOpts: map[string]any{
			"ProfileRuns": 5,
		},
	}
	ev3 := &utils.CGREvent{
		Tenant: utils.CGRateSorg,
		ID:     "testIDEvent",
		Event: map[string]any{
			utils.AccountField: "1003",
		},
		APIOpts: map[string]any{},
	}
	fltrs := NewFilterS(cgrCfg, nil, nil)
	if rcv, err := GetIntOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fltrs, cgrCfg.AttributeSCfg().Opts.ProcessRuns); err != nil {
		t.Error(err)
	} else if rcv != 4 {
		t.Errorf("expected %d,received %d", 4, rcv)
	}
	if rcv, err := GetIntOpts(context.Background(), "cgrates.org", ev3.AsDataProvider(), nil, fltrs, cgrCfg.AttributeSCfg().Opts.ProcessRuns); err != nil {
		t.Error(err)
	} else if rcv != 11 {
		t.Errorf("expected %d,received %d", 11, rcv)
	}
	if rcv, err := GetIntOpts(context.Background(), "cgrates.org", ev2.AsDataProvider(), nil, fltrs, cgrCfg.AttributeSCfg().Opts.ProcessRuns); err != nil {
		t.Error(err)
	} else if rcv != config.AttributesProcessRunsDftOpt {
		t.Errorf("expected %d,received %d", config.AttributesProcessRunsDftOpt, rcv)
	}

	if rcv, err := GetIntOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fltrs, cgrCfg.AttributeSCfg().Opts.ProfileRuns); err != nil {
		t.Error(err)
	} else if rcv != 5 {
		t.Errorf("expected %d,received %d", 5, rcv)
	}
	if rcv, err := GetIntOpts(context.Background(), "cgrates.org", ev2.AsDataProvider(), nil, fltrs, cgrCfg.AttributeSCfg().Opts.ProfileRuns); err != nil {
		t.Error(err)
	} else if rcv != config.AttributesProfileRunsDftOpt {
		t.Errorf("expected %d,received %d", config.AttributesProcessRunsDftOpt, rcv)
	}

	if rcv, err := GetBoolOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fltrs, cgrCfg.AttributeSCfg().Opts.ProfileIgnoreFilters); err != nil {
		t.Error(err)
	} else if !rcv {
		t.Errorf("expected true,received %v", rcv)
	}
	if rcv, err := GetBoolOpts(context.Background(), "cgrates.org", ev2.AsDataProvider(), nil, fltrs, cgrCfg.AttributeSCfg().Opts.ProfileIgnoreFilters); err != nil {
		t.Error(err)
	} else if rcv != config.AttributesProfileIgnoreFiltersDftOpt {
		t.Errorf("expected %v,received %v", config.AttributesProcessRunsDftOpt, rcv)
	}
}

func TestSessionDynamicOptsFromJson(t *testing.T) {
	cfgJSONStr := `{
		"sessions": {
			"enabled": true,
			"listen_bijson": "127.0.0.1:2018",
			"replication_conns": ["*localhost"],
			"store_session_costs": true,
            "min_dur_low_balance": "1s",
			"client_protocol": 2.0,
			"terminate_attempts": 10,
			"opts": {
			  "*accounts": [
		      	{
		      		"FilterIDs": ["*string:~*req.Account:1001"],
		      		"Value": "~*opts.*accounts"
		      	}
		      ],
		      "*attributes": [
		      	{
		      		"FilterIDs": ["*string:~*req.Account:1001"],
		      		"Value": "~*opts.*attributes"
		      	}
		      ],
				"*ttl": [
					{
				        "FilterIDs": ["*string:~*req.Account:1001"],
						"Value": "~*req.TTL",
					},
				],
				"*debitInterval": [
					{
				        "FilterIDs": ["*string:~*req.Account:1001"],
						"Value": "~*req.Usage",
					},
				],
			},
		},
	}`

	cgrCfg, err := config.NewCGRConfigFromJSONStringWithDefaults(cfgJSONStr)
	if err != nil {
		t.Error(err)
	}
	ev := &utils.CGREvent{
		Tenant: utils.CGRateSorg,
		ID:     "testIDEvent",
		Event: map[string]any{
			utils.AccountField: "1001",
			utils.Usage:        8 * time.Second,
			utils.TTL:          "1s",
		},
		APIOpts: map[string]any{
			utils.MetaAttributes: true,
			utils.MetaAccounts:   true,
		},
	}
	ev2 := &utils.CGREvent{
		Tenant: utils.CGRateSorg,
		ID:     "testIDEvent",
		Event: map[string]any{
			utils.AccountField: "1002",
			utils.Usage:        8 * time.Second,
			utils.TTL:          "1s",
		},
		APIOpts: map[string]any{
			utils.MetaAttributes: true,
			utils.MetaAccounts:   true,
		},
	}
	fltrs := NewFilterS(cgrCfg, nil, nil)
	if rcv, err := GetBoolOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fltrs, cgrCfg.SessionSCfg().Opts.Accounts); err != nil {
		t.Error(err)
	} else if !rcv {
		t.Errorf("expected true,received %v", rcv)
	}
	if rcv, err := GetBoolOpts(context.Background(), "cgrates.org", ev2.AsDataProvider(), nil, fltrs, cgrCfg.SessionSCfg().Opts.Accounts); err != nil {
		t.Error(err)
	} else if rcv != config.SessionsAccountsDftOpt {
		t.Errorf("expected %v,received %v", config.SessionsAccountsDftOpt, rcv)
	}

	if rcv, err := GetBoolOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fltrs, cgrCfg.SessionSCfg().Opts.Attributes); err != nil {
		t.Error(err)
	} else if !rcv {
		t.Errorf("expected true,received %v", rcv)
	}
	if rcv, err := GetBoolOpts(context.Background(), "cgrates.org", ev2.AsDataProvider(), nil, fltrs, cgrCfg.SessionSCfg().Opts.Attributes); err != nil {
		t.Error(err)
	} else if rcv != config.SessionsAttributesDftOpt {
		t.Errorf("expected %v,received %v", config.SessionsAttributesDftOpt, rcv)
	}

	if rcv, err := GetDurationOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fltrs, cgrCfg.SessionSCfg().Opts.TTL); err != nil {
		t.Error(err)
	} else if rcv != time.Second {
		t.Errorf("expected %v,received %v", time.Second, rcv)
	}
	if rcv, err := GetDurationOpts(context.Background(), "cgrates.org", ev2.AsDataProvider(), nil, fltrs, cgrCfg.SessionSCfg().Opts.TTL); err != nil {
		t.Error(err)
	} else if rcv != config.SessionsTTLDftOpt {
		t.Errorf("expected %v,received %v", config.SessionsTTLDftOpt, rcv)
	}

	if rcv, err := GetDurationOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fltrs, cgrCfg.SessionSCfg().Opts.DebitInterval); err != nil {
		t.Error(err)
	} else if rcv != (8 * time.Second) {
		t.Errorf("expected %v,received %v", 8*time.Second, rcv)
	}
	if rcv, err := GetDurationOpts(context.Background(), "cgrates.org", ev2.AsDataProvider(), nil, fltrs, cgrCfg.SessionSCfg().Opts.DebitInterval); err != nil {
		t.Error(err)
	} else if rcv != config.SessionsDebitIntervalDftOpt {
		t.Errorf("expected %v,received %v", config.SessionsDebitIntervalDftOpt, rcv)
	}
}

func TestResDynamicOptsFromJson(t *testing.T) {
	cfgJSONStr := `{
		"resources": {								
			"enabled": true,						
			"store_interval": "7m",					
			"thresholds_conns": ["*internal:*thresholds", "*conn1"],					
			"indexed_selects":true,		
			"nested_fields": true,
				"opts":{
		          "*usageID": [
		          	{
		          		"FilterIDs": ["*string:~*req.Account:1001"],
		          		"Value": "~*req.UsageID"
		          	}
		          ],
		          "*usageTTL": [
		          	{
		          		"FilterIDs": ["*string:~*req.Account:1001"],
		          		"Value": "~*req.UsageTTL"
		          	}
		          ],
		          "*units": [
		          	{
		          		"FilterIDs": ["*string:~*req.Account:1001"],
		          		"Value": "~*opts.*units"
		          	}
		          ]
	           }				
		},
	}`

	cgrCfg, err := config.NewCGRConfigFromJSONStringWithDefaults(cfgJSONStr)
	if err != nil {
		t.Error(err)
	}
	ev := &utils.CGREvent{
		Tenant: utils.CGRateSorg,
		ID:     "testIDEvent",
		Event: map[string]any{
			utils.AccountField: "1001",
			"UsageID":          "UsgID3232",
			"UsageTTL":         3 * time.Second,
		},
		APIOpts: map[string]any{
			"*units": 23.22,
		},
	}
	ev2 := &utils.CGREvent{
		Tenant: utils.CGRateSorg,
		ID:     "testIDEvent",
		Event: map[string]any{
			utils.AccountField: "1002",
			"UsageID":          "UsgID3232",
			"UsageTTL":         3 * time.Second,
		},
		APIOpts: map[string]any{
			"*units": 23.22,
		},
	}
	fltrs := NewFilterS(cgrCfg, nil, nil)
	if rcv, err := GetStringOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fltrs, cgrCfg.ResourceSCfg().Opts.UsageID); err != nil {
		t.Error(err)
	} else if rcv != "UsgID3232" {
		t.Errorf("expected %s,received %s", "UsgID3232", rcv)
	}
	if rcv, err := GetStringOpts(context.Background(), "cgrates.org", ev2.AsDataProvider(), nil, fltrs, cgrCfg.ResourceSCfg().Opts.UsageID); err != nil {
		t.Error(err)
	} else if rcv != config.ResourcesUsageIDDftOpt {
		t.Errorf("expected %s,received %s", config.ResourcesUsageTTLDftOpt, rcv)
	}

	if rcv, err := GetDurationOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fltrs, cgrCfg.ResourceSCfg().Opts.UsageTTL); err != nil {
		t.Error(err)
	} else if rcv != 3*time.Second {
		t.Errorf("expected %v,received %d", 3*time.Second, rcv)
	}
	if rcv, err := GetDurationOpts(context.Background(), "cgrates.org", ev2.AsDataProvider(), nil, fltrs, cgrCfg.ResourceSCfg().Opts.UsageTTL); err != nil {
		t.Error(err)
	} else if rcv != config.ResourcesUsageTTLDftOpt {
		t.Errorf("expected %d,received %d", config.ResourcesUsageTTLDftOpt, rcv)
	}

	if rcv, err := GetFloat64Opts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fltrs, cgrCfg.ResourceSCfg().Opts.Units); err != nil {
		t.Error(err)
	} else if rcv != 23.22 {
		t.Errorf("expected %f,received %f", 23.22, rcv)
	}
	if rcv, err := GetFloat64Opts(context.Background(), "cgrates.org", ev2.AsDataProvider(), nil, fltrs, cgrCfg.ResourceSCfg().Opts.Units); err != nil {
		t.Error(err)
	} else if rcv != config.ResourcesUnitsDftOpt {
		t.Errorf("expected %v,received %v", config.ResourcesUnitsDftOpt, rcv)
	}
}

func TestRoutesDynamicOptsFromJson(t *testing.T) {
	routeJsnStr := `{
		"routes": {
			"enabled": true,
			"indexed_selects":false,
             "opts":{
		         "*usage": [
		         	{
		         		"FilterIDs": ["*string:~*req.Account:1001"],
		         		"Value": "~*req.Usage"
		         	},
					{
		         		"FilterIDs": ["*string:~*req.Account:1002"],
		         		"Value": 15555
		         	},
					{
		         		"FilterIDs": ["*string:~*req.Account:1003"],
		         		"Value": "5m"
		         	},
					{
		         		"FilterIDs": ["*string:~*req.Account:1004"],
		         		"Value": "~*opts.*usage"
		         	},
		         ]
	            }
		}
	}`

	cgrCfg, err := config.NewCGRConfigFromJSONStringWithDefaults(routeJsnStr)
	if err != nil {
		t.Error(err)
	}
	ev := &utils.CGREvent{
		Tenant: utils.CGRateSorg,
		ID:     "testIDEvent",
		Event: map[string]any{
			utils.AccountField: "1001",
			utils.Usage:        43364.4,
		},
		APIOpts: map[string]any{
			"*usage": "12m",
		},
	}
	var dec decimal.Big
	dec.SetFloat64(43364.4)

	fltrs := NewFilterS(cgrCfg, nil, nil)
	if rcv, err := GetDecimalBigOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fltrs, cgrCfg.RouteSCfg().Opts.Usage); err != nil {
		t.Error(err)
	} else if rcv.Cmp(&dec) != 0 {
		t.Errorf("expected %v,received %v", dec.String(), rcv.String())
	}
	ev.Event[utils.AccountField] = 1002
	dec.SetUint64(15555)
	if rcv, err := GetDecimalBigOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fltrs, cgrCfg.RouteSCfg().Opts.Usage); err != nil {
		t.Error(err)
	} else if rcv.Cmp(&dec) != 0 {
		t.Errorf("expected %v,received %v", dec.String(), rcv.String())
	}
	ev.Event[utils.AccountField] = 1003
	dec = *decimal.New(int64(5*time.Minute), 0)
	if rcv, err := GetDecimalBigOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fltrs, cgrCfg.RouteSCfg().Opts.Usage); err != nil {
		t.Error(err)
	} else if rcv.Cmp(&dec) != 0 {
		t.Errorf("expected %v,received %v", dec.String(), rcv.String())
	}
	ev.Event[utils.AccountField] = 1004
	dec = *decimal.New(int64(12*time.Minute), 0)
	if rcv, err := GetDecimalBigOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fltrs, cgrCfg.RouteSCfg().Opts.Usage); err != nil {
		t.Error(err)
	} else if rcv.Cmp(&dec) != 0 {
		t.Errorf("expected %v,received %v", dec.String(), rcv.String())
	}
	ev.Event[utils.AccountField] = 1005
	if rcv, err := GetDecimalBigOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fltrs, cgrCfg.RouteSCfg().Opts.Usage); err != nil {
		t.Error(err)
	} else if rcv.Cmp(config.RatesUsageDftOpt) != 0 {
		t.Errorf("expected %v,received %v", config.RatesUsageDftOpt.String(), rcv.String())
	}
}

func TestLibFiltersGetDecimalBigOptsFilterCheckErr(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{},
	}
	strOpts := []*config.DynamicInterfaceOpt{
		// function will return error after trying to parse the filter
		{
			FilterIDs: []string{"*string.invalid:filter"},
			Tenant:    "cgrates.org",
			Value:     -1,
		},
	}
	dynOpts, _ := config.IfaceToDecimalBigDynamicOpts(strOpts)
	experr := `inline parse error for string: <*string.invalid:filter>`
	if _, err := GetDecimalBigOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		utils.OptsRatesUsage); err == nil ||
		err.Error() != experr {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
	}
}

func TestLibFiltersGetDecimalBigOptsReturnDefaultOpt(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{},
	}
	strOpts := []*config.DynamicInterfaceOpt{
		{
			FilterIDs: []string{"*string:~*req.Account:1002"},
			Tenant:    "cgrates.org",
			Value:     -1,
		},
		{Value: config.RatesUsageDftOpt},
	}
	dynOpts, err := config.IfaceToDecimalBigDynamicOpts(strOpts)
	if err != nil {
		t.Fatal(err)
	}
	if rcv, err := GetDecimalBigOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		utils.OptsRatesUsage); err != nil {
		t.Error(err)
	} else if rcv.Cmp(config.RatesUsageDftOpt) != 0 {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", config.RatesUsageDftOpt, rcv)
	}
}

func TestLibFiltersGetDecimalBigOptsReturnOptFromAPIOpts(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{
			utils.OptsRatesUsage: decimal.New(4321, 5),
		},
	}

	strOpts := []*config.DynamicInterfaceOpt{
		// will never get to this opt because it will return once it
		// finds the one set in APIOpts
		{
			FilterIDs: []string{"*string:~*req.Account:1001"},
			Tenant:    "cgrates.org",
			Value:     decimal.New(1234, 5),
		},
	}
	dynOpts, _ := config.IfaceToDecimalBigDynamicOpts(strOpts)

	expected := decimal.New(4321, 5)
	if rcv, err := GetDecimalBigOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		"nonExistingAPIOpt", utils.OptsRatesUsage); err != nil {
		t.Error(err)
	} else if rcv.Cmp(expected) != 0 {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
	}
}

func TestLibFiltersGetIntPointerOptsReturnConfigOpt(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{},
	}
	dynOpts := []*config.DynamicIntPointerOpt{
		// tenant will not be recognized, will ignore this opt
		config.NewDynamicIntPointerOpt([]string{"*string:~*req.Account:1001"}, "cgrates.net", utils.IntPointer(3), nil),
		// filter will not pass, will ignore this opt
		config.NewDynamicIntPointerOpt([]string{"*string:~*req.Account:1002"}, "cgrates.org", utils.IntPointer(4), nil),
		config.NewDynamicIntPointerOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", utils.IntPointer(5), nil),
	}

	expected := 5
	if rcv, err := GetIntPointerOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		utils.OptsRoutesProfilesCount); err != nil {
		t.Error(err)
	} else if *rcv != expected {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, *rcv)
	}
}

func TestLibFiltersGetIntPointerOptsFilterCheckErr(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{},
	}
	dynOpts := []*config.DynamicIntPointerOpt{
		// function will return error after trying to parse the filter
		config.NewDynamicIntPointerOpt([]string{"*string.invalid:filter"}, "cgrates.org", utils.IntPointer(4), nil),
	}

	experr := `inline parse error for string: <*string.invalid:filter>`
	if _, err := GetIntPointerOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		utils.OptsRoutesProfilesCount); err == nil ||
		err.Error() != experr {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
	}
}

func TestLibFiltersGetIntPointerOptsReturnDft(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{},
	}
	dynOpts := []*config.DynamicIntPointerOpt{
		// filter will not pass, will ignore this opt
		config.NewDynamicIntPointerOpt([]string{"*string:~*req.Account:1002"}, "cgrates.org", utils.IntPointer(4), nil),
	}

	if rcv, err := GetIntPointerOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		utils.OptsRoutesProfilesCount); err != nil {
		t.Error(err)
	} else if rcv != nil {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", nil, rcv)
	}
}

func TestLibFiltersGetIntPointerOptsReturnOptFromAPIOptsOK(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{
			utils.OptsRoutesProfilesCount: 6,
		},
	}
	dynOpts := []*config.DynamicIntPointerOpt{
		// will never get to this opt because it will return once it
		// finds the one set in APIOpts
		config.NewDynamicIntPointerOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", utils.IntPointer(5), nil),
	}

	expected := 6
	if rcv, err := GetIntPointerOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		"nonExistingAPIOpt", utils.OptsRoutesProfilesCount); err != nil {
		t.Error(err)
	} else if *rcv != expected {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
	}
}

func TestLibFiltersGetIntPointerOptsReturnOptFromAPIOptsErr(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	ev := &utils.CGREvent{
		Tenant: "cgrates.org",
		ID:     "TestEvent",
		Event: map[string]any{
			utils.AccountField: 1001,
		},
		APIOpts: map[string]any{
			utils.OptsRoutesProfilesCount: true,
		},
	}
	dynOpts := []*config.DynamicIntPointerOpt{
		// will never get to this opt because it will return once it
		// finds the one set in APIOpts
		config.NewDynamicIntPointerOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", utils.IntPointer(5), nil),
	}

	experr := `cannot convert field<bool>: true to int`
	if _, err := GetIntPointerOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), nil, fS, dynOpts,
		"nonExistingAPIOpt", utils.OptsRoutesProfilesCount); err == nil || err.Error() != experr {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
	}
}

func TestLibFiltersGetDurationOptsFromMultipleMapsReturnConfigOpt(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	eventStart := map[string]any{
		utils.AccountField: 1001,
	}
	apiOpts := map[string]any{}
	startOpts := map[string]any{}
	dynOpts := []*config.DynamicDurationOpt{
		// tenant will not be recognized, will ignore this opt
		config.NewDynamicDurationOpt([]string{"*string:~*req.Account:1001"}, "cgrates.net", time.Millisecond, nil),
		// filter will not pass, will ignore this opt
		config.NewDynamicDurationOpt([]string{"*string:~*req.Account:1002"}, "cgrates.net", time.Second, nil),
		config.NewDynamicDurationOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", time.Minute, nil),
	}

	expected := time.Minute
	if rcv, err := GetDurationOptsFromMultipleMaps(context.Background(), "cgrates.org", eventStart, apiOpts, startOpts, fS, dynOpts,
		config.SessionsTTLDftOpt, utils.OptsSesTTL); err != nil {
		t.Error(err)
	} else if rcv != expected {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
	}
}

func TestLibFiltersGetDurationOptsFromMultipleMapsFilterCheckErr(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	eventStart := map[string]any{
		utils.AccountField: 1001,
	}
	apiOpts := map[string]any{}
	startOpts := map[string]any{}
	dynOpts := []*config.DynamicDurationOpt{
		// function will return error after trying to parse the filter
		config.NewDynamicDurationOpt([]string{"*string.invalid:filter"}, "cgrates.org", time.Second, nil),
	}

	experr := `inline parse error for string: <*string.invalid:filter>`
	if _, err := GetDurationOptsFromMultipleMaps(context.Background(), "cgrates.org", eventStart, apiOpts, startOpts, fS, dynOpts,
		config.SessionsTTLDftOpt, utils.OptsSesTTL); err == nil ||
		err.Error() != experr {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
	}
}

func TestLibFiltersGetDurationOptsFromMultipleMapsErrNotFound(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	eventStart := map[string]any{
		utils.AccountField: 1001,
	}
	apiOpts := map[string]any{}
	startOpts := map[string]any{}
	dynOpts := []*config.DynamicDurationOpt{
		// filter will not pass, will ignore this opt
		config.NewDynamicDurationOpt([]string{"*string:~*req.Account:1002"}, "cgrates.org", time.Second, nil),
	}

	if rcv, err := GetDurationOptsFromMultipleMaps(context.Background(), "cgrates.org", eventStart, apiOpts, startOpts, fS, dynOpts,
		config.SessionsTTLDftOpt, utils.OptsSesTTL); err != nil {
		t.Error(err)
	} else if rcv != config.SessionsTTLDftOpt {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", config.SessionsTTLDftOpt, rcv)
	}
}

func TestLibFiltersGetDurationOptsFromMultipleMapsReturnOptFromAPIOptsOK(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	eventStart := map[string]any{
		utils.AccountField: 1001,
	}
	apiOpts := map[string]any{
		utils.OptsSesTTL: time.Hour,
	}
	startOpts := map[string]any{}
	dynOpts := []*config.DynamicDurationOpt{
		// will never get to this opt because it will return once it
		// finds the one set in APIOpts
		config.NewDynamicDurationOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", time.Minute, nil),
	}

	expected := time.Hour
	if rcv, err := GetDurationOptsFromMultipleMaps(context.Background(), "cgrates.org", eventStart, apiOpts, startOpts, fS, dynOpts,
		config.SessionsTTLDftOpt, utils.OptsSesTTL); err != nil {
		t.Error(err)
	} else if rcv != expected {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
	}
}

func TestLibFiltersGetDurationOptsFromMultipleMapsReturnOptFromAPIOptsErr(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	eventStart := map[string]any{
		utils.AccountField: 1001,
	}
	apiOpts := map[string]any{
		utils.OptsSesTTL: true,
	}
	startOpts := map[string]any{}
	dynOpts := []*config.DynamicDurationOpt{
		// will never get to this opt because it will return once it
		// finds the one set in APIOpts
		config.NewDynamicDurationOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", time.Minute, nil),
	}

	experr := `cannot convert field: true to time.Duration`
	if _, err := GetDurationOptsFromMultipleMaps(context.Background(), "cgrates.org", eventStart, apiOpts, startOpts, fS, dynOpts,
		config.SessionsTTLDftOpt, utils.OptsSesTTL); err == nil || err.Error() != experr {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
	}
}

func TestLibFiltersGetDurationOptsFromMultipleMapsReturnOptFromStartOptsOK(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	eventStart := map[string]any{
		utils.AccountField: 1001,
	}
	apiOpts := map[string]any{}
	startOpts := map[string]any{
		utils.OptsSesTTL: time.Hour,
	}
	dynOpts := []*config.DynamicDurationOpt{
		// will never get to this opt because it will return once it
		// finds the one set in APIOpts
		config.NewDynamicDurationOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", time.Minute, nil),
	}

	expected := time.Hour
	if rcv, err := GetDurationOptsFromMultipleMaps(context.Background(), "cgrates.org", eventStart, apiOpts, startOpts, fS, dynOpts,
		config.SessionsTTLDftOpt, utils.OptsSesTTL); err != nil {
		t.Error(err)
	} else if rcv != expected {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
	}
}

func TestLibFiltersGetDurationOptsFromMultipleMapsReturnOptFromStartOptsErr(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	eventStart := map[string]any{
		utils.AccountField: 1001,
	}
	apiOpts := map[string]any{}
	startOpts := map[string]any{
		utils.OptsSesTTL: true,
	}
	dynOpts := []*config.DynamicDurationOpt{
		// will never get to this opt because it will return once it
		// finds the one set in APIOpts
		config.NewDynamicDurationOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", time.Minute, nil),
	}

	experr := `cannot convert field: true to time.Duration`
	if _, err := GetDurationOptsFromMultipleMaps(context.Background(), "cgrates.org", eventStart, apiOpts, startOpts, fS, dynOpts,
		config.SessionsTTLDftOpt, utils.OptsSesTTL); err == nil || err.Error() != experr {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
	}
}

func TestLibFiltersGetDurationPointerOptsFromMultipleMapsReturnConfigOpt(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	eventStart := map[string]any{
		utils.AccountField: 1001,
	}
	apiOpts := map[string]any{}
	startOpts := map[string]any{}
	dynOpts := []*config.DynamicDurationPointerOpt{
		// tenant will not be recognized, will ignore this opt
		config.NewDynamicDurationPointerOpt([]string{"*string:~*req.Account:1001"}, "cgrates.net", utils.DurationPointer(time.Millisecond), nil),
		// filter will not pass, will ignore this opt
		config.NewDynamicDurationPointerOpt([]string{"*string:~*req.Account:1002"}, "cgrates.org", utils.DurationPointer(time.Second), nil),
		config.NewDynamicDurationPointerOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", utils.DurationPointer(time.Minute), nil),
	}

	expected := time.Minute
	if rcv, err := GetDurationPointerOptsFromMultipleMaps(context.Background(), "cgrates.org", eventStart, apiOpts, startOpts, fS, dynOpts,
		utils.OptsSesTTLUsage); err != nil {
		t.Error(err)
	} else if *rcv != expected {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
	}
}

func TestLibFiltersGetDurationPointerOptsFromMultipleMapsFilterCheckErr(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	eventStart := map[string]any{
		utils.AccountField: 1001,
	}
	apiOpts := map[string]any{}
	startOpts := map[string]any{}
	dynOpts := []*config.DynamicDurationPointerOpt{
		// function will return error after trying to parse the filter
		config.NewDynamicDurationPointerOpt([]string{"*string.invalid:filter"}, "cgrates.org", utils.DurationPointer(time.Second), nil),
	}

	experr := `inline parse error for string: <*string.invalid:filter>`
	if _, err := GetDurationPointerOptsFromMultipleMaps(context.Background(), "cgrates.org", eventStart, apiOpts, startOpts, fS, dynOpts,
		utils.OptsSesTTLUsage); err == nil ||
		err.Error() != experr {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
	}
}

func TestLibFiltersGetDurationPointerOptsFromMultipleMapsReturnDft(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	eventStart := map[string]any{
		utils.AccountField: 1001,
	}
	apiOpts := map[string]any{}
	startOpts := map[string]any{}
	dynOpts := []*config.DynamicDurationPointerOpt{
		// filter will not pass, will ignore this opt
		config.NewDynamicDurationPointerOpt([]string{"*string:~*req.Account:1002"}, "cgrates.org", utils.DurationPointer(time.Second), nil),
	}

	if _, err := GetDurationPointerOptsFromMultipleMaps(context.Background(), "cgrates.org", eventStart, apiOpts, startOpts, fS, dynOpts,
		utils.OptsSesTTLUsage); err != nil {
		t.Error(err)
	}
}

func TestLibFiltersGetDurationPointerOptsFromMultipleMapsReturnOptFromAPIOptsOK(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	eventStart := map[string]any{
		utils.AccountField: 1001,
	}
	apiOpts := map[string]any{
		utils.OptsSesTTLUsage: time.Hour,
	}
	startOpts := map[string]any{}
	dynOpts := []*config.DynamicDurationPointerOpt{
		// will never get to this opt because it will return once it
		// finds the one set in APIOpts
		config.NewDynamicDurationPointerOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", utils.DurationPointer(time.Minute), nil),
	}

	expected := time.Hour
	if rcv, err := GetDurationPointerOptsFromMultipleMaps(context.Background(), "cgrates.org", eventStart, apiOpts, startOpts, fS, dynOpts,
		utils.OptsSesTTLUsage); err != nil {
		t.Error(err)
	} else if *rcv != expected {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
	}
}

func TestLibFiltersGetDurationPointerOptsFromMultipleMapsReturnOptFromAPIOptsErr(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	eventStart := map[string]any{
		utils.AccountField: 1001,
	}
	apiOpts := map[string]any{
		utils.OptsSesTTLUsage: true,
	}
	startOpts := map[string]any{}
	dynOpts := []*config.DynamicDurationPointerOpt{
		// will never get to this opt because it will return once it
		// finds the one set in APIOpts
		config.NewDynamicDurationPointerOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", utils.DurationPointer(time.Minute), nil),
	}

	experr := `cannot convert field: true to time.Duration`
	if _, err := GetDurationPointerOptsFromMultipleMaps(context.Background(), "cgrates.org", eventStart, apiOpts, startOpts, fS, dynOpts,
		utils.OptsSesTTLUsage); err == nil || err.Error() != experr {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
	}
}

func TestLibFiltersGetDurationPointerOptsFromMultipleMapsReturnOptFromStartOptsOK(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	eventStart := map[string]any{
		utils.AccountField: 1001,
	}
	apiOpts := map[string]any{}
	startOpts := map[string]any{
		utils.OptsSesTTLUsage: time.Hour,
	}
	dynOpts := []*config.DynamicDurationPointerOpt{
		// will never get to this opt because it will return once it
		// finds the one set in APIOpts
		config.NewDynamicDurationPointerOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", utils.DurationPointer(time.Minute), nil),
	}

	expected := time.Hour
	if rcv, err := GetDurationPointerOptsFromMultipleMaps(context.Background(), "cgrates.org", eventStart, apiOpts, startOpts, fS, dynOpts,
		utils.OptsSesTTLUsage); err != nil {
		t.Error(err)
	} else if *rcv != expected {
		t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
	}
}

func TestLibFiltersGetDurationPointerOptsFromMultipleMapsReturnOptFromStartOptsErr(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	eventStart := map[string]any{
		utils.AccountField: 1001,
	}
	apiOpts := map[string]any{}
	startOpts := map[string]any{
		utils.OptsSesTTLUsage: true,
	}
	dynOpts := []*config.DynamicDurationPointerOpt{
		// will never get to this opt because it will return once it
		// finds the one set in APIOpts
		config.NewDynamicDurationPointerOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", utils.DurationPointer(time.Minute), nil),
	}

	experr := `cannot convert field: true to time.Duration`
	if _, err := GetDurationPointerOptsFromMultipleMaps(context.Background(), "cgrates.org", eventStart, apiOpts, startOpts, fS, dynOpts,
		utils.OptsSesTTLUsage); err == nil || err.Error() != experr {
		t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
	}
}

func TestGetBoolOptsFieldAsInterfaceErr(t *testing.T) {

	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	dynOpts := []*config.DynamicBoolOpt{
		config.NewDynamicBoolOpt([]string{"*string:~*req.Account:1"}, "cgrates.org", false, nil),
	}

	if _, err := GetBoolOpts(context.Background(), "cgrates.org", new(mockDP), nil, fS, dynOpts,
		"nonExistingAPIOpt", utils.MetaProfileIgnoreFilters); err != utils.ErrAccountNotFound {
		t.Errorf("Expecting error <%+v>,\n Recevied  error <%+v>", utils.ErrAccountNotFound, err)
	}

}

func TestGetBoolOptsCantCastErr(t *testing.T) {
	cfg := config.NewDefaultCGRConfig()
	dataDB, _ := NewInternalDB(nil, nil, nil, nil)
	dbCM := NewDBConnManager(map[string]DataDB{utils.MetaDefault: dataDB}, cfg.DbCfg())
	dm := NewDataManager(dbCM, cfg, nil)
	fS := NewFilterS(cfg, nil, dm)
	dynOpts := []*config.DynamicBoolOpt{
		config.NewDynamicBoolOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", false, nil),
	}
	if _, err := GetBoolOpts(context.Background(), "cgrates.org", utils.StringSet{utils.MetaOpts: {}}, nil, fS, dynOpts,
		"nonExistingAPIOpt", utils.MetaProfileIgnoreFilters); err.Error() != "cannot convert to map[string]any" {
		t.Errorf("Expecting error <%+v>,\n Recevied  error <%+v>", utils.ErrCastFailed, err)
	}

}

func TestOptIfaceFromDP(t *testing.T) {
	tests := []struct {
		name   string
		values []string
		exp    any
		err    error
	}{
		{name: "TestOptBoolValue", values: []string{utils.MetaProfileIgnoreFilters}, exp: true, err: nil},
		{name: "TestOptBoolValue", values: []string{utils.OptsRoutesIgnoreErrors}, exp: true, err: nil},
		{name: "TestOptFloat64Value", values: []string{utils.OptsAttributesProcessRuns}, exp: 5, err: nil},
		{name: "TestOptIntValue", values: []string{utils.OptsResourcesUnits}, exp: float64(23.1), err: nil},
		{name: "TestOptIntValue", values: []string{utils.OptsAttributesProfileRuns}, exp: 3, err: nil},
		{name: "TestOptStringValue", values: []string{utils.OptsRatesStartTime}, exp: "2021-01-01T00:00:00Z", err: nil},
		{name: "TestOptFloat64Value", values: []string{utils.OptsResourcesUsageID}, exp: "RES1", err: nil},
		{name: "TestFieldNotExist", values: []string{"field1"}, err: utils.ErrNotFound},
	}
	cgrEv := utils.MapStorage{
		utils.MetaReq: map[string]any{},
		utils.MetaOpts: map[string]any{
			utils.MetaProfileIgnoreFilters:  true,
			utils.OptsRoutesIgnoreErrors:    true,
			utils.OptsAttributesProcessRuns: 5,
			utils.OptsAttributesProfileRuns: 3,
			utils.OptsRatesStartTime:        "2021-01-01T00:00:00Z",
			utils.OptsResourcesUsageID:      "RES1",
			utils.OptsResourcesUnits:        23.1,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if rcv, err := optIfaceFromDP(cgrEv, nil, tt.values); err != tt.err {
				t.Errorf("expected: <%v>,\nreceived: <%v>", tt.err, err)
			} else if rcv != tt.exp {
				t.Errorf("expected: <%v>,\nreceived: <%v>", tt.exp, rcv)
			}
		})
	}
}

func TestConvertOptsToMapStringAny(t *testing.T) {
	t.Run("MapEvent_Account", func(t *testing.T) {
		accountEvent := MapEvent{
			"Account":        "1001",
			"Tenant":         "cgrates.org",
			"BalanceMap":     map[string]float64{"*monetary": 100.5},
			"ActionTriggers": []string{"STANDARD_TRIGGERS"},
		}

		result, err := ConvertOptsToMapStringAny(accountEvent)
		if err != nil {
			t.Fatalf("Expected no error, got: %v", err)
		}

		if len(result) != 4 {
			t.Fatalf("Expected 4 keys, got %d", len(result))
		}

		if result["Account"] != "1001" {
			t.Errorf("Expected Account=1001, got %v", result["Account"])
		}
		if result["Tenant"] != "cgrates.org" {
			t.Errorf("Expected Tenant=cgrates.org, got %v", result["Tenant"])
		}
	})

	t.Run("MapEvent_CDR", func(t *testing.T) {
		cdrEvent := MapEvent{
			"CGRID":       "cdr123456789",
			"OrderID":     12345,
			"OriginHost":  "192.168.1.1",
			"Source":      "SessionS",
			"OriginID":    "session_001",
			"ToR":         "*voice",
			"RequestType": "*prepaid",
			"Tenant":      "cgrates.org",
			"Category":    "call",
			"Account":     "1001",
			"Subject":     "1001",
			"Destination": "+4986517174963",
			"SetupTime":   "2024-01-15T10:30:00Z",
			"AnswerTime":  "2024-01-15T10:30:05Z",
			"Usage":       300.0,
			"Cost":        2.5,
		}

		result, err := ConvertOptsToMapStringAny(cdrEvent)
		if err != nil {
			t.Fatalf("Expected no error, got: %v", err)
		}

		if result["CGRID"] != "cdr123456789" {
			t.Errorf("Expected CGRID=cdr123456789, got %v", result["CGRID"])
		}
		if result["Account"] != "1001" {
			t.Errorf("Expected Account=1001, got %v", result["Account"])
		}
		if result["Usage"] != 300.0 {
			t.Errorf("Expected Usage=300.0, got %v", result["Usage"])
		}
	})

	t.Run("MapStorage_RatingProfile", func(t *testing.T) {
		ratingStorage := utils.MapStorage{
			"Tenant":   "cgrates.org",
			"Category": "call",
			"Subject":  "*any",
			"RatingPlanActivations": []map[string]any{
				{
					"ActivationTime": "2024-01-01T00:00:00Z",
					"RatingPlanId":   "RP_RETAIL",
					"FallbackKeys":   []string{"*any"},
				},
			},
		}

		result, err := ConvertOptsToMapStringAny(ratingStorage)
		if err != nil {
			t.Fatalf("Expected no error, got: %v", err)
		}

		if result["Tenant"] != "cgrates.org" {
			t.Errorf("Expected Tenant=cgrates.org, got %v", result["Tenant"])
		}
		if result["Subject"] != "*any" {
			t.Errorf("Expected Subject=*any, got %v", result["Subject"])
		}
	})

	t.Run("MapStorage_Destination", func(t *testing.T) {
		destStorage := utils.MapStorage{
			"Id":       "DST_GERMANY",
			"Prefixes": []string{"+49", "+4915", "+4916"},
		}

		result, err := ConvertOptsToMapStringAny(destStorage)
		if err != nil {
			t.Fatalf("Expected no error, got: %v", err)
		}

		if result["Id"] != "DST_GERMANY" {
			t.Errorf("Expected Id=DST_GERMANY, got %v", result["Id"])
		}

		prefixes, ok := result["Prefixes"].([]string)
		if !ok || len(prefixes) != 3 {
			t.Errorf("Expected Prefixes to be []string with 3 elements, got %v", result["Prefixes"])
		}
	})

	t.Run("DirectMapStringAny", func(t *testing.T) {
		directMap := map[string]any{
			"ActionId":   "TOPUP_MONETARY_10",
			"ActionType": "*topup_reset",
			"ExtraParameters": map[string]string{
				"*balance_type": "*monetary",
				"*units":        "10",
			},
		}

		result, err := ConvertOptsToMapStringAny(directMap)
		if err != nil {
			t.Fatalf("Expected no error, got: %v", err)
		}

		if !reflect.DeepEqual(result, directMap) {
			t.Errorf("Expected result to be identical to input map")
		}

		if result["ActionId"] != "TOPUP_MONETARY_10" {
			t.Errorf("Expected ActionId=TOPUP_MONETARY_10, got %v", result["ActionId"])
		}
	})

	t.Run("EmptyMapEvent", func(t *testing.T) {
		emptyEvent := MapEvent{}

		result, err := ConvertOptsToMapStringAny(emptyEvent)
		if err != nil {
			t.Fatalf("Expected no error, got: %v", err)
		}

		if len(result) != 0 {
			t.Errorf("Expected empty map, got %v", result)
		}
	})

	t.Run("EmptyMapStorage", func(t *testing.T) {
		emptyStorage := utils.MapStorage{}

		result, err := ConvertOptsToMapStringAny(emptyStorage)
		if err != nil {
			t.Fatalf("Expected no error, got: %v", err)
		}

		if len(result) != 0 {
			t.Errorf("Expected empty map, got %v", result)
		}
	})

	t.Run("UnsupportedType_String", func(t *testing.T) {
		_, err := ConvertOptsToMapStringAny("invalid_string")
		if err == nil {
			t.Fatal("Expected error for unsupported type, got nil")
		}

		expectedErr := "cannot convert to map[string]any"
		if err.Error() != expectedErr {
			t.Errorf("Expected error '%s', got '%s'", expectedErr, err.Error())
		}
	})

	t.Run("UnsupportedType_Int", func(t *testing.T) {
		_, err := ConvertOptsToMapStringAny(12345)
		if err == nil {
			t.Fatal("Expected error for unsupported type, got nil")
		}
	})

	t.Run("UnsupportedType_Slice", func(t *testing.T) {
		_, err := ConvertOptsToMapStringAny([]string{"account1", "account2"})
		if err == nil {
			t.Fatal("Expected error for unsupported type, got nil")
		}
	})

	t.Run("MapEvent_Session", func(t *testing.T) {
		sessionEvent := MapEvent{
			"CGRID":           "session_abc123",
			"Tenant":          "cgrates.org",
			"Account":         "1002",
			"Destination":     "+4986517174999",
			"InitialMaxUsage": 3600.0,
			"MaxUsage":        1800.0,
			"LastUsed":        600.0,
			"LoopIndex":       0,
			"DurationIndex":   600.0,
		}

		result, err := ConvertOptsToMapStringAny(sessionEvent)
		if err != nil {
			t.Fatalf("Expected no error, got: %v", err)
		}

		if result["Account"] != "1002" {
			t.Errorf("Expected Account=1002, got %v", result["Account"])
		}
		if result["MaxUsage"] != 1800.0 {
			t.Errorf("Expected MaxUsage=1800.0, got %v", result["MaxUsage"])
		}
	})

	t.Run("MapStorage_ActionPlan", func(t *testing.T) {
		actionPlanStorage := utils.MapStorage{
			"Id": "PACKAGE_10_SHARED_A_5",
			"ActionTimings": []map[string]any{
				{
					"Uuid":      "088fc6cc-192c-4d8e-9f5e-7b9c3d2c5b8d",
					"Timing":    "*asap",
					"ActionsId": "TOPUP_RST_10",
					"Weight":    10.0,
				},
			},
		}

		result, err := ConvertOptsToMapStringAny(actionPlanStorage)
		if err != nil {
			t.Fatalf("Expected no error, got: %v", err)
		}

		if result["Id"] != "PACKAGE_10_SHARED_A_5" {
			t.Errorf("Expected Id=PACKAGE_10_SHARED_A_5, got %v", result["Id"])
		}
	})

	t.Run("MapEvent_ResourceEvent", func(t *testing.T) {
		resourceEvent := MapEvent{
			"Tenant":      "cgrates.org",
			"ID":          "RES_GRP_1",
			"UsageID":     "call_usage_001",
			"ResourceID":  "RES_ACNT_1001",
			"Units":       1.0,
			"Account":     "1001",
			"Destination": "+4986517174963",
		}

		result, err := ConvertOptsToMapStringAny(resourceEvent)
		if err != nil {
			t.Fatalf("Expected no error, got: %v", err)
		}

		if result["ID"] != "RES_GRP_1" {
			t.Errorf("Expected ID=RES_GRP_1, got %v", result["ID"])
		}
		if result["Units"] != 1.0 {
			t.Errorf("Expected Units=1.0, got %v", result["Units"])
		}
	})

	t.Run("MapStorage_ThresholdProfile", func(t *testing.T) {
		thresholdStorage := utils.MapStorage{
			"Tenant":    "cgrates.org",
			"ID":        "THD_ACNT_1001",
			"FilterIDs": []string{"*string:~*req.Account:1001"},
			"ActivationInterval": map[string]any{
				"ActivationTime": "2024-01-01T00:00:00Z",
				"ExpiryTime":     "*never",
			},
			"MaxHits":   -1,
			"MinHits":   1,
			"MinSleep":  "1s",
			"Blocker":   false,
			"Weight":    20.0,
			"ActionIDs": []string{"LOG_WARNING"},
		}

		result, err := ConvertOptsToMapStringAny(thresholdStorage)
		if err != nil {
			t.Fatalf("Expected no error, got: %v", err)
		}

		if result["ID"] != "THD_ACNT_1001" {
			t.Errorf("Expected ID=THD_ACNT_1001, got %v", result["ID"])
		}
		if result["Weight"] != 20.0 {
			t.Errorf("Expected Weight=20.0, got %v", result["Weight"])
		}
	})
}
