package config

import (
	"log/slog"
	"os"
	"reflect"
	"strconv"
	"time"
)

type Config struct {
	// time configuration
	TimeFormat    string         `default:"2006-01-02 15:04:05" env:"time_format"`
	TimeZoneLocal string         `default:"America/Chicago"     env:"time_zone"`
	TZLocal       *time.Location `ignored:"true"`
	TZUTC         *time.Location `ignored:"true"`

	// logging
	LogLevel  int            `default:"50"   env:"log_level"`
	Log       *slog.Logger   `ignored:"true"`
	SLogLevel *slog.LevelVar `ignored:"true"`

	// webserver
	WebServerPort         int    `default:"8080"    env:"webserver_port"`
	WebServerIP           string `default:"0.0.0.0" env:"webserver_ip"`
	WebServerReadTimeout  int    `default:"5"       env:"webserver_read_timeout"`
	WebServerWriteTimeout int    `default:"1"       env:"webserver_write_timeout"`
	WebServerIdleTimeout  int    `default:"2"       env:"webserver_idle_timeout"`

	// cisa
	RemoteURL      string `default:"https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json?sort_by=field_date_added" env:"remote_url"`
	RefreshSeconds int    `default:"14400"                                                                                                        env:"refresh_seconds"`

	// nsupdate
	NSUKeyLabel     string `env:"nsupdate-key-label"`
	NSUKey          string `env:"nsupdate-key"        secret:"true"`
	NSUKeyAlgorithm string `default:"hmac-sha512"     env:"nsupdate-key-algorithm"`
	NSUPort         int    `default:"53"              env:"nsupdate-port"`
	NSUServer       string `default:"ns1.example.com" env:"nsupdate-server"`
	NSUZone         string `default:"example.com"     env:"nsupdate-zone"`
}

// New initializes the config variable for use with a prepared set of defaults.
func New() Config {
	cfg := Config{
		SLogLevel: new(slog.LevelVar),
	}

	cfg.Log = slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
		Level: cfg.SLogLevel,
	}))

	return cfg
}

func setLogLevel(cfg *Config) {
	switch {
	// error
	case cfg.LogLevel <= 20:
		cfg.SLogLevel.Set(slog.LevelError)
		cfg.Log.Info("Log level updated", "level", slog.LevelError)
	// warning
	case cfg.LogLevel > 20 && cfg.LogLevel <= 40:
		cfg.SLogLevel.Set(slog.LevelWarn)
		cfg.Log.Info("Log level updated", "level", slog.LevelWarn)
	// info
	case cfg.LogLevel > 40 && cfg.LogLevel <= 60:
		cfg.SLogLevel.Set(slog.LevelInfo)
		cfg.Log.Info("Log level updated", "level", slog.LevelInfo)
	// debug
	case cfg.LogLevel > 60:
		cfg.SLogLevel.Set(slog.LevelDebug)
		cfg.Log.Info("Log level updated", "level", slog.LevelDebug)
	}
	// set default logger
	slog.SetDefault(cfg.Log)
}

func printRunningConfig(cfg *Config, cfgInfo []structInfo) {
	for _, info := range cfgInfo {
		if info.Secret {
			cfg.Log.Debug("Running Configuration", info.Alt, "REDACTED")
		} else {
			switch info.Type.String() {
			case "string":
				p := reflect.ValueOf(cfg).Elem().FieldByName(info.Name).Addr().Interface().(*string)
				cfg.Log.Debug("Running Configuration", info.Alt, *p)
			case "bool":
				p := reflect.ValueOf(cfg).Elem().FieldByName(info.Name).Addr().Interface().(*bool)
				cfg.Log.Debug("Running Configuration", info.Alt, strconv.FormatBool(*p))
			case "int":
				p := reflect.ValueOf(cfg).Elem().FieldByName(info.Name).Addr().Interface().(*int)
				cfg.Log.Debug("Running Configuration", info.Alt, strconv.FormatInt(int64(*p), 10))
			}
		}
	}
}