package opsgenie

import (
	"net/http"
	"reflect"
	"testing"

	"github.com/TwiN/gatus/v3/alerting/alert"
	"github.com/TwiN/gatus/v3/client"
	"github.com/TwiN/gatus/v3/core"
	"github.com/TwiN/gatus/v3/test"
)

func TestAlertProvider_IsValid(t *testing.T) {
	invalidProvider := AlertProvider{APIKey: ""}
	if invalidProvider.IsValid() {
		t.Error("provider shouldn't have been valid")
	}
	validProvider := AlertProvider{APIKey: "00000000-0000-0000-0000-000000000000"}
	if !validProvider.IsValid() {
		t.Error("provider should've been valid")
	}
}

func TestAlertProvider_Send(t *testing.T) {
	defer client.InjectHTTPClient(nil)
	description := "my bad alert description"
	scenarios := []struct {
		Name             string
		Provider         AlertProvider
		Alert            alert.Alert
		Resolved         bool
		MockRoundTripper test.MockRoundTripper
		ExpectedError    bool
	}{
		{
			Name:          "triggered",
			Provider:      AlertProvider{},
			Alert:         alert.Alert{Description: &description, SuccessThreshold: 1, FailureThreshold: 1},
			Resolved:      false,
			ExpectedError: false,
			MockRoundTripper: test.MockRoundTripper(func(r *http.Request) *http.Response {
				return &http.Response{StatusCode: http.StatusOK, Body: http.NoBody}
			}),
		},
		{
			Name:          "triggered-error",
			Provider:      AlertProvider{},
			Alert:         alert.Alert{Description: &description, SuccessThreshold: 5, FailureThreshold: 3},
			Resolved:      false,
			ExpectedError: true,
			MockRoundTripper: test.MockRoundTripper(func(r *http.Request) *http.Response {
				return &http.Response{StatusCode: http.StatusInternalServerError, Body: http.NoBody}
			}),
		},
		{
			Name:          "resolved",
			Provider:      AlertProvider{},
			Alert:         alert.Alert{Description: &description, SuccessThreshold: 5, FailureThreshold: 3},
			Resolved:      true,
			ExpectedError: false,
			MockRoundTripper: test.MockRoundTripper(func(r *http.Request) *http.Response {
				return &http.Response{StatusCode: http.StatusOK, Body: http.NoBody}
			}),
		},
		{
			Name:          "resolved-error",
			Provider:      AlertProvider{},
			Alert:         alert.Alert{Description: &description, SuccessThreshold: 5, FailureThreshold: 3},
			Resolved:      true,
			ExpectedError: true,
			MockRoundTripper: test.MockRoundTripper(func(r *http.Request) *http.Response {
				return &http.Response{StatusCode: http.StatusInternalServerError, Body: http.NoBody}
			}),
		},
	}

	for _, scenario := range scenarios {
		t.Run(scenario.Name, func(t *testing.T) {
			client.InjectHTTPClient(&http.Client{Transport: scenario.MockRoundTripper})
			err := scenario.Provider.Send(
				&core.Endpoint{Name: "endpoint-name"},
				&scenario.Alert,
				&core.Result{
					ConditionResults: []*core.ConditionResult{
						{Condition: "[CONNECTED] == true", Success: scenario.Resolved},
						{Condition: "[STATUS] == 200", Success: scenario.Resolved},
					},
				},
				scenario.Resolved,
			)
			if scenario.ExpectedError && err == nil {
				t.Error("expected error, got none")
			}
			if !scenario.ExpectedError && err != nil {
				t.Error("expected no error, got", err.Error())
			}
		})
	}
}

