package entity

import (
	"encoding/json"
	"testing"

	"github.com/pingcap/tiflow/dm/pkg/checker"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestPremiumResultsValue(t *testing.T) {
	tests := map[string]struct {
		input   PremiumResults
		wantErr bool
	}{
		"empty results": {
			input: PremiumResults{
				Results: []*checker.Result{},
			},
			wantErr: false,
		},
		"single result": {
			input: PremiumResults{
				Results: []*checker.Result{
					{
						ID:    1,
						State: "pass",
					},
				},
			},
			wantErr: false,
		},
		"multiple results": {
			input: PremiumResults{
				Results: []*checker.Result{
					{
						ID:    1,
						State: "pass",
					},
					{
						ID:    2,
						State: "warn",
					},
				},
			},
			wantErr: false,
		},
	}

	for name, tc := range tests {
		t.Run(name, func(t *testing.T) {
			val, err := tc.input.Value()
			if tc.wantErr {
				assert.Error(t, err)
				return
			}
			assert.NoError(t, err)
			assert.NotNil(t, val)

			str, ok := val.(string)
			assert.True(t, ok, "Value() should return string")

			var result map[string]interface{}
			err = json.Unmarshal([]byte(str), &result)
			assert.NoError(t, err, "Value() should return valid JSON")
		})
	}
}

func TestPremiumResultsScan(t *testing.T) {
	tests := map[string]struct {
		input    []byte
		expected PremiumResults
		wantErr  bool
	}{
		"empty results": {
			input:    []byte(`{"Results":[]}`),
			expected: PremiumResults{Results: []*checker.Result{}},
			wantErr:  false,
		},
		"single result": {
			input: []byte(`{"Results":[{"ID":1,"State":"pass"}]}`),
			expected: PremiumResults{
				Results: []*checker.Result{
					{
						ID:    1,
						State: "pass",
					},
				},
			},
			wantErr: false,
		},
		"invalid json": {
			input:    []byte(`{invalid}`),
			expected: PremiumResults{Results: []*checker.Result{}},
			wantErr:  true,
		},
	}

	for name, tc := range tests {
		t.Run(name, func(t *testing.T) {
			var result PremiumResults
			err := result.Scan(tc.input)

			if tc.wantErr {
				assert.Error(t, err)
				return
			}
			assert.NoError(t, err)

			if tc.expected.Results != nil {
				assert.Equal(t, len(tc.expected.Results), len(result.Results))
				for i, expectedResult := range tc.expected.Results {
					assert.Equal(t, expectedResult.ID, result.Results[i].ID)
					assert.Equal(t, expectedResult.State, result.Results[i].State)
				}
			}
		})
	}
}

func TestPremiumResultsRoundTrip(t *testing.T) {
	tests := map[string]struct {
		input PremiumResults
	}{
		"empty results": {
			input: PremiumResults{
				Results: []*checker.Result{},
			},
		},
		"precheck results": {
			input: PremiumResults{
				Results: []*checker.Result{
					{
						ID:    1,
						State: "pass",
					},
					{
						ID:    2,
						State: "pass",
					},
				},
			},
		},
	}

	for name, tc := range tests {
		t.Run(name, func(t *testing.T) {
			val, err := tc.input.Value()
			assert.NoError(t, err)

			var result PremiumResults
			err = result.Scan([]byte(val.(string)))
			assert.NoError(t, err)

			assert.Equal(t, len(tc.input.Results), len(result.Results))
			for i, expectedResult := range tc.input.Results {
				assert.Equal(t, expectedResult.ID, result.Results[i].ID)
				assert.Equal(t, expectedResult.State, result.Results[i].State)
			}
		})
	}
}

func TestPremiumDMPrecheckItemSummarizeResults(t *testing.T) {
	tests := map[string]struct {
		item             *PremiumDMPrecheckItem
		wantOK           bool
		wantDesc         string
		wantState        checker.State
		wantReasons      []string
		wantInstructions []string
	}{
		"nil item": {
			item:   nil,
			wantOK: false,
		},
		"empty results": {
			item:   &PremiumDMPrecheckItem{},
			wantOK: false,
		},
		"aggregates failure reasons and instructions": {
			item: &PremiumDMPrecheckItem{
				Results: PremiumResults{
					Results: []*checker.Result{
						{
							Desc:        "check desc",
							State:       checker.StateSuccess,
							Instruction: "do first",
							Errors: []*checker.Error{{
								ShortErr: "first error",
							}},
						},
						{
							State:       checker.StateWarning,
							Instruction: "do second",
						},
						{
							State: checker.StateFailure,
							Errors: []*checker.Error{{
								ShortErr: "second error",
							}},
						},
					},
				},
			},
			wantOK:           true,
			wantDesc:         "check desc",
			wantState:        checker.StateFailure,
			wantReasons:      []string{"first error", "second error"},
			wantInstructions: []string{"do first", "do second"},
		},
	}

	for name, tc := range tests {
		t.Run(name, func(t *testing.T) {
			summary, ok := tc.item.SummarizeResults()
			require.Equal(t, tc.wantOK, ok)
			if !tc.wantOK {
				require.Nil(t, summary)
				return
			}
			require.NotNil(t, summary)
			assert.Equal(t, tc.wantDesc, summary.Desc)
			assert.Equal(t, tc.wantState, summary.State)
			assert.Equal(t, tc.wantReasons, summary.Reasons)
			assert.Equal(t, tc.wantInstructions, summary.Instructions)
		})
	}
}
