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"`

	// HTTP Client timeout configurations
	HTTPClientRequestTimeout      int `default:"60" env:"HTTP_CLIENT_REQUEST_TIMEOUT"`
	HTTPClientConnectTimeout      int `default:"5"  env:"HTTP_CLIENT_CONNECT_TIMEOUT"`
	HTTPClientTLSHandshakeTimeout int `default:"5"  env:"HTTP_CLIENT_TLS_TIMEOUT"`
	HTTPClientIdleTimeout         int `default:"5"  env:"HTTP_CLIENT_IDLE_TIMEOUT"`

	// Output Filename
	BindOutputFileName string `default:"./response-policy.bind" env:"OUTPUT"`

	// Config
	ConfigFileLocation string `default:"./config.yaml" env:"CONFIG_FILE"`
	ConfigFile         configFileStruct
}

type configFileStruct struct {
	ZoneConfig struct {
		BlockedDomains []string `yaml:"blockedDomains"`
		Domain         string   `yaml:"baseDomain"`
		Email          string   `yaml:"emailAddress"`
		Expire         string   `yaml:"zoneExpire"`
		Minimum        string   `yaml:"zoneMinimum"`
		NameServers    []string `yaml:"nameServers"`
		Refresh        string   `yaml:"zoneRefresh"`
		Retry          string   `yaml:"zoneRetry"`
		Serial         string   `yaml:"zoneSerialNumber"`
		TTL            string   `yaml:"timeToLive"`
	} `yaml:"zoneConfig"`
	Sources struct {
		DomainListURLs []string `yaml:"domainListURLs"`
		HostFileURLs   []string `yaml:"hostFileURLs"`
	} `yaml:"sources"`
	AllowLists []string `yaml:"allowList"`
	DenyList   []string `yaml:"denyList"`
}

// 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 {
		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))
		}
	}
}