package engine

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

	"github.com/docker/cli/cli/command/formatter"
	clitypes "github.com/docker/cli/types"
	"gotest.tools/v3/assert"
	is "gotest.tools/v3/assert/cmp"
)

func TestUpdateContextWrite(t *testing.T) {
	cases := []struct {
		context  formatter.Context
		expected string
	}{
		// Errors
		{
			formatter.Context{Format: "{{InvalidFunction}}"},
			`Template parsing error: template: :1: function "InvalidFunction" not defined
`,
		},
		{
			formatter.Context{Format: "{{nil}}"},
			`Template parsing error: template: :1:2: executing "" at <nil>: nil is not a command
`,
		},
		// Table format
		{
			formatter.Context{Format: NewUpdatesFormat("table", false)},
			`TYPE                VERSION             NOTES
updateType1         version1            description 1
updateType2         version2            description 2
`,
		},
		{
			formatter.Context{Format: NewUpdatesFormat("table", true)},
			`version1
version2
`,
		},
		{
			formatter.Context{Format: NewUpdatesFormat("table {{.Version}}", false)},
			`VERSION
version1
version2
`,
		},
		{
			formatter.Context{Format: NewUpdatesFormat("table {{.Version}}", true)},
			`VERSION
version1
version2
`,
		},
		// Raw Format
		{
			formatter.Context{Format: NewUpdatesFormat("raw", false)},
			`update_version: version1
type: updateType1
notes: description 1

update_version: version2
type: updateType2
notes: description 2

`,
		},
		{
			formatter.Context{Format: NewUpdatesFormat("raw", true)},
			`update_version: version1
update_version: version2
`,
		},
		// Custom Format
		{
			formatter.Context{Format: NewUpdatesFormat("{{.Version}}", false)},
			`version1
version2
`,
		},
	}

	for _, testcase := range cases {
		updates := []clitypes.Update{
			{Type: "updateType1", Version: "version1", Notes: "description 1"},
			{Type: "updateType2", Version: "version2", Notes: "description 2"},
		}
		out := &bytes.Buffer{}
		testcase.context.Output = out
		err := UpdatesWrite(testcase.context, updates)
		if err != nil {
			assert.Error(t, err, testcase.expected)
		} else {
			assert.Check(t, is.Equal(testcase.expected, out.String()))
		}
	}
}

func TestUpdateContextWriteJSON(t *testing.T) {
	updates := []clitypes.Update{
		{Type: "updateType1", Version: "version1", Notes: "note1"},
		{Type: "updateType2", Version: "version2", Notes: "note2"},
	}
	expectedJSONs := []map[string]interface{}{
		{"Version": "version1", "Notes": "note1", "Type": "updateType1"},
		{"Version": "version2", "Notes": "note2", "Type": "updateType2"},
	}

	out := &bytes.Buffer{}
	err := UpdatesWrite(formatter.Context{Format: "{{json .}}", Output: out}, updates)
	if err != nil {
		t.Fatal(err)
	}
	for i, line := range strings.Split(strings.TrimSpace(out.String()), "\n") {
		var m map[string]interface{}
		if err := json.Unmarshal([]byte(line), &m); err != nil {
			t.Fatal(err)
		}
		assert.Check(t, is.DeepEqual(expectedJSONs[i], m))
	}
}

func TestUpdateContextWriteJSONField(t *testing.T) {
	updates := []clitypes.Update{
		{Type: "updateType1", Version: "version1"},
		{Type: "updateType2", Version: "version2"},
	}
	out := &bytes.Buffer{}
	err := UpdatesWrite(formatter.Context{Format: "{{json .Type}}", Output: out}, updates)
	if err != nil {
		t.Fatal(err)
	}
	for i, line := range strings.Split(strings.TrimSpace(out.String()), "\n") {
		var s string
		if err := json.Unmarshal([]byte(line), &s); err != nil {
			t.Fatal(err)
		}
		assert.Check(t, is.Equal(updates[i].Type, s))
	}
}
