package analyzer

import (
	"testing"

	troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func Test_compareSortedSlices(t *testing.T) {
	type args struct {
		actual   []interface{}
		expected []interface{}
	}

	tests := []struct {
		name  string
		args  args
		equal bool
	}{
		{
			name: "empty slices",
			args: args{
				actual:   []interface{}{},
				expected: []interface{}{},
			},
			equal: true,
		},
		{
			name: "same order slices",
			args: args{
				actual:   []interface{}{"a", "b", "c"},
				expected: []interface{}{"a", "b", "c"},
			},
			equal: true,
		},
		{
			name: "unordered slices",
			args: args{
				actual:   []interface{}{"a", "b", "c"},
				expected: []interface{}{"b", "a", "c"},
			},
			equal: true,
		},
		{
			name: "different type and unordered slices",
			args: args{
				actual:   []interface{}{1, "a", "c"},
				expected: []interface{}{"a", 1, "c"},
			},
			equal: true,
		},
		{
			name: "unordered slices with map",
			args: args{
				actual:   []interface{}{map[string]int{"a": 1}, "a", "c"},
				expected: []interface{}{"a", map[string]int{"a": 1}, "c"},
			},
			equal: true,
		},
		{
			name: "unequal slices with duplicates",
			args: args{
				actual:   []interface{}{"a", "a", "a", "c"},
				expected: []interface{}{"a", "a", "c", "c"},
			},
			equal: false,
		},
		{
			name: "unordered slices with boolean and strings",
			args: args{
				actual:   []interface{}{true, "a", false, true},
				expected: []interface{}{"a", true, false, true},
			},
			equal: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := compareSortedSlices(tt.args.actual, tt.args.expected)
			assert.Equalf(t, tt.equal, got, "compareSlices() = %v, want %v", got, tt.equal)
		})
	}
}

