package jwriter

import (
	"bytes"
	"encoding/json"
	"testing"

	"github.com/launchdarkly/go-jsonstream/v3/internal/commontest"
)

// These benchmarks perform equivalent actions to the ones in writer_benchmark_test.go, but using
// the default reflection-based mechanism from the json/encoding package, so we can see how much
// less efficient that is than our default implementation and the easyjson implementation.

func BenchmarkJSONMarshalComparatives(b *testing.B) {
	b.Run("Null", benchmarkWriteNullJSONMarshal)
	b.Run("Boolean", benchmarkWriteBooleanJSONMarshal)
	b.Run("NumberInt", benchmarkWriteNumberIntJSONMarshal)
	b.Run("NumberFloat", benchmarkWriteNumberFloatJSONMarshal)
	b.Run("String", benchmarkWriteStringJSONMarshal)
	b.Run("ArrayOfBools", benchmarkWriteArrayOfBoolsJSONMarshal)
	b.Run("ArrayOfStrings", benchmarkWriteArrayOfStringsJSONMarshal)
	b.Run("Object", benchmarkWriteObjectJSONMarshal)
}

func benchmarkWriteNullJSONMarshal(b *testing.B) {
	expected := []byte("null")
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		data, err := json.Marshal(nil)
		benchmarkExpectJSONMarshalOutput(b, err, data, expected)
	}
}

func benchmarkWriteBooleanJSONMarshal(b *testing.B) {
	expected := []byte("true")
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		data, err := json.Marshal(true)
		benchmarkExpectJSONMarshalOutput(b, err, data, expected)
	}
}

func benchmarkWriteNumberIntJSONMarshal(b *testing.B) {
	expected := []byte("123")
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		data, err := json.Marshal(123)
		benchmarkExpectJSONMarshalOutput(b, err, data, expected)
	}
}

func benchmarkWriteNumberFloatJSONMarshal(b *testing.B) {
	expected := []byte("1234.5")
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		data, err := json.Marshal(1234.5)
		benchmarkExpectJSONMarshalOutput(b, err, data, expected)
	}
}

func benchmarkWriteStringJSONMarshal(b *testing.B) {
	expected := []byte(`"abc"`)
	val := "abc"
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		data, err := json.Marshal(val)
		benchmarkExpectJSONMarshalOutput(b, err, data, expected)
	}
}

func benchmarkWriteArrayOfBoolsJSONMarshal(b *testing.B) {
	vals := commontest.MakeBools()
	expected := commontest.MakeBoolsJSON(vals)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		data, err := json.Marshal(vals)
		benchmarkExpectJSONMarshalOutput(b, err, data, expected)
	}
}

func benchmarkWriteArrayOfStringsJSONMarshal(b *testing.B) {
	vals := commontest.MakeStrings()
	expected := commontest.MakeStringsJSON(vals)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		data, err := json.Marshal(vals)
		benchmarkExpectJSONMarshalOutput(b, err, data, expected)
	}
}

func benchmarkWriteObjectJSONMarshal(b *testing.B) {
	for i := 0; i < b.N; i++ {
		data, err := json.Marshal(ExampleStructWrapper(commontest.ExampleStructValue))
		benchmarkExpectJSONMarshalOutput(b, err, data, commontest.ExampleStructData)
	}
}

func benchmarkExpectJSONMarshalOutput(b *testing.B, err error, actualJSON []byte, expectedJSON []byte) {
	if err != nil {
		b.Error(err)
		b.FailNow()
	}
	if !bytes.Equal(expectedJSON, actualJSON) {
		b.FailNow()
	}
}
