package setting

import (
	"bytes"
	"errors"
	"flag"
	"fmt"
	"os"
	"time"

	"github.com/dcarrillo/whatismyip/internal/core"
)

type geodbPath struct {
	City string
	ASN  string
}
type serverSettings struct {
	ReadTimeout  time.Duration
	WriteTimeout time.Duration
}
type settings struct {
	GeodbPath           geodbPath
	TemplatePath        string
	BindAddress         string
	TLSAddress          string
	TLSCrtPath          string
	TLSKeyPath          string
	TrustedHeader       string
	TrustedPortHeader   string
	EnableSecureHeaders bool
	EnableHTTP3         bool
	Server              serverSettings
	version             bool
}

const defaultAddress = ":8080"

// ErrVersion is the custom error triggered when -version flag is passed
var ErrVersion = errors.New("setting: version requested")

// App is the var with the parsed settings
var App = settings{
	// hard-coded for the time being
	Server: serverSettings{
		ReadTimeout:  10 * time.Second,
		WriteTimeout: 10 * time.Second,
	},
}

// Setup initializes the App object parsing the flags
func Setup(args []string) (output string, err error) {
	flags := flag.NewFlagSet("whatismyip", flag.ContinueOnError)
	var buf bytes.Buffer
	flags.SetOutput(&buf)

	flags.StringVar(&App.GeodbPath.City, "geoip2-city", "", "Path to GeoIP2 city database")
	flags.StringVar(&App.GeodbPath.ASN, "geoip2-asn", "", "Path to GeoIP2 ASN database")
	flags.StringVar(&App.TemplatePath, "template", "", "Path to template file")
	flags.StringVar(
		&App.BindAddress,
		"bind",
		defaultAddress,
		"Listening address (see https://pkg.go.dev/net?#Listen)",
	)
	flags.StringVar(
		&App.TLSAddress,
		"tls-bind",
		"",
		"Listening address for TLS (see https://pkg.go.dev/net?#Listen)",
	)
	flags.StringVar(&App.TLSCrtPath, "tls-crt", "", "When using TLS, path to certificate file")
	flags.StringVar(&App.TLSKeyPath, "tls-key", "", "When using TLS, path to private key file")
	flags.StringVar(
		&App.TrustedHeader,
		"trusted-header",
		"",
		"Trusted request header for remote IP (e.g. X-Real-IP). When using this feature if -trusted-port-header is not set the client port is shown as 'unknown'",
	)
	flags.StringVar(
		&App.TrustedPortHeader,
		"trusted-port-header",
		"",
		"Trusted request header for remote client port (e.g. X-Real-Port). When this parameter is set -trusted-header becomes mandatory",
	)
	flags.BoolVar(&App.version, "version", false, "Output version information and exit")
	flags.BoolVar(
		&App.EnableSecureHeaders,
		"enable-secure-headers",
		false,
		"Add sane security-related headers to every response",
	)
	flags.BoolVar(
		&App.EnableHTTP3,
		"enable-http3",
		false,
		"Enable HTTP/3 protocol. HTTP/3 requires --tls-bind set, as HTTP/3 starts as a TLS connection that then gets upgraded to UDP. The UDP port is the same as the one used for the TLS server.",
	)

	err = flags.Parse(args)
	if err != nil {
		return buf.String(), err
	}

	if App.version {
		return fmt.Sprintf("whatismyip version %s", core.Version), ErrVersion
	}

	if App.TrustedPortHeader != "" && App.TrustedHeader == "" {
		return "", fmt.Errorf("truster-header is mandatory when truster-port-header is set")
	}

	if App.GeodbPath.City == "" || App.GeodbPath.ASN == "" {
		return "", fmt.Errorf("geoip2-city and geoip2-asn parameters are mandatory")
	}

	if (App.TLSAddress != "") && (App.TLSCrtPath == "" || App.TLSKeyPath == "") {
		return "", fmt.Errorf("in order to use TLS, the -tls-crt and -tls-key flags are mandatory")
	}

	if App.EnableHTTP3 && App.TLSAddress == "" {
		return "", fmt.Errorf("in order to use HTTP3, the -tls-bind is mandatory")
	}

	if App.TemplatePath != "" {
		info, err := os.Stat(App.TemplatePath)
		if os.IsNotExist(err) {
			return "", fmt.Errorf("%s no such file or directory", App.TemplatePath)
		}
		if info.IsDir() {
			return "", fmt.Errorf("%s must be a file", App.TemplatePath)
		}
	}

	return buf.String(), nil
}