func TestAlertProvider_buildCreateRequestBody(t *testing.T) {
	t.Parallel()
	description := "alert description"
	scenarios := []struct {
		Name     string
		Provider *AlertProvider
		Alert    *alert.Alert
		Endpoint *core.Endpoint
		Result   *core.Result
		Resolved bool
		want     alertCreateRequest
	}{
		{
			Name:     "missing all params (unresolved)",
			Provider: &AlertProvider{},
			Alert:    &alert.Alert{},
			Endpoint: &core.Endpoint{},
			Result:   &core.Result{},
			Resolved: false,
			want: alertCreateRequest{
				Message:     " - ",
				Priority:    "P1",
				Source:      "gatus",
				Entity:      "gatus-",
				Alias:       "gatus-healthcheck-",
				Description: "An alert for ** has been triggered due to having failed 0 time(s) in a row\n",
				Tags:        nil,
				Details:     map[string]string{},
			},
		},
		{
			Name:     "missing all params (resolved)",
			Provider: &AlertProvider{},
			Alert:    &alert.Alert{},
			Endpoint: &core.Endpoint{},
			Result:   &core.Result{},
			Resolved: true,
			want: alertCreateRequest{
				Message:     "RESOLVED:  - ",
				Priority:    "P1",
				Source:      "gatus",
				Entity:      "gatus-",
				Alias:       "gatus-healthcheck-",
				Description: "An alert for ** has been resolved after passing successfully 0 time(s) in a row\n",
				Tags:        nil,
				Details:     map[string]string{},
			},
		},
		{
			Name:     "with default options (unresolved)",
			Provider: &AlertProvider{},
			Alert: &alert.Alert{
				Description:      &description,
				FailureThreshold: 3,
			},
			Endpoint: &core.Endpoint{
				Name: "my super app",
			},
			Result: &core.Result{
				ConditionResults: []*core.ConditionResult{
					{
						Condition: "[STATUS] == 200",
						Success:   true,
					},
					{
						Condition: "[BODY] == OK",
						Success:   false,
					},
				},
			},
			Resolved: false,
			want: alertCreateRequest{
				Message:     "my super app - " + description,
				Priority:    "P1",
				Source:      "gatus",
				Entity:      "gatus-my-super-app",
				Alias:       "gatus-healthcheck-my-super-app",
				Description: "An alert for *my super app* has been triggered due to having failed 3 time(s) in a row\n▣ - `[STATUS] == 200`\n▢ - `[BODY] == OK`\n",
				Tags:        nil,
				Details:     map[string]string{},
			},
		},
		{
			Name: "with custom options (resolved)",
			Provider: &AlertProvider{
				Priority:     "P5",
				EntityPrefix: "oompa-",
				AliasPrefix:  "loompa-",
				Source:       "gatus-hc",
				Tags:         []string{"do-ba-dee-doo"},
			},
			Alert: &alert.Alert{
				Description:      &description,
				SuccessThreshold: 4,
			},
			Endpoint: &core.Endpoint{
				Name: "my mega app",
			},
			Result: &core.Result{
				ConditionResults: []*core.ConditionResult{
					{
						Condition: "[STATUS] == 200",
						Success:   true,
					},
				},
			},
			Resolved: true,
			want: alertCreateRequest{
				Message:     "RESOLVED: my mega app - " + description,
				Priority:    "P5",
				Source:      "gatus-hc",
				Entity:      "oompa-my-mega-app",
				Alias:       "loompa-my-mega-app",
				Description: "An alert for *my mega app* has been resolved after passing successfully 4 time(s) in a row\n▣ - `[STATUS] == 200`\n",
				Tags:        []string{"do-ba-dee-doo"},
				Details:     map[string]string{},
			},
		},
		{
			Name: "with default options and details (unresolved)",
			Provider: &AlertProvider{
				Tags: []string{"foo"},
			},
			Alert: &alert.Alert{
				Description:      &description,
				FailureThreshold: 6,
			},
			Endpoint: &core.Endpoint{
				Name:  "my app",
				Group: "end game",
				URL:   "https://my.go/app",
			},
			Result: &core.Result{
				HTTPStatus: 400,
				Hostname:   "my.go",
				Errors:     []string{"error 01", "error 02"},
				Success:    false,
				ConditionResults: []*core.ConditionResult{
					{
						Condition: "[STATUS] == 200",
						Success:   false,
					},
				},
			},
			Resolved: false,
			want: alertCreateRequest{
				Message:     "[end game] my app - " + description,
				Priority:    "P1",
				Source:      "gatus",
				Entity:      "gatus-end-game-my-app",
				Alias:       "gatus-healthcheck-end-game-my-app",
				Description: "An alert for *end game/my app* has been triggered due to having failed 6 time(s) in a row\n▢ - `[STATUS] == 200`\n",
				Tags:        []string{"foo"},
				Details: map[string]string{
					"endpoint:url":       "https://my.go/app",
					"endpoint:group":     "end game",
					"result:hostname":    "my.go",
					"result:errors":      "error 01,error 02",
					"result:http_status": "400",
				},
			},
		},
	}
	for _, scenario := range scenarios {
		actual := scenario
		t.Run(actual.Name, func(t *testing.T) {
			if got := actual.Provider.buildCreateRequestBody(actual.Endpoint, actual.Alert, actual.Result, actual.Resolved); !reflect.DeepEqual(got, actual.want) {
				t.Errorf("buildCreateRequestBody() = %v, want %v", got, actual.want)
			}
		})
	}
}

func TestAlertProvider_buildCloseRequestBody(t *testing.T) {
	t.Parallel()
	description := "alert description"
	scenarios := []struct {
		Name     string
		Provider *AlertProvider
		Alert    *alert.Alert
		Endpoint *core.Endpoint
		want     alertCloseRequest
	}{
		{
			Name:     "Missing all values",
			Provider: &AlertProvider{},
			Alert:    &alert.Alert{},
			Endpoint: &core.Endpoint{},
			want: alertCloseRequest{
				Source: "",
				Note:   "RESOLVED:  - ",
			},
		},
		{
			Name:     "Basic values",
			Provider: &AlertProvider{},
			Alert: &alert.Alert{
				Description: &description,
			},
			Endpoint: &core.Endpoint{
				Name: "endpoint name",
			},
			want: alertCloseRequest{
				Source: "endpoint-name",
				Note:   "RESOLVED: endpoint name - alert description",
			},
		},
	}

	for _, scenario := range scenarios {
		actual := scenario
		t.Run(actual.Name, func(t *testing.T) {
			if got := actual.Provider.buildCloseRequestBody(actual.Endpoint, actual.Alert); !reflect.DeepEqual(got, actual.want) {
				t.Errorf("buildCloseRequestBody() = %v, want %v", got, actual.want)
			}
		})
	}
}
