package config

import (
	"fmt"
	"log"
	"os"
	"strings"
	"text/tabwriter"
	"time"

	"github.com/inbucket/inbucket/pkg/stringutil"
	"github.com/kelseyhightower/envconfig"
)

const (
	prefix      = "inbucket"
	tableFormat = `Inbucket is configured via the environment. The following environment variables
can be used:

KEY	DEFAULT	DESCRIPTION
{{range .}}{{usage_key .}}	{{usage_default .}}	{{usage_description .}}
{{end}}`
)

var (
	// Version of this build, set by main
	Version = ""

	// BuildDate for this build, set by main
	BuildDate = ""
)

// mbNaming represents a mailbox naming strategy.
type mbNaming int

// Mailbox naming strategies.
const (
	UnknownNaming mbNaming = iota
	LocalNaming
	FullNaming
	DomainNaming
)

// Decode a naming strategy from string.
func (n *mbNaming) Decode(v string) error {
	switch strings.ToLower(v) {
	case "local":
		*n = LocalNaming
	case "full":
		*n = FullNaming
	case "domain":
		*n = DomainNaming
	default:
		return fmt.Errorf("Unknown MailboxNaming strategy: %q", v)
	}
	return nil
}

// Root contains global configuration, and structs with for specific sub-systems.
type Root struct {
	LogLevel      string   `required:"true" default:"info" desc:"debug, info, warn, or error"`
	MailboxNaming mbNaming `required:"true" default:"local" desc:"Use local, full or domain addressing"`
	SMTP          SMTP
	POP3          POP3
	Web           Web
	Storage       Storage
}

// SMTP contains the SMTP server configuration.
type SMTP struct {
	Addr            string        `required:"true" default:"0.0.0.0:2500" desc:"SMTP server IP4 host:port"`
	Domain          string        `required:"true" default:"inbucket" desc:"HELO domain"`
	MaxRecipients   int           `required:"true" default:"200" desc:"Maximum RCPT TO per message"`
	MaxMessageBytes int           `required:"true" default:"10240000" desc:"Maximum message size"`
	DefaultAccept   bool          `required:"true" default:"true" desc:"Accept all mail by default?"`
	AcceptDomains   []string      `desc:"Domains to accept mail for"`
	RejectDomains   []string      `desc:"Domains to reject mail for"`
	DefaultStore    bool          `required:"true" default:"true" desc:"Store all mail by default?"`
	StoreDomains    []string      `desc:"Domains to store mail for"`
	DiscardDomains  []string      `desc:"Domains to discard mail for"`
	Timeout         time.Duration `required:"true" default:"300s" desc:"Idle network timeout"`
	TLSEnabled      bool          `default:"false" desc:"Enable STARTTLS option"`
	TLSPrivKey      string        `default:"cert.key" desc:"X509 Private Key file for TLS Support"`
	TLSCert         string        `default:"cert.crt" desc:"X509 Public Certificate file for TLS Support"`
	Debug           bool          `ignored:"true"`
}

// POP3 contains the POP3 server configuration.
type POP3 struct {
	Addr    string        `required:"true" default:"0.0.0.0:1100" desc:"POP3 server IP4 host:port"`
	Domain  string        `required:"true" default:"inbucket" desc:"HELLO domain"`
	Timeout time.Duration `required:"true" default:"600s" desc:"Idle network timeout"`
	Debug   bool          `ignored:"true"`
}

// Web contains the HTTP server configuration.
type Web struct {
	Addr           string `required:"true" default:"0.0.0.0:9000" desc:"Web server IP4 host:port"`
	BasePath       string `default:"" desc:"Base path prefix for UI and API URLs"`
	UIDir          string `required:"true" default:"ui/dist" desc:"User interface dir"`
	GreetingFile   string `required:"true" default:"ui/greeting.html" desc:"Home page greeting HTML"`
	MonitorVisible bool   `required:"true" default:"true" desc:"Show monitor tab in UI?"`
	MonitorHistory int    `required:"true" default:"30" desc:"Monitor remembered messages"`
	PProf          bool   `required:"true" default:"false" desc:"Expose profiling tools on /debug/pprof"`
}

// Storage contains the mail store configuration.
type Storage struct {
	Type            string            `required:"true" default:"memory" desc:"Storage impl: file or memory"`
	Params          map[string]string `desc:"Storage impl parameters, see docs."`
	RetentionPeriod time.Duration     `required:"true" default:"24h" desc:"Duration to retain messages"`
	RetentionSleep  time.Duration     `required:"true" default:"50ms" desc:"Duration to sleep between mailboxes"`
	MailboxMsgCap   int               `required:"true" default:"500" desc:"Maximum messages per mailbox"`
}

// Process loads and parses configuration from the environment.
func Process() (*Root, error) {
	c := &Root{}
	err := envconfig.Process(prefix, c)
	c.LogLevel = strings.ToLower(c.LogLevel)
	stringutil.SliceToLower(c.SMTP.AcceptDomains)
	stringutil.SliceToLower(c.SMTP.RejectDomains)
	stringutil.SliceToLower(c.SMTP.StoreDomains)
	stringutil.SliceToLower(c.SMTP.DiscardDomains)
	return c, err
}

// Usage prints out the envconfig usage to Stderr.
func Usage() {
	tabs := tabwriter.NewWriter(os.Stderr, 1, 0, 4, ' ', 0)
	if err := envconfig.Usagef(prefix, &Root{}, tabs, tableFormat); err != nil {
		log.Fatalf("Unable to parse env config: %v", err)
	}
	tabs.Flush()
}