func Test_jsonCompare(t *testing.T) {
	tests := []struct {
		name         string
		isError      bool
		analyzer     troubleshootv1beta2.JsonCompare
		expectResult AnalyzeResult
		fileContents []byte
	}{
		{
			name: "basic comparison",
			analyzer: troubleshootv1beta2.JsonCompare{
				Outcomes: []*troubleshootv1beta2.Outcome{
					{
						Pass: &troubleshootv1beta2.SingleOutcome{
							Message: "pass",
						},
					},
					{
						Fail: &troubleshootv1beta2.SingleOutcome{
							Message: "fail",
						},
					},
				},
				CollectorName: "json-compare-1",
				FileName:      "json-compare-1.json",
				Value: `{
					"foo": "bar",
					"stuff": {
						"foo": "bar",
						"bar": true
					},
					"morestuff": [
						{
							"foo": {
								"bar": 123
							}
						}
					]
				}`,
			},
			expectResult: AnalyzeResult{
				IsPass:  true,
				IsWarn:  false,
				IsFail:  false,
				Title:   "json-compare-1",
				Message: "pass",
				IconKey: "kubernetes_text_analyze",
				IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
			},
			fileContents: []byte(`{
				"foo": "bar",
				"stuff": {
					"foo": "bar",
					"bar": true
				},
				"morestuff": [
					{
						"foo": {
							"bar": 123
						}
					}
				]
			}`),
		},
		{
			name: "basic comparison, but fail on match",
			analyzer: troubleshootv1beta2.JsonCompare{
				Outcomes: []*troubleshootv1beta2.Outcome{
					{
						Pass: &troubleshootv1beta2.SingleOutcome{
							Message: "pass",
							When:    "false",
						},
					},
					{
						Fail: &troubleshootv1beta2.SingleOutcome{
							Message: "fail",
							When:    "true",
						},
					},
				},
				CollectorName: "json-compare-1-1",
				FileName:      "json-compare-1-1.json",
				Value: `{
					"foo": "bar",
					"stuff": {
						"foo": "bar",
						"bar": true
					},
					"morestuff": [
						{
							"foo": {
								"bar": 123
							}
						}
					]
				}`,
			},
			expectResult: AnalyzeResult{
				IsPass:  false,
				IsWarn:  false,
				IsFail:  true,
				Title:   "json-compare-1-1",
				Message: "fail",
				IconKey: "kubernetes_text_analyze",
				IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
			},
			fileContents: []byte(`{
				"foo": "bar",
				"stuff": {
					"foo": "bar",
					"bar": true
				},
				"morestuff": [
					{
						"foo": {
							"bar": 123
						}
					}
				]
			}`),
		},
		{
			name: "comparison using path 1",
			analyzer: troubleshootv1beta2.JsonCompare{
				Outcomes: []*troubleshootv1beta2.Outcome{
					{
						Pass: &troubleshootv1beta2.SingleOutcome{
							Message: "pass",
						},
					},
					{
						Fail: &troubleshootv1beta2.SingleOutcome{
							Message: "fail",
						},
					},
				},
				CollectorName: "json-compare-2",
				FileName:      "json-compare-2.json",
				Path:          "morestuff",
				Value: `[
					{
						"foo": {
							"bar": 123
						}
					}
				]`,
			},
			expectResult: AnalyzeResult{
				IsPass:  true,
				IsWarn:  false,
				IsFail:  false,
				Title:   "json-compare-2",
				Message: "pass",
				IconKey: "kubernetes_text_analyze",
				IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
			},
			fileContents: []byte(`{
				"foo": "bar",
				"stuff": {
					"foo": "bar",
					"bar": true
				},
				"morestuff": [
					{
						"foo": {
							"bar": 123
						}
					}
				]
			}`),
		},
		{
			name: "comparison using path 2",
			analyzer: troubleshootv1beta2.JsonCompare{
				Outcomes: []*troubleshootv1beta2.Outcome{
					{
						Pass: &troubleshootv1beta2.SingleOutcome{
							Message: "pass",
						},
					},
					{
						Fail: &troubleshootv1beta2.SingleOutcome{
							Message: "fail",
						},
					},
				},
				CollectorName: "json-compare-3",
				FileName:      "json-compare-3.json",
				Path:          "morestuff.[0].foo.bar",
				Value:         `123`,
			},
			expectResult: AnalyzeResult{
				IsPass:  true,
				IsWarn:  false,
				IsFail:  false,
				Title:   "json-compare-3",
				Message: "pass",
				IconKey: "kubernetes_text_analyze",
				IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
			},
			fileContents: []byte(`{
				"foo": "bar",
				"stuff": {
					"foo": "bar",
					"bar": true
				},
				"morestuff": [
					{
						"foo": {
							"bar": 123
						}
					}
				]
			}`),
		},
		{
			name: "comparison using path 2, but warn on match",
			analyzer: troubleshootv1beta2.JsonCompare{
				Outcomes: []*troubleshootv1beta2.Outcome{
					{
						Pass: &troubleshootv1beta2.SingleOutcome{
							Message: "pass",
							When:    "false",
						},
					},
					{
						Warn: &troubleshootv1beta2.SingleOutcome{
							Message: "warn",
							When:    "true",
						},
					},
				},
				CollectorName: "json-compare-3-1",
				FileName:      "json-compare-3-1.json",
				Path:          "morestuff.[0].foo.bar",
				Value:         `123`,
			},
			expectResult: AnalyzeResult{
				IsPass:  false,
				IsWarn:  true,
				IsFail:  false,
				Title:   "json-compare-3-1",
				Message: "warn",
				IconKey: "kubernetes_text_analyze",
				IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
			},
			fileContents: []byte(`{
				"foo": "bar",
				"stuff": {
					"foo": "bar",
					"bar": true
				},
				"morestuff": [
					{
						"foo": {
							"bar": 123
						}
					}
				]
			}`),
		},
		{
			name: "basic comparison fail",
			analyzer: troubleshootv1beta2.JsonCompare{
				Outcomes: []*troubleshootv1beta2.Outcome{
					{
						Pass: &troubleshootv1beta2.SingleOutcome{
							Message: "pass",
						},
					},
					{
						Fail: &troubleshootv1beta2.SingleOutcome{
							Message: "fail",
						},
					},
				},
				CollectorName: "json-compare-4",
				FileName:      "json-compare-4.json",
				Value: `{
					"foo": "bar",
					"stuff": {
						"foo": "bar",
						"bar": true
					},
					"morestuff": [
						{
							"foo": {
								"bar": 123
							}
						}
					]
				}`,
			},
			expectResult: AnalyzeResult{
				IsPass:  false,
				IsWarn:  false,
				IsFail:  true,
				Title:   "json-compare-4",
				Message: "fail",
				IconKey: "kubernetes_text_analyze",
				IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
			},
			fileContents: []byte(`{
				"foo": "bar",
				"stuff": {
					"foo": "bar",
					"bar": true
				},
				"otherstuff": [
					{
						"foo": {
							"bar": 123
						}
					}
				]
			}`),
		},
		{
			name: "comparison using path fail 1",
			analyzer: troubleshootv1beta2.JsonCompare{
				Outcomes: []*troubleshootv1beta2.Outcome{
					{
						Pass: &troubleshootv1beta2.SingleOutcome{
							Message: "pass",
						},
					},
					{
						Fail: &troubleshootv1beta2.SingleOutcome{
							Message: "fail",
						},
					},
				},
				CollectorName: "json-compare-5",
				FileName:      "json-compare-5.json",
				Path:          "morestuff",
				Value: `[
					{
						"foo": {
							"bar": 321
						}
					}
				]`,
			},
			expectResult: AnalyzeResult{
				IsPass:  false,
				IsWarn:  false,
				IsFail:  true,
				Title:   "json-compare-5",
				Message: "fail",
				IconKey: "kubernetes_text_analyze",
				IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
			},
			fileContents: []byte(`{
				"foo": "bar",
				"stuff": {
					"foo": "bar",
					"bar": true
				},
				"morestuff": [
					{
						"foo": {
							"bar": 123
						}
					}
				]
			}`),
		},
		{
			name: "comparison using path, but pass when not matching",
			analyzer: troubleshootv1beta2.JsonCompare{
				Outcomes: []*troubleshootv1beta2.Outcome{
					{
						Pass: &troubleshootv1beta2.SingleOutcome{
							Message: "pass",
							When:    "false",
						},
					},
					{
						Fail: &troubleshootv1beta2.SingleOutcome{
							Message: "fail",
							When:    "true",
						},
					},
				},
				CollectorName: "json-compare-5-1",
				FileName:      "json-compare-5-1.json",
				Path:          "morestuff",
				Value: `[
					{
						"foo": {
							"bar": 321
						}
					}
				]`,
			},
			expectResult: AnalyzeResult{
				IsPass:  true,
				IsWarn:  false,
				IsFail:  false,
				Title:   "json-compare-5-1",
				Message: "pass",
				IconKey: "kubernetes_text_analyze",
				IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
			},
			fileContents: []byte(`{
				"foo": "bar",
				"stuff": {
					"foo": "bar",
					"bar": true
				},
				"morestuff": [
					{
						"foo": {
							"bar": 123
						}
					}
				]
			}`),
		},
		{
			name: "basic comparison warn",
			analyzer: troubleshootv1beta2.JsonCompare{
				Outcomes: []*troubleshootv1beta2.Outcome{
					{
						Pass: &troubleshootv1beta2.SingleOutcome{
							Message: "pass",
						},
					},
					{
						Warn: &troubleshootv1beta2.SingleOutcome{
							Message: "warn",
						},
					},
				},
				CollectorName: "json-compare-6",
				FileName:      "json-compare-6.json",
				Value: `{
					"foo": "bar",
					"stuff": {
						"foo": "bar",
						"bar": true
					},
					"morestuff": [
						{
							"foo": {
								"bar": 123
							}
						}
					]
				}`,
			},
			expectResult: AnalyzeResult{
				IsPass:  false,
				IsWarn:  true,
				IsFail:  false,
				Title:   "json-compare-6",
				Message: "warn",
				IconKey: "kubernetes_text_analyze",
				IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
			},
			fileContents: []byte(`{
				"foo": "bar",
				"stuff": {
					"foo": "bar",
					"bar": true
				},
				"otherstuff": [
					{
						"foo": {
							"bar": 123
						}
					}
				]
			}`),
		},
		{
			name:    "invalid json error",
			isError: true,
			analyzer: troubleshootv1beta2.JsonCompare{
				Outcomes: []*troubleshootv1beta2.Outcome{
					{
						Pass: &troubleshootv1beta2.SingleOutcome{
							Message: "pass",
						},
					},
					{
						Fail: &troubleshootv1beta2.SingleOutcome{
							Message: "fail",
						},
					},
				},
				CollectorName: "json-compare-7",
				FileName:      "json-compare-7.json",
				Path:          "morestuff",
				Value: `[
					{
						"foo": {
							"bar": 123
						}
					}
				]`,
			},
			fileContents: []byte(`{ "this: - is-invalid: json }`),
		},
		{
			name:    "no json error",
			isError: true,
			analyzer: troubleshootv1beta2.JsonCompare{
				Outcomes: []*troubleshootv1beta2.Outcome{
					{
						Pass: &troubleshootv1beta2.SingleOutcome{
							Message: "pass",
						},
					},
					{
						Fail: &troubleshootv1beta2.SingleOutcome{
							Message: "fail",
						},
					},
				},
				CollectorName: "json-compare-8",
				FileName:      "json-compare-8.json",
				Path:          "morestuff",
				Value: `[
					{
						"foo": {
							"bar": 123
						}
					}
				]`,
			},
			fileContents: []byte(``),
		},
		{
			name: "jsonpath comparison",
			analyzer: troubleshootv1beta2.JsonCompare{
				Outcomes: []*troubleshootv1beta2.Outcome{
					{
						Pass: &troubleshootv1beta2.SingleOutcome{
							Message: "pass",
						},
					},
					{
						Fail: &troubleshootv1beta2.SingleOutcome{
							Message: "fail",
						},
					},
				},
				CollectorName: "jsonpath-compare-1",
				FileName:      "jsonpath-compare-1.json",
				JsonPath:      "{$.morestuff[0]}",
				Value: `{
					"foo": {
						"bar": 123
					}
				}`,
			},
			expectResult: AnalyzeResult{
				IsPass:  true,
				IsWarn:  false,
				IsFail:  false,
				Title:   "jsonpath-compare-1",
				Message: "pass",
				IconKey: "kubernetes_text_analyze",
				IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
			},
			fileContents: []byte(`{
				"foo": "bar",
				"stuff": {
					"foo": "bar",
					"bar": true
				},
				"morestuff": [
					{
						"foo": {
							"bar": 123
						}
					}
				]
			}`),
		},
		{
			name: "jsonpath comparison, but fail on match",
			analyzer: troubleshootv1beta2.JsonCompare{
				Outcomes: []*troubleshootv1beta2.Outcome{
					{
						Pass: &troubleshootv1beta2.SingleOutcome{
							Message: "pass",
							When:    "false",
						},
					},
					{
						Fail: &troubleshootv1beta2.SingleOutcome{
							Message: "fail",
							When:    "true",
						},
					},
				},
				CollectorName: "jsonpath-compare-1-1",
				FileName:      "jsonpath-compare-1-1.json",
				JsonPath:      "{$.morestuff[0].foo.bar}",
				Value:         `123`,
			},
			expectResult: AnalyzeResult{
				IsPass:  false,
				IsWarn:  false,
				IsFail:  true,
				Title:   "jsonpath-compare-1-1",
				Message: "fail",
				IconKey: "kubernetes_text_analyze",
				IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
			},
			fileContents: []byte(`{
				"foo": "bar",
				"stuff": {
					"foo": "bar",
					"bar": true
				},
				"morestuff": [
					{
						"foo": {
							"bar": 123
						}
					}
				]
			}`),
		},
		{
			name: "jsonpath comparison, multiple values",
			analyzer: troubleshootv1beta2.JsonCompare{
				Outcomes: []*troubleshootv1beta2.Outcome{
					{
						Pass: &troubleshootv1beta2.SingleOutcome{
							Message: "pass",
						},
					},
					{
						Fail: &troubleshootv1beta2.SingleOutcome{
							Message: "fail",
						},
					},
				},
				CollectorName: "jsonpath-compare-2",
				FileName:      "jsonpath-compare-2.json",
				JsonPath:      "{$..bar}",
				Value:         `[true, 123]`,
			},
			expectResult: AnalyzeResult{
				IsPass:  true,
				IsWarn:  false,
				IsFail:  false,
				Title:   "jsonpath-compare-2",
				Message: "pass",
				IconKey: "kubernetes_text_analyze",
				IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
			},
			fileContents: []byte(`{
				"foo": "bar",
				"stuff": {
					"foo": "bar",
					"bar": true
				},
				"morestuff": [
					{
						"foo": {
							"bar": 123
						}
					}
				]
			}`),
		},
		{
			name: "basic comparison with outcome message templated",
			analyzer: troubleshootv1beta2.JsonCompare{
				Outcomes: []*troubleshootv1beta2.Outcome{
					{
						Pass: &troubleshootv1beta2.SingleOutcome{
							Message: "Status: {{ .morestuff.status }}, Info: {{ .morestuff.info }}",
							When:    "true",
						},
					},
					{
						Fail: &troubleshootv1beta2.SingleOutcome{
							Message: "Status: {{ .morestuff.status }}, Info: {{ .morestuff.info }}",
							When:    "false",
						},
					},
				},
				CollectorName: "json-compare",
				FileName:      "json-compare.json",
				Path:          "morestuff.status",
				Value:         `"ready"`,
			},
			expectResult: AnalyzeResult{
				IsPass:  false,
				IsWarn:  false,
				IsFail:  true,
				Title:   "json-compare",
				Message: "Status: notready, Info: morestuff is not ready",
				IconKey: "kubernetes_text_analyze",
				IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
			},
			fileContents: []byte(`{
			    "stuff": {
			      "status": "ready",
			      "info": "this stuff is ready"
			    },
			    "morestuff": {
			      "status": "notready",
			      "info": "morestuff is not ready"
			    }
			}`),
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			req := require.New(t)

			getCollectedFileContents := func(n string) ([]byte, error) {
				return test.fileContents, nil
			}

			actual, err := analyzeJsonCompare(&test.analyzer, getCollectedFileContents, test.analyzer.CollectorName)
			if !test.isError {
				req.NoError(err)
				req.Equal(test.expectResult, *actual)
			} else {
				req.Error(err)
			}
		})
	}
}
