// Copyright 2014 Manu Martinez-Almeida.  All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.

package sse

import (
	"bytes"
	"net/http/httptest"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestEncodeOnlyData(t *testing.T) {
	w := new(bytes.Buffer)
	event := Event{
		Data: "junk\n\njk\nid:fake",
	}
	err := Encode(w, event)
	assert.NoError(t, err)
	assert.Equal(t, w.String(),
		`data:junk
data:
data:jk
data:id:fake

`)

	decoded, _ := Decode(w)
	assert.Equal(t, "message", decoded[0].Event)
	assert.Equal(t, decoded[0].Data, []Event{event}[0].Data)
}

func TestEncodeWithEvent(t *testing.T) {
	w := new(bytes.Buffer)
	event := Event{
		Event: "t\n:<>\r\test",
		Data:  "junk\n\njk\nid:fake",
	}
	err := Encode(w, event)
	assert.NoError(t, err)
	assert.Equal(t, w.String(),
		`event:t\n:<>\r	est
data:junk
data:
data:jk
data:id:fake

`)

	decoded, _ := Decode(w)
	assert.Equal(t, "t\\n:<>\\r\test", decoded[0].Event)
	assert.Equal(t, decoded[0].Data, []Event{event}[0].Data)
}

func TestEncodeWithId(t *testing.T) {
	w := new(bytes.Buffer)
	err := Encode(w, Event{
		Id:   "t\n:<>\r\test",
		Data: "junk\n\njk\nid:fa\rke",
	})
	assert.NoError(t, err)
	assert.Equal(t, w.String(),
		`id:t\n:<>\r	est
data:junk
data:
data:jk
data:id:fa\rke

`)
}

func TestEncodeWithRetry(t *testing.T) {
	w := new(bytes.Buffer)
	err := Encode(w, Event{
		Retry: 11,
		Data:  "junk\n\njk\nid:fake\n",
	})
	assert.NoError(t, err)
	assert.Equal(t, w.String(),
		`retry:11
data:junk
data:
data:jk
data:id:fake
data:

`)
}

func TestEncodeWithEverything(t *testing.T) {
	w := new(bytes.Buffer)
	err := Encode(w, Event{
		Event: "abc",
		Id:    "12345",
		Retry: 10,
		Data:  "some data",
	})
	assert.NoError(t, err)
	assert.Equal(t, w.String(), "id:12345\nevent:abc\nretry:10\ndata:some data\n\n")
}

func TestEncodeMap(t *testing.T) {
	w := new(bytes.Buffer)
	err := Encode(w, Event{
		Event: "a map",
		Data: map[string]interface{}{
			"foo": "b\n\rar",
			"bar": "id: 2",
		},
	})
	assert.NoError(t, err)
	assert.Equal(t, w.String(), "event:a map\ndata:{\"bar\":\"id: 2\",\"foo\":\"b\\n\\rar\"}\n\n")
}

func TestEncodeSlice(t *testing.T) {
	w := new(bytes.Buffer)
	err := Encode(w, Event{
		Event: "a slice",
		Data:  []interface{}{1, "text", map[string]interface{}{"foo": "bar"}},
	})
	assert.NoError(t, err)
	assert.Equal(t, w.String(), "event:a slice\ndata:[1,\"text\",{\"foo\":\"bar\"}]\n\n")
}

func TestEncodeStruct(t *testing.T) {
	myStruct := struct {
		A int
		B string `json:"value"`
	}{1, "number"}

	w := new(bytes.Buffer)
	err := Encode(w, Event{
		Event: "a struct",
		Data:  myStruct,
	})
	assert.NoError(t, err)
	assert.Equal(t, w.String(), "event:a struct\ndata:{\"A\":1,\"value\":\"number\"}\n\n")

	w.Reset()
	err = Encode(w, Event{
		Event: "a struct",
		Data:  &myStruct,
	})
	assert.NoError(t, err)
	assert.Equal(t, w.String(), "event:a struct\ndata:{\"A\":1,\"value\":\"number\"}\n\n")
}

func TestEncodeInteger(t *testing.T) {
	w := new(bytes.Buffer)
	err := Encode(w, Event{
		Event: "an integer",
		Data:  1,
	})
	assert.NoError(t, err)
	assert.Equal(t, w.String(), "event:an integer\ndata:1\n\n")
}

func TestEncodeFloat(t *testing.T) {
	w := new(bytes.Buffer)
	err := Encode(w, Event{
		Event: "Float",
		Data:  1.5,
	})
	assert.NoError(t, err)
	assert.Equal(t, w.String(), "event:Float\ndata:1.5\n\n")
}

func TestEncodeStream(t *testing.T) {
	w := new(bytes.Buffer)

	_ = Encode(w, Event{
		Event: "float",
		Data:  1.5,
	})

	_ = Encode(w, Event{
		Id:   "123",
		Data: map[string]interface{}{"foo": "bar", "bar": "foo"},
	})

	_ = Encode(w, Event{
		Id:    "124",
		Event: "chat",
		Data:  "hi! dude",
	})
	assert.Equal(t, w.String(),
		"event:float\ndata:1.5\n\n"+
			"id:123\ndata:{\"bar\":\"foo\",\"foo\":\"bar\"}\n\n"+
			"id:124\nevent:chat\ndata:hi! dude\n\n")
}

func TestRenderSSE(t *testing.T) {
	w := httptest.NewRecorder()

	err := (Event{
		Event: "msg",
		Data:  "hi! how are you?",
	}).Render(w)

	assert.NoError(t, err)
	assert.Equal(t, w.Body.String(), "event:msg\ndata:hi! how are you?\n\n")
	assert.Equal(t, w.Header().Get("Content-Type"), "text/event-stream;charset=utf-8")
	assert.Equal(t, w.Header().Get("Cache-Control"), "no-cache")
}

func BenchmarkResponseWriter(b *testing.B) {
	w := httptest.NewRecorder()
	b.ResetTimer()
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		_ = (Event{
			Event: "new_message",
			Data:  "hi! how are you? I am fine. this is a long stupid message!!!",
		}).Render(w)
	}
}

func BenchmarkFullSSE(b *testing.B) {
	buf := new(bytes.Buffer)
	b.ResetTimer()
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		_ = Encode(buf, Event{
			Event: "new_message",
			Id:    "13435",
			Retry: 10,
			Data:  "hi! how are you? I am fine. this is a long stupid message!!!",
		})
		buf.Reset()
	}
}

func BenchmarkNoRetrySSE(b *testing.B) {
	buf := new(bytes.Buffer)
	b.ResetTimer()
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		_ = Encode(buf, Event{
			Event: "new_message",
			Id:    "13435",
			Data:  "hi! how are you? I am fine. this is a long stupid message!!!",
		})
		buf.Reset()
	}
}

func BenchmarkSimpleSSE(b *testing.B) {
	buf := new(bytes.Buffer)
	b.ResetTimer()
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		_ = Encode(buf, Event{
			Event: "new_message",
			Data:  "hi! how are you? I am fine. this is a long stupid message!!!",
		})
		buf.Reset()
	}
}
