package common

import (
	"fmt"
	"reflect"
	"strings"

	"github.com/mitchellh/reflectwalk"
)

type walker struct {
	depth int
	say   func(string)
}

func newDumpConfig(say func(string)) *walker {
	return &walker{
		depth: 0,
		say:   say,
	}
}

func (s *walker) Enter(l reflectwalk.Location) error {
	s.depth += 1
	return nil
}

func (s *walker) Exit(l reflectwalk.Location) error {
	s.depth -= 1
	return nil
}

func (s *walker) Struct(v reflect.Value) error {
	return nil
}

func (s *walker) StructField(f reflect.StructField, v reflect.Value) error {
	if !s.shouldDump(v) {
		return nil
	}

	switch v.Kind() {
	case reflect.String:
		s.say(fmt.Sprintf("%s=%s", f.Name, s.formatValue(f.Name, v.String())))
	}

	return nil
}

func (s *walker) shouldDump(v reflect.Value) bool {
	return s.depth == 2 && v.IsValid() && v.CanInterface()
}

func (s *walker) formatValue(name, value string) string {
	if s.isMaskable(name) {
		return strings.Repeat("*", len(value))
	}

	return value
}

func (s *walker) isMaskable(name string) bool {
	up := strings.ToUpper(name)
	return strings.Contains(up, "SECRET") || strings.Contains(up, "PASSWORD")
}

func DumpConfig(config interface{}, say func(string)) {
	walker := newDumpConfig(say)
	reflectwalk.Walk(config, walker)
}
