package interfaces

import (
	"context"
	"encoding/base64"
	"fmt"
	"github.com/armory/spinnaker-operator/pkg/inspect"
)

// GetServiceSettingsPropString returns a service settings prop for a given service
func (s *SpinnakerConfig) GetServiceSettingsPropString(ctx context.Context, svc, prop string) (string, error) {
	return inspect.GetObjectPropString(ctx, s.ServiceSettings, fmt.Sprintf("%s.%s", svc, prop))
}

// GetHalConfigPropString returns a property stored in halconfig, decrypting it if necessary
// We use the dot notation including for arrays
// e.g. providers.aws.accounts.0.name
func (s *SpinnakerConfig) GetHalConfigPropString(ctx context.Context, prop string) (string, error) {
	return inspect.GetObjectPropString(ctx, s.Config, prop)
}

// GetRawHalConfigPropString returns a property stored in halconfig
// We use the dot notation including for arrays
// e.g. providers.aws.accounts.0.name
func (s *SpinnakerConfig) GetRawHalConfigPropString(prop string) (string, error) {
	return inspect.GetRawObjectPropString(s.Config, prop)
}

// GetHalConfigObjectArray reads an untyped array
func (s *SpinnakerConfig) GetHalConfigObjectArray(ctx context.Context, prop string) ([]map[string]interface{}, error) {
	return inspect.GetObjectArray(s.Config, prop)
}

// GetServiceConfigObjectArray reads an untyped array from profile config
func (s *SpinnakerConfig) GetServiceConfigObjectArray(svc, prop string) ([]map[string]interface{}, error) {
	p, ok := s.Profiles[svc]
	if ok {
		return inspect.GetObjectArray(p, prop)
	}
	return nil, nil
}

// GetConfigObjectArray reads an untyped array from profile config, if not found, reads itt from hal config
func (s *SpinnakerConfig) GetConfigObjectArray(svc, prop string) ([]map[string]interface{}, ConfigSource, error) {
	p, ok := s.Profiles[svc]
	if ok {
		a, err := inspect.GetObjectArray(p, prop)
		if err == nil && a != nil {
			return a, ProfileConfigSource, err
		} else {
			a, err = inspect.GetObjectArray(s.Config, prop)
			return a, HalConfigSource, err
		}
	} else {
		a, err := inspect.GetObjectArray(s.Config, prop)
		return a, HalConfigSource, err
	}
}

// SetHalConfigProp sets a property in the config
func (s *SpinnakerConfig) SetHalConfigProp(prop string, value interface{}) error {
	return inspect.SetObjectProp(s.Config, prop, value)
}

// SetServiceConfigProp sets a property in the profile config
func (s *SpinnakerConfig) SetServiceConfigProp(svc, prop string, value interface{}) error {
	p, ok := s.Profiles[svc]
	if ok {
		return inspect.SetObjectProp(p, prop, value)
	}
	return nil
}

// GetHalConfigPropBool returns a boolean property in halconfig
func (s *SpinnakerConfig) GetHalConfigPropBool(prop string, defaultVal bool) (bool, error) {
	return inspect.GetObjectPropBool(s.Config, prop, defaultVal)
}

// GetServiceConfigPropString returns the value of the prop in a service profile file
func (s *SpinnakerConfig) GetServiceConfigPropString(ctx context.Context, svc, prop string) (string, error) {
	p, ok := s.Profiles[svc]
	if ok {
		return inspect.GetObjectPropString(ctx, p, prop)
	}
	return "", nil
}

// GetRawServiceConfigPropString returns the value of the prop in a service profile file, without decrypting any secret reference.
func (s *SpinnakerConfig) GetRawServiceConfigPropString(svc, prop string) (string, error) {
	p, ok := s.Profiles[svc]
	if ok {
		return inspect.GetRawObjectPropString(p, prop)
	}
	return "", nil
}

// GetRawConfigPropString returns the raw value of the prop in a service profile file, if not found, returns the value of the prop in the main hal config file
func (s *SpinnakerConfig) GetRawConfigPropString(svc, prop string) (string, ConfigSource, error) {
	p, ok := s.Profiles[svc]
	if ok {
		v, err := inspect.GetRawObjectPropString(p, prop)
		if err == nil {
			return v, ProfileConfigSource, err
		} else {
			v, err = s.GetRawHalConfigPropString(prop)
			return v, HalConfigSource, err
		}
	} else {
		v, err := s.GetRawHalConfigPropString(prop)
		return v, HalConfigSource, err
	}
}

// GetFileContent returns the file content at key. It will be base64 decoded if possible.
func (s *SpinnakerConfig) GetFileContent(key string) []byte {
	str := s.Files[key]
	r, err := base64.StdEncoding.DecodeString(str)
	if err != nil {
		return []byte(str)
	}
	return r

}

// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SpinnakerConfig) DeepCopyInto(out *SpinnakerConfig) {
	*out = *in
	if in.Files != nil {
		in, out := &in.Files, &out.Files
		*out = make(map[string]string, len(*in))
		for key, val := range *in {
			(*out)[key] = val
		}
	}
	if in.ServiceSettings != nil {
		in, out := &in.ServiceSettings, &out.ServiceSettings
		*out = make(map[string]FreeForm, len(*in))
		for key, val := range *in {
			(*out)[key] = *val.DeepCopy()
		}
	}
	if in.Profiles != nil {
		in, out := &in.Profiles, &out.Profiles
		*out = make(map[string]FreeForm, len(*in))
		for key, val := range *in {
			(*out)[key] = *val.DeepCopy()
		}
	}
	in.Config.DeepCopyInto(&out.Config)
	return
}

// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SpinnakerConfig.
func (in *SpinnakerConfig) DeepCopy() *SpinnakerConfig {
	if in == nil {
		return nil
	}
	out := new(SpinnakerConfig)
	in.DeepCopyInto(out)
	return out
}
