/*
 * Copyright 2021 CloudWeGo Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package configutil

import (
	"testing"
	"time"

	"github.com/cloudwego/kitex/internal/test"
)

type mockConfig struct {
	data map[interface{}]interface{}
}

func (c *mockConfig) Get(key string) (interface{}, bool) {
	ret, ok := c.data[key]
	return ret, ok
}

func TestConfigs_Get(t *testing.T) {
	// empty config
	emptyConfigs := configs{}
	val, ok := emptyConfigs.Get("key")
	test.Assert(t, val == nil)
	test.Assert(t, ok == false)
	val, ok = emptyConfigs.Get("")
	test.Assert(t, val == nil)
	test.Assert(t, ok == false)

	// contain single config
	singleCfg := configs{
		sources: []Config{
			&mockConfig{
				data: map[interface{}]interface{}{
					"key": "val",
				},
			},
		},
	}
	val, ok = singleCfg.Get("key")
	test.Assert(t, val == "val")
	test.Assert(t, ok == true)
	val, ok = singleCfg.Get("not_exist_key")
	test.Assert(t, val == nil)
	test.Assert(t, ok == false)
	val, ok = singleCfg.Get("")
	test.Assert(t, val == nil)
	test.Assert(t, ok == false)

	// contain multi configs
	multiCfgs := configs{
		sources: []Config{
			&mockConfig{
				data: map[interface{}]interface{}{
					"key1": "val1",
				},
			},
			&mockConfig{
				data: map[interface{}]interface{}{
					"key2": "val2",
				},
			},
			&mockConfig{
				data: map[interface{}]interface{}{
					"key2": "val3",
				},
			},
		},
	}
	val, ok = multiCfgs.Get("key2")
	test.Assert(t, val == "val3")
	test.Assert(t, ok == true)
	val, ok = multiCfgs.Get("key1")
	test.Assert(t, val == "val1")
	test.Assert(t, ok == true)
	val, ok = multiCfgs.Get("key")
	test.Assert(t, val == nil)
	test.Assert(t, ok == false)
	val, ok = multiCfgs.Get("")
	test.Assert(t, val == nil)
	test.Assert(t, ok == false)
}

func TestConfigs_AddPriorSource(t *testing.T) {
	invalidCfgs := &configs{}
	invalidCfgs.AddPriorSource(nil)
	nilVal := invalidCfgs.sources[0]
	test.Assert(t, nilVal == nil)

	cfgs := &configs{}
	c1 := &mockConfig{
		data: map[interface{}]interface{}{
			"key": "val1",
		},
	}
	cfgs.AddPriorSource(c1)
	val, ok := cfgs.Get("key")
	test.Assert(t, val == "val1", ok == true)

	c2 := &mockConfig{
		data: map[interface{}]interface{}{
			"key": "val2",
		},
	}
	cfgs.AddPriorSource(c2)
	val, ok = cfgs.Get("key")
	test.Assert(t, val == "val2", ok == true)
}

func TestDefaultConfig_Get(t *testing.T) {
	// empty config
	emptyCfg := &mockConfig{}
	emptyDefCfg := NewDefaultConfig(emptyCfg)
	val := emptyDefCfg.Get("key", "def_val")
	test.Assert(t, val.(string) == "def_val")

	// config contain element
	cfg := &mockConfig{
		data: map[interface{}]interface{}{
			"key": "val",
		},
	}
	defCfg := NewDefaultConfig(cfg)
	val = defCfg.Get("key", "def_val")
	test.Assert(t, val.(string) == "val")
	val = defCfg.Get("not_exist_key", "def_val")
	test.Assert(t, val.(string) == "def_val")
	val = defCfg.Get("", "def_val")
	test.Assert(t, val.(string) == "def_val")
}

func TestDummyConfig_Get(t *testing.T) {
	emptyCfg := NewDummyConfig()
	val, ok := emptyCfg.Get("key")
	test.Assert(t, val == nil, ok == false)
	val, ok = emptyCfg.Get("")
	test.Assert(t, val == nil, ok == false)
}

func TestRichTypeConfig_GetBool(t *testing.T) {
	// empty config
	emptyCfg := &mockConfig{}
	richCfg := NewRichTypeConfig(emptyCfg)
	val, ok := richCfg.GetBool("key")
	test.Assert(t, val == false, ok == false)
	val, ok = richCfg.GetBool("")
	test.Assert(t, val == false, ok == false)

	// contain other type element
	otherTypeCfg := &mockConfig{
		data: map[interface{}]interface{}{
			"key": "val",
		},
	}
	richCfg = NewRichTypeConfig(otherTypeCfg)
	val, ok = richCfg.GetBool("key")
	test.Assert(t, val == false, ok == false)
	val, ok = richCfg.GetBool("not_exist_key")
	test.Assert(t, val == false, ok == false)
	val, ok = richCfg.GetBool("")
	test.Assert(t, val == false, ok == false)

	// contain bool type element
	boolTypeCfg := &mockConfig{
		data: map[interface{}]interface{}{
			"key": true,
		},
	}
	richCfg = NewRichTypeConfig(boolTypeCfg)
	val, ok = richCfg.GetBool("key")
	test.Assert(t, val == true, ok == true)
	val, ok = richCfg.GetBool("not_exist_key")
	test.Assert(t, val == false, ok == false)
	val, ok = richCfg.GetBool("")
	test.Assert(t, val == false, ok == false)
}

func TestRichTypeConfig_GetDuration(t *testing.T) {
	// empty config
	emptyCfg := &mockConfig{}
	richCfg := NewRichTypeConfig(emptyCfg)
	val, ok := richCfg.GetDuration("key")
	test.Assert(t, int64(val) == 0, ok == false)
	val, ok = richCfg.GetDuration("")
	test.Assert(t, int64(val) == 0, ok == false)

	// contain other type element
	otherTypeCfg := &mockConfig{
		data: map[interface{}]interface{}{
			"key": "val",
		},
	}
	richCfg = NewRichTypeConfig(otherTypeCfg)
	val, ok = richCfg.GetDuration("key")
	test.Assert(t, int64(val) == 0, ok == false)
	val, ok = richCfg.GetDuration("not_exist_key")
	test.Assert(t, int64(val) == 0, ok == false)
	val, ok = richCfg.GetDuration("")
	test.Assert(t, int64(val) == 0, ok == false)

	// contain bool type element
	durationTypeCfg := &mockConfig{
		data: map[interface{}]interface{}{
			"key": time.Second,
		},
	}
	richCfg = NewRichTypeConfig(durationTypeCfg)
	val, ok = richCfg.GetDuration("key")
	test.Assert(t, val == time.Second, ok == true)
	val, ok = richCfg.GetDuration("not_exist_key")
	test.Assert(t, int64(val) == 0, ok == false)
	val, ok = richCfg.GetDuration("")
	test.Assert(t, int64(val) == 0, ok == false)
}

func TestRichTypeConfig_GetFloat(t *testing.T) {
	// empty config
	emptyCfg := &mockConfig{}
	richCfg := NewRichTypeConfig(emptyCfg)
	val, ok := richCfg.GetFloat("key")
	test.Assert(t, val == float64(0), ok == false)
	val, ok = richCfg.GetFloat("")
	test.Assert(t, val == float64(0), ok == false)

	// contain other type element
	otherTypeCfg := &mockConfig{
		data: map[interface{}]interface{}{
			"key": "val",
		},
	}
	richCfg = NewRichTypeConfig(otherTypeCfg)
	val, ok = richCfg.GetFloat("key")
	test.Assert(t, val == float64(0), ok == false)
	val, ok = richCfg.GetFloat("not_exist_key")
	test.Assert(t, val == float64(0), ok == false)
	val, ok = richCfg.GetFloat("")
	test.Assert(t, val == float64(0), ok == false)

	// contain bool type element
	boolTypeCfg := &mockConfig{
		data: map[interface{}]interface{}{
			"key": float64(1.23),
		},
	}
	richCfg = NewRichTypeConfig(boolTypeCfg)
	val, ok = richCfg.GetFloat("key")
	test.Assert(t, val == float64(1.23), ok == true)
	val, ok = richCfg.GetFloat("not_exist_key")
	test.Assert(t, val == float64(0), ok == false)
	val, ok = richCfg.GetFloat("")
	test.Assert(t, val == float64(0), ok == false)
}

func TestRichTypeConfig_GetInt(t *testing.T) {
	// empty config
	emptyCfg := &mockConfig{}
	richCfg := NewRichTypeConfig(emptyCfg)
	val, ok := richCfg.GetInt("key")
	test.Assert(t, val == 0, ok == false)
	val, ok = richCfg.GetInt("")
	test.Assert(t, val == 0, ok == false)

	// contain other type element
	otherTypeCfg := &mockConfig{
		data: map[interface{}]interface{}{
			"key": "val",
		},
	}
	richCfg = NewRichTypeConfig(otherTypeCfg)
	val, ok = richCfg.GetInt("key")
	test.Assert(t, val == 0, ok == false)
	val, ok = richCfg.GetInt("not_exist_key")
	test.Assert(t, val == 0, ok == false)
	val, ok = richCfg.GetInt("")
	test.Assert(t, val == 0, ok == false)

	// contain bool type element
	boolTypeCfg := &mockConfig{
		data: map[interface{}]interface{}{
			"key": 123,
		},
	}
	richCfg = NewRichTypeConfig(boolTypeCfg)
	val, ok = richCfg.GetInt("key")
	test.Assert(t, val == 123, ok == true)
	val, ok = richCfg.GetInt("not_exist_key")
	test.Assert(t, val == 0, ok == false)
	val, ok = richCfg.GetInt("")
	test.Assert(t, val == 0, ok == false)
}

func TestRichTypeConfig_GetInt64(t *testing.T) {
	// empty config
	emptyCfg := &mockConfig{}
	richCfg := NewRichTypeConfig(emptyCfg)
	val, ok := richCfg.GetInt64("key")
	test.Assert(t, val == int64(0), ok == false)
	val, ok = richCfg.GetInt64("")
	test.Assert(t, val == int64(0), ok == false)

	// contain other type element
	otherTypeCfg := &mockConfig{
		data: map[interface{}]interface{}{
			"key": "val",
		},
	}
	richCfg = NewRichTypeConfig(otherTypeCfg)
	val, ok = richCfg.GetInt64("key")
	test.Assert(t, val == int64(0), ok == false)
	val, ok = richCfg.GetInt64("not_exist_key")
	test.Assert(t, val == int64(0), ok == false)
	val, ok = richCfg.GetInt64("")
	test.Assert(t, val == int64(0), ok == false)

	// contain bool type element
	boolTypeCfg := &mockConfig{
		data: map[interface{}]interface{}{
			"key": int64(123456),
		},
	}
	richCfg = NewRichTypeConfig(boolTypeCfg)
	val, ok = richCfg.GetInt64("key")
	test.Assert(t, val == int64(123456), ok == true)
	val, ok = richCfg.GetInt64("not_exist_key")
	test.Assert(t, val == int64(0), ok == false)
	val, ok = richCfg.GetInt64("")
	test.Assert(t, val == int64(0), ok == false)
}

func TestRichTypeConfig_GetString(t *testing.T) {
	// empty config
	emptyCfg := &mockConfig{}
	richCfg := NewRichTypeConfig(emptyCfg)
	val, ok := richCfg.GetString("key")
	test.Assert(t, val == "", ok == false)
	val, ok = richCfg.GetString("")
	test.Assert(t, val == "", ok == false)

	// contain other type element
	otherTypeCfg := &mockConfig{
		data: map[interface{}]interface{}{
			"key": true,
		},
	}
	richCfg = NewRichTypeConfig(otherTypeCfg)
	val, ok = richCfg.GetString("key")
	test.Assert(t, val == "", ok == false)
	val, ok = richCfg.GetString("not_exist_key")
	test.Assert(t, val == "", ok == false)
	val, ok = richCfg.GetString("")
	test.Assert(t, val == "", ok == false)

	// contain bool type element
	boolTypeCfg := &mockConfig{
		data: map[interface{}]interface{}{
			"key": "val",
		},
	}
	richCfg = NewRichTypeConfig(boolTypeCfg)
	val, ok = richCfg.GetString("key")
	test.Assert(t, val == "val", ok == true)
	val, ok = richCfg.GetString("not_exist_key")
	test.Assert(t, val == "", ok == false)
	val, ok = richCfg.GetString("")
	test.Assert(t, val == "", ok == false)
}

func TestRichTypeDefaultConfig_GetBool(t *testing.T) {
	// empty config
	emptyCfg := &mockConfig{}
	richCfg := NewRichTypeConfig(emptyCfg)
	emptyDefRichCfg := NewRichTypeDefaultConfig(richCfg)
	val := emptyDefRichCfg.GetBool("key", true)
	test.Assert(t, val == true)
	val = emptyDefRichCfg.GetBool("", false)
	test.Assert(t, val == false)

	// contain other type element
	otherTypeCfg := &mockConfig{
		data: map[interface{}]interface{}{
			"key": "val",
		},
	}
	richCfg = NewRichTypeConfig(otherTypeCfg)
	defRichCfg := NewRichTypeDefaultConfig(richCfg)
	val = defRichCfg.GetBool("key", true)
	test.Assert(t, val == true)
	val = defRichCfg.GetBool("not_exist_key", true)
	test.Assert(t, val == true)
	val = defRichCfg.GetBool("", false)
	test.Assert(t, val == false)

	// contain bool type element
	boolTypeCfg := &mockConfig{
		data: map[interface{}]interface{}{
			"key": true,
		},
	}
	richCfg = NewRichTypeConfig(boolTypeCfg)
	defRichCfg = NewRichTypeDefaultConfig(richCfg)
	val = defRichCfg.GetBool("key", false)
	test.Assert(t, val == true)
	val = defRichCfg.GetBool("not_exist_key", true)
	test.Assert(t, val == true)
	val = defRichCfg.GetBool("", false)
	test.Assert(t, val == false)
}

func TestRichTypeDefaultConfig_GetDuration(t *testing.T) {
	// empty config
	emptyCfg := &mockConfig{}
	richCfg := NewRichTypeConfig(emptyCfg)
	emptyDefRichCfg := NewRichTypeDefaultConfig(richCfg)
	val := emptyDefRichCfg.GetDuration("key", time.Second)
	test.Assert(t, val == time.Second)
	val = emptyDefRichCfg.GetDuration("", time.Second)
	test.Assert(t, val == time.Second)

	// contain other type element
	otherTypeCfg := &mockConfig{
		data: map[interface{}]interface{}{
			"key": "val",
		},
	}
	richCfg = NewRichTypeConfig(otherTypeCfg)
	defRichCfg := NewRichTypeDefaultConfig(richCfg)
	val = defRichCfg.GetDuration("key", time.Second)
	test.Assert(t, val == time.Second)
	val = defRichCfg.GetDuration("not_exist_key", time.Second)
	test.Assert(t, val == time.Second)
	val = defRichCfg.GetDuration("", time.Second)
	test.Assert(t, val == time.Second)

	// contain bool type element
	boolTypeCfg := &mockConfig{
		data: map[interface{}]interface{}{
			"key": time.Second,
		},
	}
	richCfg = NewRichTypeConfig(boolTypeCfg)
	defRichCfg = NewRichTypeDefaultConfig(richCfg)
	val = defRichCfg.GetDuration("key", time.Minute)
	test.Assert(t, val == time.Second)
	val = defRichCfg.GetDuration("not_exist_key", time.Minute)
	test.Assert(t, val == time.Minute)
	val = defRichCfg.GetDuration("", time.Minute)
	test.Assert(t, val == time.Minute)
}

func TestRichTypeDefaultConfig_GetFloat(t *testing.T) {
	// empty config
	emptyCfg := &mockConfig{}
	richCfg := NewRichTypeConfig(emptyCfg)
	emptyDefRichCfg := NewRichTypeDefaultConfig(richCfg)
	val := emptyDefRichCfg.GetFloat("key", 1.23)
	test.Assert(t, val == 1.23)
	val = emptyDefRichCfg.GetFloat("", 2.34)
	test.Assert(t, val == 2.34)

	// contain other type element
	otherTypeCfg := &mockConfig{
		data: map[interface{}]interface{}{
			"key": "val",
		},
	}
	richCfg = NewRichTypeConfig(otherTypeCfg)
	defRichCfg := NewRichTypeDefaultConfig(richCfg)
	val = defRichCfg.GetFloat("key", 1.23)
	test.Assert(t, val == 1.23)
	val = defRichCfg.GetFloat("not_exist_key", 2.34)
	test.Assert(t, val == 2.34)
	val = defRichCfg.GetFloat("", 3.45)
	test.Assert(t, val == 3.45)

	// contain bool type element
	boolTypeCfg := &mockConfig{
		data: map[interface{}]interface{}{
			"key": 0.01,
		},
	}
	richCfg = NewRichTypeConfig(boolTypeCfg)
	defRichCfg = NewRichTypeDefaultConfig(richCfg)
	val = defRichCfg.GetFloat("key", 1.12)
	test.Assert(t, val == 0.01)
	val = defRichCfg.GetFloat("not_exist_key", 2.34)
	test.Assert(t, val == 2.34)
	val = defRichCfg.GetFloat("", 3.45)
	test.Assert(t, val == 3.45)
}

func TestRichTypeDefaultConfig_GetInt(t *testing.T) {
	// empty config
	emptyCfg := &mockConfig{}
	richCfg := NewRichTypeConfig(emptyCfg)
	emptyDefRichCfg := NewRichTypeDefaultConfig(richCfg)
	val := emptyDefRichCfg.GetInt("key", 1)
	test.Assert(t, val == 1)
	val = emptyDefRichCfg.GetInt("", 2)
	test.Assert(t, val == 2)

	// contain other type element
	otherTypeCfg := &mockConfig{
		data: map[interface{}]interface{}{
			"key": "val",
		},
	}
	richCfg = NewRichTypeConfig(otherTypeCfg)
	defRichCfg := NewRichTypeDefaultConfig(richCfg)
	val = defRichCfg.GetInt("key", 1)
	test.Assert(t, val == 1)
	val = defRichCfg.GetInt("not_exist_key", 2)
	test.Assert(t, val == 2)
	val = defRichCfg.GetInt("", 3)
	test.Assert(t, val == 3)

	// contain bool type element
	boolTypeCfg := &mockConfig{
		data: map[interface{}]interface{}{
			"key": 123,
		},
	}
	richCfg = NewRichTypeConfig(boolTypeCfg)
	defRichCfg = NewRichTypeDefaultConfig(richCfg)
	val = defRichCfg.GetInt("key", 456)
	test.Assert(t, val == 123)
	val = defRichCfg.GetInt("not_exist_key", 678)
	test.Assert(t, val == 678)
	val = defRichCfg.GetInt("", 789)
	test.Assert(t, val == 789)
}

func TestRichTypeDefaultConfig_GetInt64(t *testing.T) {
	// empty config
	emptyCfg := &mockConfig{}
	richCfg := NewRichTypeConfig(emptyCfg)
	emptyDefRichCfg := NewRichTypeDefaultConfig(richCfg)
	val := emptyDefRichCfg.GetInt64("key", 123456)
	test.Assert(t, val == 123456)
	val = emptyDefRichCfg.GetInt64("", 456789)
	test.Assert(t, val == 456789)

	// contain other type element
	otherTypeCfg := &mockConfig{
		data: map[interface{}]interface{}{
			"key": "val",
		},
	}
	richCfg = NewRichTypeConfig(otherTypeCfg)
	defRichCfg := NewRichTypeDefaultConfig(richCfg)
	val = defRichCfg.GetInt64("key", 123456)
	test.Assert(t, val == 123456)
	val = defRichCfg.GetInt64("not_exist_key", 23456)
	test.Assert(t, val == 23456)
	val = defRichCfg.GetInt64("", 45678)
	test.Assert(t, val == 45678)

	// contain bool type element
	boolTypeCfg := &mockConfig{
		data: map[interface{}]interface{}{
			"key": 987654321,
		},
	}
	richCfg = NewRichTypeConfig(boolTypeCfg)
	defRichCfg = NewRichTypeDefaultConfig(richCfg)
	val = defRichCfg.GetInt64("key", 123456789)
	test.Assert(t, val == 987654321)
	val = defRichCfg.GetInt64("not_exist_key", 123)
	test.Assert(t, val == 123)
	val = defRichCfg.GetInt64("", 456)
	test.Assert(t, val == 456)
}

func TestRichTypeDefaultConfig_GetString(t *testing.T) {
	// empty config
	emptyCfg := &mockConfig{}
	richCfg := NewRichTypeConfig(emptyCfg)
	emptyDefRichCfg := NewRichTypeDefaultConfig(richCfg)
	val := emptyDefRichCfg.GetString("key", "def_val")
	test.Assert(t, val == "def_val")
	val = emptyDefRichCfg.GetString("", "def_val")
	test.Assert(t, val == "def_val")

	// contain other type element
	otherTypeCfg := &mockConfig{
		data: map[interface{}]interface{}{
			"key": true,
		},
	}
	richCfg = NewRichTypeConfig(otherTypeCfg)
	defRichCfg := NewRichTypeDefaultConfig(richCfg)
	val = defRichCfg.GetString("key", "def_val")
	test.Assert(t, val == "def_val")
	val = defRichCfg.GetString("not_exist_key", "def_val")
	test.Assert(t, val == "def_val")
	val = defRichCfg.GetString("", "def_val")
	test.Assert(t, val == "def_val")

	// contain bool type element
	boolTypeCfg := &mockConfig{
		data: map[interface{}]interface{}{
			"key": "val",
		},
	}
	richCfg = NewRichTypeConfig(boolTypeCfg)
	defRichCfg = NewRichTypeDefaultConfig(richCfg)
	val = defRichCfg.GetString("key", "def_val")
	test.Assert(t, val == "val")
	val = defRichCfg.GetString("not_exist_key", "def_val")
	test.Assert(t, val == "def_val")
	val = defRichCfg.GetString("", "def_val")
	test.Assert(t, val == "def_val")
}
