package health_test

import (
	"context"
	"errors"
	"testing"
	"time"

	"gitea.dwysokinski.me/twhelp/corev3/internal/health"
	"gitea.dwysokinski.me/twhelp/corev3/internal/health/healthtest"
	"github.com/google/go-cmp/cmp"
	"github.com/google/go-cmp/cmp/cmpopts"
	"github.com/stretchr/testify/assert"
)

var errCheckFailed = errors.New("failed")

func TestHealth_CheckReady(t *testing.T) {
	t.Parallel()

	tests := []struct {
		name           string
		options        []health.Option
		expectedResult health.Result
	}{
		{
			name: "0 checks",
			expectedResult: health.Result{
				Status: health.StatusPass,
				Checks: make(map[string][]health.SingleCheckResult),
			},
		},
		{
			name: "3 checks passed",
			options: []health.Option{
				health.WithReadyCheck(healthtest.Checker{Nam: "test", Err: nil}),
				health.WithReadyCheck(healthtest.Checker{Nam: "test", Err: nil}),
				health.WithReadyCheck(healthtest.Checker{Nam: "test2", Err: nil}),
				health.WithMaxConcurrent(2),
			},
			expectedResult: health.Result{
				Status: health.StatusPass,
				Checks: map[string][]health.SingleCheckResult{
					"test": {
						{
							Status: health.StatusPass,
						},
						{
							Status: health.StatusPass,
						},
					},
					"test2": {
						{
							Status: health.StatusPass,
						},
					},
				},
			},
		},
		{
			name: "2 checks passed, 1 check failed",
			options: []health.Option{
				health.WithReadyCheck(healthtest.Checker{Nam: "test", Err: nil}),
				health.WithReadyCheck(healthtest.Checker{Nam: "test", Err: nil}),
				health.WithReadyCheck(healthtest.Checker{Nam: "test2", Err: errCheckFailed}),
			},
			expectedResult: health.Result{
				Status: health.StatusFail,
				Checks: map[string][]health.SingleCheckResult{
					"test": {
						{
							Status: health.StatusPass,
						},
						{
							Status: health.StatusPass,
						},
					},
					"test2": {
						{
							Status: health.StatusFail,
							Err:    errCheckFailed,
						},
					},
				},
			},
		},
	}

	for _, tt := range tests {
		tt := tt

		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()

			result := health.New(tt.options...).CheckReady(context.Background())

			assert.Empty(t, cmp.Diff(
				tt.expectedResult,
				result,
				cmpopts.IgnoreTypes(time.Time{}),
				cmpopts.EquateErrors(),
			))
		})
	}
}

func TestHealth_CheckLive(t *testing.T) {
	t.Parallel()

	tests := []struct {
		name           string
		options        []health.Option
		expectedResult health.Result
	}{
		{
			name: "0 checks",
			expectedResult: health.Result{
				Status: health.StatusPass,
				Checks: make(map[string][]health.SingleCheckResult),
			},
		},
		{
			name: "3 checks passed",
			options: []health.Option{
				health.WithLiveCheck(healthtest.Checker{Nam: "test", Err: nil}),
				health.WithLiveCheck(healthtest.Checker{Nam: "test", Err: nil}),
				health.WithLiveCheck(healthtest.Checker{Nam: "test2", Err: nil}),
				health.WithMaxConcurrent(2),
			},
			expectedResult: health.Result{
				Status: health.StatusPass,
				Checks: map[string][]health.SingleCheckResult{
					"test": {
						{
							Status: health.StatusPass,
						},
						{
							Status: health.StatusPass,
						},
					},
					"test2": {
						{
							Status: health.StatusPass,
						},
					},
				},
			},
		},
		{
			name: "2 checks passed, 1 check failed",
			options: []health.Option{
				health.WithLiveCheck(healthtest.Checker{Nam: "test", Err: nil}),
				health.WithLiveCheck(healthtest.Checker{Nam: "test", Err: nil}),
				health.WithLiveCheck(healthtest.Checker{Nam: "test2", Err: errCheckFailed}),
			},
			expectedResult: health.Result{
				Status: health.StatusFail,
				Checks: map[string][]health.SingleCheckResult{
					"test": {
						{
							Status: health.StatusPass,
						},
						{
							Status: health.StatusPass,
						},
					},
					"test2": {
						{
							Status: health.StatusFail,
							Err:    errCheckFailed,
						},
					},
				},
			},
		},
	}

	for _, tt := range tests {
		tt := tt

		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()

			result := health.New(tt.options...).CheckLive(context.Background())

			assert.Empty(t, cmp.Diff(
				tt.expectedResult,
				result,
				cmpopts.IgnoreTypes(time.Time{}),
				cmpopts.EquateErrors(),
			))
		})
	}
}
