package config

import (
	"os"
	"path"
	"testing"

	"github.com/sirupsen/logrus"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func initJson(t *testing.T, content string) string {
	tmpPath := t.TempDir()
	confPath := path.Join(tmpPath, "config.json")
	err := os.WriteFile(confPath, []byte(content), 0o644)
	require.NoError(t, err)
	return confPath
}

func setEnv(t *testing.T, envVars map[string]string) {
	for key, val := range envVars {
		t.Setenv(key, val)
	}
}

func TestDefault(t *testing.T) {
	t.Run("no file", func(t *testing.T) {
		conf, err := New("/this/path/does/not/exist")
		require.NoError(t, err)
		assert.Equal(t, defaultConfig(), *conf)
	})

	t.Run("empty config", func(t *testing.T) {
		confPath := initJson(t, `{}`)
		conf, err := New(confPath)
		require.NoError(t, err)
		assert.Equal(t, defaultConfig(), *conf)
	})
}

// Since we still use a JSON conf for the OIDC config, we still need to check this for now
// But as soon as the config file is not necessary, this will probably disappear
func TestInvalidJSON(t *testing.T) {
	confPath := initJson(t, "toto")
	errMsg := "failed to parse config file: invalid character 'o' in literal true (expecting 'r')"
	_, err := New(confPath)
	assert.ErrorContains(t, err, errMsg)
}

func TestJSONConfig(t *testing.T) {
	confPath := initJson(t, `{
	"log": {"level":"info"},
	"server": {
		"mode": "net",
		"host": "0.0.0.0",
		"port": 1337
	}
}`)

	conf, err := New(confPath)
	require.NoError(t, err)

	assert.Equal(t, ModeNet, conf.ServerMode)
	assert.Equal(t, "0.0.0.0", conf.Host)
	assert.Equal(t, 1337, conf.Port)
}

func TestJSONConfigOverriden(t *testing.T) {
	confPath := initJson(t, `{
	"log": {"level":"info"},
	"server": {
		"mode": "net",
		"host": "0.0.0.0",
		"port": 1337
	}
}`)

	envVars := map[string]string{
		string("POLYCULECONNECT_SERVER_MODE"): string(ModeUnix),
		string("POLYCULECONNECT_SERVER_SOCK"): "/run/polyculeconnect.sock",
	}
	setEnv(t, envVars)

	conf, err := New(confPath)
	require.NoError(t, err)

	assert.Equal(t, ModeUnix, conf.ServerMode)
	assert.Equal(t, "0.0.0.0", conf.Host)
	assert.Equal(t, 1337, conf.Port)
	assert.Equal(t, "/run/polyculeconnect.sock", conf.SockPath)
}

func TestHostNetMode(t *testing.T) {
	envVars := map[string]string{
		string("POLYCULECONNECT_SERVER_MODE"): string(ModeNet),
		string("POLYCULECONNECT_SERVER_HOST"): "127.0.0.1",
		string("POLYCULECONNECT_SERVER_PORT"): "8888",
	}
	setEnv(t, envVars)

	conf, err := New("")
	require.NoError(t, err)

	assert.Equal(t, ModeNet, conf.ServerMode)
	assert.Equal(t, "127.0.0.1", conf.Host)
	assert.Equal(t, 8888, conf.Port)
}

func TestHostSocketMode(t *testing.T) {
	envVars := map[string]string{
		string("POLYCULECONNECT_SERVER_MODE"): string(ModeUnix),
		string("POLYCULECONNECT_SERVER_SOCK"): "/run/polyculeconnect.sock",
	}
	setEnv(t, envVars)

	conf, err := New("")
	require.NoError(t, err)

	assert.Equal(t, ModeUnix, conf.ServerMode)
	assert.Equal(t, "/run/polyculeconnect.sock", conf.SockPath)
}

func TestLogLevel(t *testing.T) {
	envVars := map[string]string{
		string("POLYCULECONNECT_LOG_LEVEL"): "error",
	}
	setEnv(t, envVars)

	conf, err := New("")
	require.NoError(t, err)

	assert.Equal(t, logrus.ErrorLevel, conf.LogLevel)
}

func TestSqliteConfig(t *testing.T) {
	envVars := map[string]string{
		string("POLYCULECONNECT_STORAGE_TYPE"): "sqlite",
		string("POLYCULECONNECT_STORAGE_PATH"): "/data/polyculeconnect.db",
	}
	setEnv(t, envVars)

	conf, err := New("")
	require.NoError(t, err)

	assert.Equal(t, string(SQLite), conf.StorageType)
	assert.Equal(t, "/data/polyculeconnect.db", conf.StorageConfig.File)
}

func TestSqliteConfigJSON(t *testing.T) {
	confPath := initJson(t, `{
		"log": {"level":"info"},
		"storage": {
			"type": "sqlite",
			"path": "/data/polyculeconnect.db"
		}
	}`)

	conf, err := New(confPath)
	require.NoError(t, err)

	assert.Equal(t, string(SQLite), conf.StorageType)
	assert.Equal(t, "/data/polyculeconnect.db", conf.StorageConfig.File)
}

func TestSqliteConfigJSONOverriden(t *testing.T) {
	confPath := initJson(t, `{
		"log": {"level":"info"},
		"storage": {
			"type": "sqlite",
			"path": "/data/polyculeconnect.db"
		}
	}`)

	envVars := map[string]string{
		string("POLYCULECONNECT_STORAGE_PATH"): "/tmp/polyculeconnect.db",
	}
	setEnv(t, envVars)

	conf, err := New(confPath)
	require.NoError(t, err)

	assert.Equal(t, string(SQLite), conf.StorageType)
	assert.Equal(t, "/tmp/polyculeconnect.db", conf.StorageConfig.File)
}
