package validator

import (
	"fmt"
	"io/ioutil"
	"strings"

	"github.com/spf13/viper"

	"github.com/authelia/authelia/internal/configuration/schema"
)

// SecretNameToEnvName converts a secret name into the env name.
func SecretNameToEnvName(secretName string) (envName string) {
	return "authelia." + secretName + ".file"
}

func isSecretKey(value string) (isSecretKey bool) {
	for _, secretKey := range SecretNames {
		if value == secretKey || value == SecretNameToEnvName(secretKey) {
			return true
		}
	}

	return false
}

// ValidateSecrets checks that secrets are either specified by config file/env or by file references.
func ValidateSecrets(configuration *schema.Configuration, validator *schema.StructValidator, viper *viper.Viper) {
	configuration.JWTSecret = getSecretValue(SecretNames["JWTSecret"], validator, viper)
	configuration.Session.Secret = getSecretValue(SecretNames["SessionSecret"], validator, viper)

	if configuration.DuoAPI != nil {
		configuration.DuoAPI.SecretKey = getSecretValue(SecretNames["DUOSecretKey"], validator, viper)
	}

	if configuration.Session.Redis != nil {
		configuration.Session.Redis.Password = getSecretValue(SecretNames["RedisPassword"], validator, viper)

		if configuration.Session.Redis.HighAvailability != nil {
			configuration.Session.Redis.HighAvailability.SentinelPassword =
				getSecretValue(SecretNames["RedisSentinelPassword"], validator, viper)
		}
	}

	if configuration.AuthenticationBackend.LDAP != nil {
		configuration.AuthenticationBackend.LDAP.Password = getSecretValue(SecretNames["LDAPPassword"], validator, viper)
	}

	if configuration.Notifier != nil && configuration.Notifier.SMTP != nil {
		configuration.Notifier.SMTP.Password = getSecretValue(SecretNames["SMTPPassword"], validator, viper)
	}

	if configuration.Storage.MySQL != nil {
		configuration.Storage.MySQL.Password = getSecretValue(SecretNames["MySQLPassword"], validator, viper)
	}

	if configuration.Storage.PostgreSQL != nil {
		configuration.Storage.PostgreSQL.Password = getSecretValue(SecretNames["PostgreSQLPassword"], validator, viper)
	}
}

func getSecretValue(name string, validator *schema.StructValidator, viper *viper.Viper) string {
	configValue := viper.GetString(name)
	fileEnvValue := viper.GetString(SecretNameToEnvName(name))

	// Error Checking.
	if fileEnvValue != "" && configValue != "" {
		validator.Push(fmt.Errorf("error loading secret (%s): it's already defined in the config file", name))
	}

	// Derive Secret.
	if fileEnvValue != "" {
		content, err := ioutil.ReadFile(fileEnvValue)
		if err != nil {
			validator.Push(fmt.Errorf("error loading secret file (%s): %s", name, err))
		} else {
			return strings.ReplaceAll(string(content), "\n", "")
		}
	}

	return configValue
}
