package http

import (
	"encoding/base64"
	"encoding/json"
	"errors"
	"strings"
	"testing"

	"go.uber.org/zap"
	"go.uber.org/zap/zaptest/observer"
	"google.golang.org/protobuf/types/known/structpb"
)

type failingRequestMarshaler struct{}

func (failingRequestMarshaler) MarshalJSON() ([]byte, error) {
	return nil, errors.New("canary-marshal-secret")
}

func TestLoggerWithDesensitizedRequest(t *testing.T) {
	const secret = "canary-request-secret"
	payload := `{"password":"` + secret + `","sink_uri":"kafka://broker/topic?sasl-password=` + secret + `","nested":{"sslKeyContent":"` + secret + `"},"items":["kafka://broker/topic?sasl-password=` + secret + `",{"name":"visible"}],"id":9007199254740993}`
	var object map[string]interface{}
	decoder := json.NewDecoder(strings.NewReader(payload))
	decoder.UseNumber()
	if err := decoder.Decode(&object); err != nil {
		t.Fatal(err)
	}
	protoBody, err := structpb.NewStruct(map[string]interface{}{"name": "visible", "password": secret, "sink_uri": "kafka://broker/topic?sasl-password=" + secret})
	if err != nil {
		t.Fatal(err)
	}
	for name, body := range map[string]interface{}{
		"object": object, "JSON string": payload, "JSON bytes": []byte(payload), "protobuf": protoBody,
		"opaque bytes": []byte(secret), "malformed JSON": `{"password":"` + secret,
		"marshal error": failingRequestMarshaler{}, "nil": nil,
	} {
		t.Run(name, func(t *testing.T) {
			core, entries := observer.New(zap.DebugLevel)
			client := &RestyClient{}
			client.loggerWithDesensitizedRequest(zap.New(core), body).Info("request")
			fields := entries.All()[0].ContextMap()
			encoded, _ := json.Marshal(fields)
			for _, value := range []string{secret, "canary-marshal-secret", base64.StdEncoding.EncodeToString([]byte(secret))} {
				if strings.Contains(string(encoded), value) {
					t.Fatal("request diagnostics leaked credentials")
				}
			}
			switch name {
			case "nil":
				if _, exists := fields["request_body"]; exists {
					t.Fatal("nil body should not add a log field")
				}
			case "opaque bytes", "malformed JSON", "marshal error":
				if fields["request_body"] != "[REDACTED]" {
					t.Fatal("unsafe body should be omitted")
				}
			default:
				if !strings.Contains(string(encoded), "visible") || !strings.Contains(string(encoded), "kafka://broker/topic") {
					t.Fatal("safe request details were lost")
				}
				if name != "protobuf" && !strings.Contains(string(encoded), "9007199254740993") {
					t.Fatal("request ID lost precision")
				}
			}
		})
	}
	if object["password"] != secret {
		t.Fatal("logging mutated the caller's request")
	}
}
