package config

import (
	"errors"
	"fmt"

	"github.com/BurntSushi/toml"
)

// Config contains notifier configuration
type Config struct {
	Config     *RefConfigs          `toml:"config"`
	Middleware *[]*MiddlewareConfig `toml:"middleware"`
	Main       *[]string            `toml:"main"`
}

// RefConfig contains reference for duplicated used config
type RefConfig map[string]interface{}

// RefConfigs contains all ref configs
type RefConfigs map[string]RefConfig

// MiddlewareConfig contains single middleware config
type MiddlewareConfig struct {
	Name   string       `toml:"name"`
	Driver *string      `toml:"driver"`
	Config *interface{} `toml:"config"`
}

// Span will span config by refConfig
func (ref *RefConfigs) Span(config *interface{}) error {
	if config == nil {
		return nil
	}
	configMap, ok := (*config).(map[string]interface{})
	if !ok {
		return nil
	}

	refName, ok := configMap["__ref"]
	if !ok {
		return nil
	}

	switch refName.(type) {
	case []interface{}:
		refName, ok := refName.([]interface{})
		if !ok {
			return errors.New("RefName is not a []interface{} (which is expected here)")
		}
		for _, name := range refName {
			name, ok := name.(string)
			if !ok {
				return errors.New("Element of a __ref list is not string")
			}
			refConfig, ok := (*ref)[name]
			if !ok {
				return fmt.Errorf("__ref of %s cannot be found", name)
			}
			for key, value := range refConfig {
				configMap[key] = value
			}
		}
	case string:
		name, ok := refName.(string)
		if !ok {
			return errors.New("RefName is not a string (which is expected here)")
		}
		refConfig, ok := (*ref)[name]
		if !ok {
			return fmt.Errorf("__ref of %s cannot be found", name)
		}
		for key, value := range refConfig {
			configMap[key] = value
		}
	}

	return nil
}

func (cfg *Config) spanRef() error {
	if cfg.Middleware != nil {
		for _, middleware := range *cfg.Middleware {
			err := cfg.Config.Span(middleware.Config)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

func (cfg *Config) setDefaultDriver() {
	if cfg.Middleware != nil {
		for _, middleware := range *cfg.Middleware {
			if middleware.Driver == nil {
				middleware.Driver = &middleware.Name
			}
		}
	}
}

// Load loads config options from a toml file.
func (cfg *Config) Load(confFile string) error {
	_, err := toml.DecodeFile(confFile, cfg)
	if err != nil {
		return err
	}

	cfg.setDefaultDriver()

	return cfg.spanRef()
}
