package main

import (
	"fmt"
	"log"
	"strings"

	"github.com/knadh/koanf/parsers/json"
	"github.com/knadh/koanf/parsers/yaml"
	"github.com/knadh/koanf/providers/confmap"
	"github.com/knadh/koanf/providers/env"
	"github.com/knadh/koanf/providers/file"
	"github.com/knadh/koanf/v2"
)

// Config struct for unmarshaling
type Config struct {
	Server   ServerConfig   `koanf:"server"`
	Database DatabaseConfig `koanf:"database"`
}

type ServerConfig struct {
	Host string `koanf:"host"`
	Port int    `koanf:"port"`
}

type DatabaseConfig struct {
	Host     string `koanf:"host"`
	Port     int    `koanf:"port"`
	User     string `koanf:"user"`
	Password string `koanf:"password"`
}

func main() {
	// Create a new koanf instance
	k := koanf.New(".")

	// Load default values from a map
	k.Load(confmap.Provider(map[string]interface{}{
		"server.host":    "localhost",
		"server.port":    8080,
		"database.host":  "localhost",
		"database.port":  5432,
		"database.user":  "postgres",
	}, "."), nil)

	// Load from YAML file (if exists)
	if err := k.Load(file.Provider("config.yaml"), yaml.Parser()); err != nil {
		log.Printf("YAML config not found: %v", err)
	}

	// Load from JSON file (if exists)
	if err := k.Load(file.Provider("config.json"), json.Parser()); err != nil {
		log.Printf("JSON config not found: %v", err)
	}

	// Load from environment variables
	// APP_SERVER_HOST becomes server.host
	k.Load(env.Provider("APP_", ".", func(s string) string {
		return strings.Replace(
			strings.ToLower(strings.TrimPrefix(s, "APP_")),
			"_", ".", -1)
	}), nil)

	// Access values directly
	fmt.Println("Server Host:", k.String("server.host"))
	fmt.Println("Server Port:", k.Int("server.port"))
	fmt.Println("DB Host:", k.String("database.host"))

	// Access with defaults
	fmt.Println("Timeout:", k.Int64("server.timeout"))           // 0 (not set)
	fmt.Println("Debug:", k.Bool("server.debug"))                 // false (not set)

	// Check if key exists
	if k.Exists("database.password") {
		fmt.Println("Password is set")
	}

	// Get all keys
	fmt.Println("\nAll keys:", k.Keys())

	// Get all values as map
	fmt.Printf("\nAll config: %+v\n", k.All())

	// Unmarshal into struct
	var cfg Config
	if err := k.Unmarshal("", &cfg); err != nil {
		log.Fatal(err)
	}
	fmt.Printf("\nUnmarshaled: %+v\n", cfg)

	// Unmarshal specific section
	var serverCfg ServerConfig
	if err := k.Unmarshal("server", &serverCfg); err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Server config: %+v\n", serverCfg)

	// Get nested map
	dbMap := k.Cut("database").All()
	fmt.Printf("Database map: %+v\n", dbMap)

	// Merge another config
	k.Load(confmap.Provider(map[string]interface{}{
		"server.timeout": 30,
		"server.debug":   true,
	}, "."), nil)

	fmt.Println("\nAfter merge:")
	fmt.Println("Timeout:", k.Int("server.timeout"))
	fmt.Println("Debug:", k.Bool("server.debug"))

	// Watch file for changes (useful for hot-reload)
	// f := file.Provider("config.yaml")
	// f.Watch(func(event interface{}, err error) {
	//     if err != nil {
	//         log.Println("Watch error:", err)
	//         return
	//     }
	//     k.Load(f, yaml.Parser())
	//     log.Println("Config reloaded")
	// })
}
