package main

import (
	"log"
	"os"
	"os/user"
	"path"
	"path/filepath"
	"time"

	"github.com/kkyr/fig"
)

// Config is the service-mode configuration, loaded from YAML (via fig) and
// overridable by flags. It only governs the output exporters; drive discovery
// and SMART collection auto-detect their tools and need no configuration.
type Config struct {
	// Hostname is used as the host tag/label on every metric. When empty it is
	// resolved from the system hostname.
	Hostname string `fig:"hostname"`

	// Metric outputs.
	HTTP   HTTPOutputConfig   `fig:"http_output"`
	Influx InfluxOutputConfig `fig:"influx_output"`
}

// HTTPOutputConfig configures the Prometheus HTTP exporter.
type HTTPOutputConfig struct {
	Enabled     bool   `fig:"enabled"`
	BindAddr    string `fig:"bind_addr"`
	Port        uint   `fig:"port"`
	MetricsPath string `fig:"metrics_path"`
}

// InfluxOutputConfig configures the scheduled InfluxDB output. Metrics are
// pushed every Frequency to InfluxDB's v2 API and/or to Kafka. A zero Frequency
// (or no destination configured) disables the output.
type InfluxOutputConfig struct {
	Frequency time.Duration `fig:"frequency"`

	KafkaBrokers            []string `fig:"kafka_brokers"`
	KafkaTopic              string   `fig:"kafka_topic"`
	KafkaUsername           string   `fig:"kafka_username"`
	KafkaPassword           string   `fig:"kafka_password"`
	KafkaInsecureSkipVerify bool     `fig:"kafka_insecure_skip_verify"`
	KafkaOutputFormat       string   `fig:"kafka_output_format"` // lineprotocol (default) or json.

	InfluxServer string `fig:"influx_server"`
	Token        string `fig:"token"`
	Org          string `fig:"org"`
	Bucket       string `fig:"bucket"`
}

// defaultConfig returns the configuration with all defaults applied, used as the
// base before a file (if any) is loaded over it.
func defaultConfig() *Config {
	return &Config{
		HTTP: HTTPOutputConfig{
			Enabled:     true,
			Port:        9101,
			MetricsPath: "/metrics",
		},
		Influx: InfluxOutputConfig{
			KafkaOutputFormat: "lineprotocol",
		},
	}
}

// findConfigFile returns the first configuration file that exists, preferring
// the -config flag (configPath), then a local file, the user config dir, and
// finally /etc. It returns "" when none is found — configuration is optional.
func findConfigFile(configPath string) string {
	if configPath != "" {
		if _, err := os.Stat(configPath); err == nil {
			return configPath
		}
		log.Printf("Configured config path %q not found, falling back to defaults", configPath)
	}

	candidates := []string{}
	if local, err := filepath.Abs("./config.yaml"); err == nil {
		candidates = append(candidates, local)
	}
	if usr, err := user.Current(); err == nil {
		candidates = append(candidates, usr.HomeDir+"/.config/drive-health-metrics/config.yaml")
	}
	candidates = append(candidates, "/etc/drive-health-metrics.yaml")

	for _, c := range candidates {
		if _, err := os.Stat(c); err == nil {
			return c
		}
	}
	return ""
}

// ReadConfig loads the configuration into app.config. It always succeeds with a
// usable config: a file is loaded over the defaults when present, flag overrides
// are applied, and the host tag is resolved when unset.
func (a *App) ReadConfig() {
	config := defaultConfig()

	// Load a configuration file over the defaults when one is available.
	if configFile := findConfigFile(a.flags.ConfigPath); configFile != "" {
		dir, name := path.Split(configFile)
		if dir == "" {
			dir = "."
		}
		if err := fig.Load(config, fig.File(name), fig.Dirs(dir)); err != nil {
			log.Printf("Error parsing configuration %q: %s", configFile, err)
		}
	}

	// Resolve the host tag from the system when not configured.
	if config.Hostname == "" {
		config.Hostname = hostname()
	}

	// Flag overrides for the HTTP output.
	if a.flags.HTTPBind != "" {
		config.HTTP.BindAddr = a.flags.HTTPBind
	}
	if a.flags.HTTPPort != 0 {
		config.HTTP.Port = a.flags.HTTPPort
	}
	if a.flags.HTTPMetricsPath != "" {
		config.HTTP.MetricsPath = a.flags.HTTPMetricsPath
	}

	a.config = config
}
