package ldmodel

import (
	"encoding/json"
	"testing"

	"github.com/launchdarkly/go-jsonstream/v3/jreader"
)

var (
	benchmarkBytesResult   []byte
	benchmarkErrorResult   error
	benchmarkFlagResult    FeatureFlag
	benchmarkSegmentResult Segment
)

func BenchmarkMarshalFlag(b *testing.B) {
	b.Run("all properties", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			benchmarkBytesResult, benchmarkErrorResult =
				jsonDataModelSerialization{}.MarshalFeatureFlag(flagWithAllProperties)
		}
	})

	b.Run("minimal properties", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			benchmarkBytesResult, benchmarkErrorResult =
				jsonDataModelSerialization{}.MarshalFeatureFlag(flagWithMinimalProperties)
		}
	})
}

func BenchmarkMarshalSegment(b *testing.B) {
	b.Run("all properties", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			benchmarkBytesResult, benchmarkErrorResult =
				jsonDataModelSerialization{}.MarshalSegment(segmentWithAllProperties)
		}
	})

	b.Run("minimal properties", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			benchmarkBytesResult, benchmarkErrorResult =
				jsonDataModelSerialization{}.MarshalSegment(segmentWithMinimalProperties)
		}
	})
}

func BenchmarkUnmarshalFlag(b *testing.B) {
	b.Run("all properties", func(b *testing.B) {
		bytes, _ := json.Marshal(flagWithAllPropertiesJSON)
		b.ResetTimer()
		for i := 0; i < b.N; i++ {
			benchmarkFlagResult, benchmarkErrorResult =
				jsonDataModelSerialization{}.UnmarshalFeatureFlag(bytes)
		}
	})

	b.Run("minimal properties", func(b *testing.B) {
		bytes, _ := json.Marshal(flagWithMinimalPropertiesJSON)
		b.ResetTimer()
		for i := 0; i < b.N; i++ {
			benchmarkFlagResult, benchmarkErrorResult =
				jsonDataModelSerialization{}.UnmarshalFeatureFlag(bytes)
		}
	})
}

func BenchmarkUnmarshalSegment(b *testing.B) {
	b.Run("all properties", func(b *testing.B) {
		bytes, _ := json.Marshal(segmentWithAllPropertiesJSON)
		b.ResetTimer()
		for i := 0; i < b.N; i++ {
			benchmarkSegmentResult, benchmarkErrorResult =
				jsonDataModelSerialization{}.UnmarshalSegment(bytes)
		}
	})

	b.Run("minimal properties", func(b *testing.B) {
		bytes, _ := json.Marshal(segmentWithMinimalPropertiesJSON)
		b.ResetTimer()
		for i := 0; i < b.N; i++ {
			benchmarkSegmentResult, benchmarkErrorResult =
				jsonDataModelSerialization{}.UnmarshalSegment(bytes)
		}
	})
}

func BenchmarkLargeFlagComparative(b *testing.B) {
	b.Run("our unmarshaler", func(b *testing.B) {
		bytes := makeLargeFlagJSON()
		b.ResetTimer()
		for i := 0; i < b.N; i++ {
			r := jreader.NewReader(bytes)
			var f FeatureFlag
			readFeatureFlag(&r, &f)
			// Calling the lower-level function readFeatureFlag means we're skipping the post-processing step,
			// since we're not doing that step in the comparative UnmarshalJSON benchmark.
			benchmarkErrorResult = r.Error()
			if benchmarkErrorResult != nil {
				b.Error(benchmarkErrorResult)
				b.FailNow()
			}
		}
	})

	b.Run("UnmarshalJSON for equivalent struct", func(b *testing.B) {
		bytes := makeLargeFlagJSON()
		b.ResetTimer()
		for i := 0; i < b.N; i++ {
			var f featureFlagEquivalentStruct
			benchmarkErrorResult = json.Unmarshal(bytes, &f)
			if benchmarkErrorResult != nil {
				b.Error(benchmarkErrorResult)
				b.FailNow()
			}
		}
	})
}

func BenchmarkLargeSegmentComparative(b *testing.B) {
	b.Run("our unmarshaler", func(b *testing.B) {
		bytes := makeLargeSegmentJSON()
		b.ResetTimer()
		for i := 0; i < b.N; i++ {
			r := jreader.NewReader(bytes)
			var s Segment
			readSegment(&r, &s)
			// Calling the lower-level function readSegment means we're skipping the post-processing step,
			// since we're not doing that step in the comparative UnmarshalJSON benchmark.
			benchmarkErrorResult = r.Error()
			if benchmarkErrorResult != nil {
				b.Error(benchmarkErrorResult)
				b.FailNow()
			}
		}
	})

	b.Run("UnmarshalJSON for equivalent struct", func(b *testing.B) {
		bytes := makeLargeSegmentJSON()
		b.ResetTimer()
		for i := 0; i < b.N; i++ {
			var s segmentEquivalentStruct
			benchmarkErrorResult = json.Unmarshal(bytes, &s)
			if benchmarkErrorResult != nil {
				b.Error(benchmarkErrorResult)
				b.FailNow()
			}
		}
	})
}
