package config

import (
	"errors"
	"fmt"
	"math/rand"
	"os"
	"reflect"
	"strings"
	"testing"
	"time"

	alchemyConfig "github.com/AlchemyTelcoSolutions/go-packages/pkg/config"
	"gotest.tools/assert"
)

func TestConfig(t *testing.T) {
	t.Run("test new should return a new config", func(t *testing.T) {
		config := New()
		var want = &Config{}
		var err error
		if !reflect.DeepEqual(config, want) {
			err = fmt.Errorf("Config_not_equals, New() = %v, want %v", config, want)
		}
		assert.NilError(t, err)
	})

	t.Run("test new should return config", func(t *testing.T) {
		new := New()
		config := new.GetConfigurations()
		var err error
		if !reflect.DeepEqual(new, config) {
			err = fmt.Errorf("Config not equals, New() = %v, want %v", new, config)
		}
		assert.NilError(t, err)
	})

	t.Run("test aws Mapping should return ConfigMap", func(t *testing.T) {
		new := New()
		alchemyConfigMap := alchemyConfig.ConfigMap{}
		awsConfigMap := new.AWSLocalMap()
		var err error
		if !reflect.DeepEqual(alchemyConfigMap, awsConfigMap) {
			err = fmt.Errorf("Config not equals, New() = %v, want %v", alchemyConfigMap, awsConfigMap)
		}
		assert.NilError(t, err)
	})

	t.Run("test fail Load configs", func(t *testing.T) {
		os.Unsetenv("ENVIRONMENT_CONFIG")
		config := New()
		assert.ErrorContains(t, config.LoadConfigs(), "ENVIRONMENT_CONFIG")
	})

	t.Run("test Success Loading configs", func(t *testing.T) {
		config := New()
		file, testPath, err := createTestConfigFile("./../../../test/")
		data := `{"grpc_server": {"enabled": false},"apollo_product": {"consumer": {"enabled": false, "sku_attributes_mapping": {"1": ["carrier", "keyboard", "area"],"2": ["storage", "bezel_size", "type"],"3": ["color", "ram/color"],"4": ["variant", "processor"]}}}}`
		createFile(testPath+"/config.json", data)
		assert.NilError(t, err)
		os.Setenv("ENVIRONMENT_CONFIG", file)
		os.Setenv("ENVIRONMENT_STORAGE", "LOCAL")
		os.Setenv("LOCAL_CONFIG", testPath+"/config.json")
		assert.NilError(t, config.LoadConfigs())
		os.Setenv("ENVIRONMENT_CONFIG", file)
		os.Unsetenv("ENVIRONMENT_STORAGE")
		os.Unsetenv("ENVIRONMENT_CONFIG")
		os.Unsetenv("LOCAL_CONFIG")
		os.RemoveAll(testPath)
	})
}

type MockConfig struct {
	App    MockApplicationConfig `mapstructure:"application"`
	Logger MockLoggerConfig      `mapstructure:"logger"`
}
type MockApplicationConfig struct {
	Port int `mapstructure:"port"`
}
type MockLoggerConfig struct {
	LogOutput   string `mapstructure:"log_output_to"`
	ErrorOutput string `mapstructure:"log_errors_to"`
	Level       string `mapstructure:"log_level"`
}
type MockConfigFail struct {
	*Config
}

func (m *MockConfigFail) LoadConfigs() error {
	return errors.New("Fail loading configs")
}

func createTestConfigFile(path string) (string, string, error) {
	testpath := path + randomString(8)
	config := testpath + "/env.local.json"
	err := os.MkdirAll(testpath, os.ModePerm)
	if err != nil {
		return "", testpath, err
	}
	data := `{"application": {"port": 8080}}`
	createFile(config, data)
	return config, testpath, nil
}

func createFile(path, data string) {
	err := os.WriteFile(path, []byte(data), 0644)
	if err != nil {
		fmt.Printf("error creating file")
	}
}

// randomString return a random string
func randomString(length int) string {
	rand.Seed(time.Now().UnixNano())
	chars := []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
	var b strings.Builder
	for i := 0; i < length; i++ {
		b.WriteRune(chars[rand.Intn(len(chars))])
	}
	return b.String()
}
