package main

import (
	"fmt"
	"io/ioutil"
	"path"
	"path/filepath"

	"github.com/goamz/goamz/aws"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

var (
	daemonConfiguration = &necroConfiguration{
		Services: make(necroServicesConfiguration),
	}
)

const (
	defaultAWSProfile = ""
	defaultAWSRegion  = "us-west-2"
	defaultS3Bucket   = "tw-prod-dta-necronomicon-api"
	defaultInterval   = 30 // Minimum delay between each pull

	serviceFileExtension  = ".service"
	defaultServiceTimeout = 10
	defaultServiceSplay   = 0
)

type necroConfiguration struct {
	ServicesDirectory string `mapstructure:"services_directory"`
	Services          necroServicesConfiguration
	S3                necroS3Configuration
}

type necroS3Configuration struct {
	Profile  string
	Region   string
	Bucket   string
	Interval int
	Delay    int
}

type necroServicesConfiguration map[string]*necroServiceConfigurationEntry

type necroServiceConfigurationEntry struct {
	Environment string
	Namespaces  []string
	File        string
	Reload      string
	Delay       int // Random delay
	Timeout     int
}

func loadConfig(cmd *cobra.Command, args []string) error {
	config := viper.New()
	config.SetConfigType("toml")
	config.SetDefault("s3.profile", defaultAWSProfile)
	config.SetDefault("s3.region", defaultAWSRegion)
	config.SetDefault("s3.bucket", defaultS3Bucket)
	config.SetDefault("s3.interval", defaultInterval)
	config.BindPFlag("services_directory", cmd.Flags().Lookup("services-directory"))

	config.SetConfigFile(cmd.Flag("config").Value.String())
	if err := config.ReadInConfig(); err != nil {
		return fmt.Errorf("Could not read config file: %v", err)
	}
	if err := loadServices(config); err != nil {
		return fmt.Errorf("Could not read services: %v", err)
	}
	if err := config.Unmarshal(daemonConfiguration); err != nil {
		return fmt.Errorf("Could not unmarshal config: %v", err)
	}
	log.Debugln("daemonConfiguration", daemonConfiguration)
	for k, v := range daemonConfiguration.Services {
		log.Debugln("daemonConfiguration.Services", k, v)
	}
	if err := validateConfig(); err != nil {
		return fmt.Errorf("Could not validate configuration: %v", err)
	}
	if err := validateServices(); err != nil {
		return fmt.Errorf("Could not validate services configuration: %v", err)
	}

	return nil
}

func loadServices(config *viper.Viper) error {
	servicesDirectory := config.GetString("services_directory")
	files, err := ioutil.ReadDir(servicesDirectory)
	if err != nil {
		return fmt.Errorf("Could not read service directory %s: %v", servicesDirectory, err)
	}
	serviceConfig := viper.New()
	serviceConfig.SetConfigType("toml")
	for _, serviceFile := range files {
		fileName := path.Join(servicesDirectory, serviceFile.Name())
		if filepath.Ext(fileName) != serviceFileExtension {
			log.Debugln("skipping file", fileName)
			continue
		}
		serviceConfig.SetConfigFile(fileName)
		err = serviceConfig.MergeInConfig()
		if err != nil {
			return fmt.Errorf("Could not read service file %s: %v", fileName, err)
		}
	}
	for serviceName := range serviceConfig.GetStringMap("services") {
		p := fmt.Sprintf("services.%s", serviceName)
		service := serviceConfig.Sub(p)
		service.SetDefault("timeout", defaultServiceTimeout)
		service.SetDefault("splay", defaultServiceSplay)
		config.Set(p, service.AllSettings())
		log.Debugln("loaded", serviceName, "-", service)
	}
	return nil
}

func validateConfig() error {
	if _, ok := aws.Regions[daemonConfiguration.S3.Region]; !ok {
		return fmt.Errorf("The AWS region configured (%s) is invalid", daemonConfiguration.S3.Region)
	}
	return nil
}

func validateServices() error {
	for name, service := range daemonConfiguration.Services {
		if service.Environment == "" {
			return Error(fmt.Sprintf("Service %s has no environment configured", name))
		}
		if service.File == "" {
			return Error(fmt.Sprintf("Service %s has no output file configured", name))
		}
		if service.Reload == "" {
			return Error(fmt.Sprintf("Service %s has no reload command configured", name))
		}
	}
	return nil
}
