package policy

import (
	"strings"

	"code.justin.tv/systems/sandstorm/manager"
)

// Authorizer represents user based Authorization for policy management
type Authorizer interface {
	// AuthorizeSecretKeys validates that the user has authorization to access
	// these secrets
	AuthorizeSecretKeys(keys []string) (err error)

	// AuthorizeLDAPGroups returns this user's ldap groups
	AuthorizeLDAPGroups(ldapGroups []string) (err error)

	// IsAdmin is used to determine if user has sudo access to sandstorm
	IsAdmin() bool

	// AuthorizeNamespace takes a namespace and returns an error if the
	// namespace isn't authorized for access
	AuthorizeNamespace(dynamoNamespace string) error
}

// ValidateWildcards is business logic to ensure we are only allowing wildcards
// in certain situations
func ValidateWildcards(secretNamespace *manager.SecretNamespace) bool {
	if secretNamespace.Environment == developmentEnvironment {
		if strings.Contains(secretNamespace.Team, wildcard) {
			return false
		}
		return true
	}

	key := secretNamespace.String()
	if strings.Count(key, wildcard) != strings.Count(secretNamespace.Name, wildcard) {
		return false
	}
	return true
}

// ValidateNamespaces validates that specified keys are from a single environment,
// only contains wildcard on secret name, and errors if otherwise.
func ValidateNamespaces(secretKeys []string, secrets SecretsManager) (namespaces []string, err error) {
	crossEnvSecrets, err := secrets.CrossEnvironmentSecretsSet(secretKeys)

	ns := make(map[string]struct{})
	var environment string
	for _, key := range secretKeys {
		var elements *manager.SecretNamespace
		elements, err = manager.ParseSecretNamespace(key)
		if err != nil {
			return
		}

		if !ValidateWildcards(elements) {
			err = ErrDisallowedWildcard
			return
		}

		_, isCrossEnv := crossEnvSecrets[key]
		if !isCrossEnv {
			if environment == "" {
				environment = getEnv(elements.Environment)
			} else {
				if environment != getEnv(elements.Environment) {
					err = ErrMultipleEnvironments
					return
				}
			}
		}

		ns[elements.Team] = struct{}{}
	}

	namespaces = make([]string, 0, len(ns))
	for key := range ns {
		namespaces = append(namespaces, key)
	}
	return
}

func getEnv(env string) string {
	if isStaging(env) {
		return "staging"
	}

	if isProduction(env) {
		return "production"
	}

	if isDevelopment(env) {
		return "development"
	}

	return env
}

func isDevelopment(envStr string) bool {
	return envStr == "dev" || envStr == "develop" || envStr == "development"
}

func isStaging(envStr string) bool {
	return envStr == "stag" || envStr == "staging"
}

func isProduction(envStr string) bool {
	return envStr == "prod" || envStr == "production"
}
