package config

import (
	"strings"

	"os"

	"time"

	"github.com/spf13/viper"
)

// Config represents the configuration values for the whole service.
type Config struct {
	App             AppConfig
	DB              DBConfig
	HTTP            HTTPConfig
	Log             LogConfig
	Aws             AwsConfig
	Cache           CacheConfig
	Twilio          TwilioConfig
	Lyft            LyftConfig
	LyftProd        LyftProdConfig
	BXE             BXEConfig
	Blue365         Blue365Config
	Email           EmailConfig
	GoogleShortener GoogleShortenerConfig
	Eligibility     EligibilityConfig
}

// AppConfig represents the configuration values about the application.
type AppConfig struct {
	Name                 string
	Debug                bool
	Docs                 DocsConfig
	DisableZipValidation bool
}

// TwilioConfig represents the configuration values about the twilio.
type TwilioConfig struct {
	Account  string
	Token    string
	Sender   string
	TwiMLSID string
}

// LyftConfig represents the configuration values about the lyft.
type LyftConfig struct {
	Client       string
	Secret       string
	RefreshToken string
}

type LyftProdConfig struct {
	Lyft     LyftConfig
	UserUUID string
}

// BXEConfig represents the configuration values about the BXE.
type BXEConfig struct {
	URL    string
	APIKey string
	Secret string
}

// Blue365Config represents the configuration values about the Blue 365.
type Blue365Config struct {
	URL    string
	APIKey string
	Secret string
}

// DBConfig represents the configuration values about the DB.
type DBConfig struct {
	User             string
	Pass             string
	Name             string
	Host             string
	Port             int
	MaxLifeInMinutes int
	MaxIdleConns     int
	MaxOpenConns     int
}

// HTTPConfig represents the configuration values about the HTTP server.
type HTTPConfig struct {
	Port   int
	Prefix string
	Auth   HTTPAuthConfig
}

// HTTPAuthConfig represents the configuration values about the HTTP authentication (JWT and CORS).
type HTTPAuthConfig struct {
	AppKey          string
	CertificatePath string
	FrontendURLs    []string
}

// LogConfig represents the configuration values about the logging config.
type LogConfig struct {
	LogToFile bool
	Path      string
}

// AwsConfig represents the configuration values about the aws config.
type AwsConfig struct {
	S3Bucket string
}

// DocsConfig represents the configuration values about the documentation config.
type DocsConfig struct {
	YAMLPath    string
	SwaggerPath string
}

// CacheConfig represents the configuration values about the documentation config.
type CacheConfig struct {
	Server            string
	Port              int
	DB                int
	Pass              string
	Prefix            string
	DefaultExpiration time.Duration
	Master            string
}

// CacheConfig represents the configuration values about the documentation config.
type EmailConfig struct {
	Server string
	Port   int
	User   string
	Pass   string
	Sender string
}

type GoogleShortenerConfig struct {
	APIKey    string
	ClientID  string
	SecretKey string
}

type EligibilityConfig struct {
	Url string
}

// Read returns the configuration values,
//		based on the configuration files and environment variables.
func Read() (*Config, error) {
	setup()
	err := viper.ReadInConfig()
	if err != nil {
		if err == os.ErrNotExist {
			viper.SetConfigName("config.local")
			err = viper.ReadInConfig()
		}

		if err != nil {
			return nil, err
		}
	}

	return &Config{
		App: AppConfig{
			Name:                 viper.GetString("app.name"),
			Debug:                viper.GetBool("app.debug"),
			DisableZipValidation: viper.GetBool("app.disable-zip-validation"),
			Docs: DocsConfig{
				YAMLPath:    viper.GetString("app.docs.yaml-path"),
				SwaggerPath: viper.GetString("app.docs.swagger-path"),
			},
		},
		DB: DBConfig{
			User:             viper.GetString("db.user"),
			Pass:             viper.GetString("db.pass"),
			Name:             viper.GetString("db.name"),
			Host:             viper.GetString("db.host"),
			Port:             viper.GetInt("db.port"),
			MaxLifeInMinutes: viper.GetInt("db.max-life-minutes"),
			MaxIdleConns:     viper.GetInt("db.max-idle-conns"),
			MaxOpenConns:     viper.GetInt("db.max-open-conns"),
		},
		HTTP: HTTPConfig{
			Port:   viper.GetInt("http.port"),
			Prefix: viper.GetString("http.prefix"),
			Auth: HTTPAuthConfig{
				AppKey:          viper.GetString("http.auth.app-key"),
				CertificatePath: viper.GetString("http.auth.certificate-path"),
				FrontendURLs:    viper.GetStringSlice("http.auth.frontend-urls"),
			},
		},
		Log: LogConfig{
			LogToFile: viper.GetBool("log.log-to-file"),
			Path:      viper.GetString("log.path"),
		},
		Aws: AwsConfig{
			S3Bucket: viper.GetString("aws.s3-bucket"),
		},
		Cache: CacheConfig{
			Server:            viper.GetString("cache.server"),
			Port:              viper.GetInt("cache.port"),
			DB:                viper.GetInt("cache.db"),
			Pass:              viper.GetString("cache.pass"),
			Prefix:            viper.GetString("cache.prefix"),
			DefaultExpiration: viper.GetDuration("cache.default-expiration"),
			Master:            viper.GetString("cache.master-name"),
		},
		Lyft: LyftConfig{
			Client:       viper.GetString("lyft.key"),
			Secret:       viper.GetString("lyft.secret"),
			RefreshToken: viper.GetString("lyft.token"),
		},
		LyftProd: LyftProdConfig{
			Lyft: LyftConfig{
				Client:       viper.GetString("lyft-prod.key"),
				Secret:       viper.GetString("lyft-prod.secret"),
				RefreshToken: viper.GetString("lyft-prod.token"),
			},
			UserUUID: viper.GetString("lyft-prod.user-uuid"),
		},
		Twilio: TwilioConfig{
			Account:  viper.GetString("twilio.account"),
			Token:    viper.GetString("twilio.token"),
			Sender:   viper.GetString("twilio.sender"),
			TwiMLSID: viper.GetString("twilio.twiml-sid"),
		},
		BXE: BXEConfig{
			URL:    viper.GetString("bxe.url"),
			APIKey: viper.GetString("bxe.key"),
			Secret: viper.GetString("bxe.secret"),
		},
		Blue365: Blue365Config{
			URL:    viper.GetString("blue365.url"),
			APIKey: viper.GetString("blue365.key"),
			Secret: viper.GetString("blue365.secret"),
		},
		Email: EmailConfig{
			Server: viper.GetString("email.server"),
			Port:   viper.GetInt("email.port"),
			User:   viper.GetString("email.user"),
			Pass:   viper.GetString("email.pass"),
			Sender: viper.GetString("email.sender"),
		},
		GoogleShortener: GoogleShortenerConfig{
			APIKey:    viper.GetString("google-shortener.api-key"),
			ClientID:  viper.GetString("google-shortener.client-id"),
			SecretKey: viper.GetString("google-shortener.secret-key"),
		},
		Eligibility: EligibilityConfig{
			Url: viper.GetString("eligibility.url"),
		},
	}, nil
}

func setup() {
	viper.SetEnvPrefix("api")
	viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
	viper.AutomaticEnv()

	viper.SetConfigName("config")
	viper.AddConfigPath(".")
}
