// Copyright (c) 2020 Gitpod GmbH. All rights reserved.
// Licensed under the GNU Affero General Public License (AGPL).
// See License.AGPL.txt in the project root for license information.

package log

import (
	"encoding/json"
	"fmt"
	"reflect"
	"strings"
)

var (
	redactedValue  = "[redacted]"
	redactedFields = []string{
		"auth_",
		"password",
		"token",
		"key",
		"jwt",
		"secret",
	}
)

// RedactJSON removes sensitive data from JSON structures
func RedactJSON(data []byte) (res []byte, err error) {
	var jsonBlob interface{}
	err = json.Unmarshal(data, &jsonBlob)
	if err != nil {
		return data, err
	}
	redactValue(&jsonBlob)

	return json.Marshal(jsonBlob)
}

// blatently copied from https://github.com/cloudfoundry/lager/blob/master/json_redacter.go#L45
func redactValue(data *interface{}) interface{} {
	if data == nil {
		return data
	}

	if a, ok := (*data).([]interface{}); ok {
		redactArray(&a)
	} else if m, ok := (*data).(map[string]interface{}); ok {
		redactObject(&m)
	} else if s, ok := (*data).(string); ok {
		for _, prohibited := range redactedFields {
			if strings.Contains(strings.ToLower(fmt.Sprintf("%v", s)), prohibited) {
				(*data) = redactedValue
				continue
			}
		}
	}
	return (*data)
}

func redactArray(data *[]interface{}) {
	for i := range *data {
		redactValue(&((*data)[i]))
	}
}

func redactObject(data *map[string]interface{}) {
	var forceRedact bool
loop:
	for k, v := range *data {
		for _, prohibited := range redactedFields {
			if strings.Contains(strings.ToLower(fmt.Sprintf("%v", k)), prohibited) {
				(*data)[k] = redactedValue
				continue
			}
		}

		if forceRedact {
			(*data)[k] = redactedValue
		}
		if (*data)[k] != redactedValue {
			//TODO: refactor
			//nolint:gosec
			was := (*data)[k]
			(*data)[k] = redactValue(&v)
			if !reflect.DeepEqual(was, (*data)[k]) {
				// force all the values redacted
				forceRedact = true
				goto loop
			}
		}
	}
}
