package viper

import (
	"bytes"
	"io"
	"log"

	v "github.com/spf13/viper"
)

const (
	Provider = "oneconfig"
	Endpoint = "default"
)

type Response struct {
	Value []byte
	Error error
}

// A ConfigManager retrieves and decrypts configuration from a key/value store.
type ConfigManager interface {
	Get(key string) ([]byte, error)
	Watch(key string, stop chan bool) <-chan *Response
}

type remoteConfigProvider struct {
}

func (kcp remoteConfigProvider) Get(rp v.RemoteProvider) (io.Reader, error) {
	cm, err := getConfigManager(rp)
	if err != nil {
		log.Printf("Failed to create Config Manager: %v", err)
		return nil, err
	}
	b, err := cm.Get(rp.Path())
	if err != nil {
		log.Printf("Failed to get config: %v", err)
		return nil, err
	}
	return bytes.NewReader(b), nil
}

func (kcp remoteConfigProvider) Watch(rp v.RemoteProvider) (io.Reader, error) {
	cm, err := getConfigManager(rp)
	if err != nil {
		log.Printf("Failed to create Config Manager: %v", err)
		return nil, err
	}
	resp, err := cm.Get(rp.Path())
	if err != nil {
		log.Printf("Failed to get config: %v", err)
		return nil, err
	}

	return bytes.NewReader(resp), nil
}

func (kcp remoteConfigProvider) WatchChannel(rp v.RemoteProvider) (<-chan *v.RemoteResponse, chan bool) {
	cm, err := getConfigManager(rp)
	if err != nil {
		log.Printf("Failed to create Config Manager: %v", err)
		return nil, nil
	}
	quit := make(chan bool)
	quitwc := make(chan bool)
	viperResponsCh := make(chan *v.RemoteResponse)
	cryptoResponseCh := cm.Watch(rp.Path(), quit)
	// need this function to convert the Channel response from crypt.Response to viper.Response
	go func(cr <-chan *Response, vr chan<- *v.RemoteResponse, quitwc chan bool, quit chan<- bool) {
		defer close(quit)
		defer close(quitwc)

		for {
			select {
			case <-quitwc:
				quit <- true
				return
			case resp := <-cr:
				if resp.Error != nil {
					log.Printf("Received error in watch event: %v", resp.Error)
				}
				vr <- &v.RemoteResponse{
					Error: resp.Error,
					Value: resp.Value,
				}

			}

		}
	}(cryptoResponseCh, viperResponsCh, quitwc, quit)

	return viperResponsCh, quitwc
}

func getConfigManager(rp v.RemoteProvider) (ConfigManager, error) {
	var cm ConfigManager
	var err error

	switch rp.Provider() {
	default:
		cm, err = NewConfigMapConfigManager(rp.SecretKeyring())
	}
	if err != nil {
		return nil, err
	}
	return cm, nil
}

func init() {
	v.RemoteConfig = &remoteConfigProvider{}
	v.SupportedRemoteProviders = []string{Provider}
}
