package config

import (
	"os"
	"path"
	"path/filepath"

	"coopcloud.tech/abra/pkg/log"
	"gopkg.in/yaml.v3"
)

// LoadAbraConfig returns the abra configuration. It tries to find a abra
// configuration file (see findAbraConfig for lookup logic). When no
// configuration was found it returns the default config.
func LoadAbraConfig() Abra {
	wd, _ := os.Getwd()
	configFile := findAbraConfig(wd)
	if configFile == "" {
		log.Debugf("no config file found")
		return Abra{}
	}
	data, err := os.ReadFile(configFile)
	if err != nil {
		// Do nothing, when an error occurs
		log.Debugf("error reading config file: %s", err)
		return Abra{}
	}

	config := Abra{}
	err = yaml.Unmarshal(data, &config)
	if err != nil {
		// Do nothing, when an error occurs
		log.Debugf("error loading config file: %s", err)
		return Abra{}
	}
	log.Debugf("config file loaded from: %s", configFile)
	config.configPath = filepath.Dir(configFile)
	return config
}

// findAbraConfig recursively looks for a abra.y(a)ml file in the given directory.
// When the file was not found it calls the function again with the parent
// directory until the home directory is hit. When no abra config was found it
// returns an empty string.
func findAbraConfig(dir string) string {
	dir, err := filepath.Abs(dir)
	if err != nil {
		return ""
	}
	if dir == os.ExpandEnv("$HOME") || dir == "/" {
		return ""
	}
	p := path.Join(dir, "abra.yaml")
	if _, err := os.Stat(p); err == nil {
		return p
	}
	p = path.Join(dir, "abra.yml")
	if _, err := os.Stat(p); err == nil {
		return p
	}
	return findAbraConfig(filepath.Dir(dir))
}

// Abra defines the configuration file for abra.
type Abra struct {
	configPath string
	AbraDir    string `yaml:"abraDir"`
}

// GetAbraDir returns the abra dir. It has the following logic:
// 1. check if $ABRA_DIR is set
// 2. check if abraDir was set in a config file
// 3. use $HOME/.abra when above two options failed
func (a Abra) GetAbraDir() string {
	if dir, exists := os.LookupEnv("ABRA_DIR"); exists && dir != "" {
		log.Debug("read abra dir from $ABRA_DIR")
		return dir
	}
	if a.AbraDir != "" {
		log.Debug("read abra dir from config file")
		if path.IsAbs(a.AbraDir) {
			return a.AbraDir
		}
		// Make the path absolute
		return path.Join(a.configPath, a.AbraDir)
	}
	log.Debug("using default abra dir")
	return os.ExpandEnv("$HOME/.abra")
}

func (a Abra) GetServersDir() string   { return path.Join(a.GetAbraDir(), "servers") }
func (a Abra) GetRecipesDir() string   { return path.Join(a.GetAbraDir(), "recipes") }
func (a Abra) GetVendorDir() string    { return path.Join(a.GetAbraDir(), "vendor") }
func (a Abra) GetBackupDir() string    { return path.Join(a.GetAbraDir(), "backups") }
func (a Abra) GetCatalogueDir() string { return path.Join(a.GetAbraDir(), "catalogue") }

var config = LoadAbraConfig()

var (
	ABRA_DIR                 = config.GetAbraDir()
	SERVERS_DIR              = config.GetServersDir()
	RECIPES_DIR              = config.GetRecipesDir()
	VENDOR_DIR               = config.GetVendorDir()
	BACKUP_DIR               = config.GetBackupDir()
	CATALOGUE_DIR            = config.GetCatalogueDir()
	RECIPES_JSON             = path.Join(config.GetCatalogueDir(), "recipes.json")
	REPOS_BASE_URL           = "https://git.coopcloud.tech/coop-cloud"
	CATALOGUE_JSON_REPO_NAME = "recipes-catalogue-json"
	SSH_URL_TEMPLATE         = "ssh://git@git.coopcloud.tech:2222/coop-cloud/%s.git"

	// NOTE(d1): please note, this was done purely out of laziness on our part
	// AFAICR. it's easy to punt the value into the label because that is what is
	// expects. it's not particularly useful in terms of UI/UX but hey, nobody
	// complained yet!
	CHAOS_DEFAULT = "false"

	DIRTY_DEFAULT = "+U"

	NO_DOMAIN_DEFAULT  = "N/A"
	NO_VERSION_DEFAULT = "N/A"

	UNKNOWN_DEFAULT = "unknown"
)
