// Package config parses and validates admin's environment.
//
// Everything required is checked at boot and the process refuses to start
// without it. A licensing service that cannot sign is worse than one that is
// down, because it looks healthy.
package config

import (
	"fmt"
	"net/url"
	"os"
	"strings"
)

type Config struct {
	AdminMongoURI   string
	AdminDBName     string
	ControlMongoURI string
	ControlDBName   string
	RedisAddr       string
	SigningKey      string
	PublicURL       string
	AllowedOrigins  []string
	TrustProxy      bool
	Addr            string

	SMTPHost     string
	SMTPPort     string
	SMTPFrom     string
	SMTPUsername string
	SMTPPassword string
}

// dbNameFromURI reads the database from a Mongo URI path.
//
// Both URIs must name their database inline rather than through a separate
// variable. Admin talks to two databases; a bare MONGO_DB would be ambiguous
// about which, and guessing wrong means writing licence fields into the wrong
// place.
func dbNameFromURI(raw, which string) (string, error) {
	u, err := url.Parse(raw)
	if err != nil {
		return "", fmt.Errorf("%s is not a valid URI: %w", which, err)
	}
	name := strings.TrimPrefix(u.Path, "/")
	if name == "" {
		return "", fmt.Errorf("%s must name a database in its path, e.g. mongodb://host:27017/vantage_admin", which)
	}
	return name, nil
}

func Load() (Config, error) {
	c := Config{
		AdminMongoURI:   os.Getenv("ADMIN_MONGO_URI"),
		ControlMongoURI: os.Getenv("CONTROL_MONGO_URI"),
		RedisAddr:       os.Getenv("REDIS_ADDR"),
		SigningKey:      os.Getenv("LICENSE_SIGNING_KEY"),
		PublicURL:       strings.TrimSuffix(os.Getenv("PUBLIC_URL"), "/"),
		TrustProxy:      strings.EqualFold(os.Getenv("TRUST_PROXY"), "true"),
		Addr:            ":" + envOr("PORT", "8083"),

		SMTPHost:     os.Getenv("SMTP_HOST"),
		SMTPPort:     envOr("SMTP_PORT", "587"),
		SMTPFrom:     os.Getenv("SMTP_FROM"),
		SMTPUsername: os.Getenv("SMTP_USERNAME"),
		SMTPPassword: os.Getenv("SMTP_PASSWORD"),
	}

	var missing []string
	for name, v := range map[string]string{
		"ADMIN_MONGO_URI":     c.AdminMongoURI,
		"CONTROL_MONGO_URI":   c.ControlMongoURI,
		"REDIS_ADDR":          c.RedisAddr,
		"LICENSE_SIGNING_KEY": c.SigningKey,
		"PUBLIC_URL":          c.PublicURL,
		"ADMIN_ORIGIN":        os.Getenv("ADMIN_ORIGIN"),
	} {
		if v == "" {
			missing = append(missing, name)
		}
	}
	if len(missing) > 0 {
		return Config{}, fmt.Errorf("missing required environment: %s", strings.Join(missing, ", "))
	}

	var err error
	if c.AdminDBName, err = dbNameFromURI(c.AdminMongoURI, "ADMIN_MONGO_URI"); err != nil {
		return Config{}, err
	}
	if c.ControlDBName, err = dbNameFromURI(c.ControlMongoURI, "CONTROL_MONGO_URI"); err != nil {
		return Config{}, err
	}
	if c.AdminMongoURI == c.ControlMongoURI {
		return Config{}, fmt.Errorf("ADMIN_MONGO_URI and CONTROL_MONGO_URI must not be the same database")
	}

	for _, o := range strings.Split(os.Getenv("ADMIN_ORIGIN"), ",") {
		if o = strings.TrimSpace(o); o != "" {
			c.AllowedOrigins = append(c.AllowedOrigins, o)
		}
	}
	return c, nil
}

func envOr(key, fallback string) string {
	if v := os.Getenv(key); v != "" {
		return v
	}
	return fallback
}
