package matcher_test

import (
	"encoding/json"
	"log/slog"
	"testing"

	jirapi "github.com/tidbcloud/jiraport/pkg/api/jira"
	"github.com/tidbcloud/jiraport/pkg/matcher"
)

func TestProdTiDBCloudAssignOwnershipRoutes(t *testing.T) {
	m, err := matcher.NewMatcher(slog.Default(), "../../config/prod-tidbcloud/policy-route.yml")
	if err != nil {
		t.Fatal(err)
	}

	tests := []struct {
		name      string
		payload   string
		want      []string
		doNotWant []string
	}{
		{
			name:      "TCOC L3 assignment is owned by the new service",
			payload:   jiraEvent("TCOC", "P2", "status", "", "ESCALATE TO L3", "TiDB"),
			doNotWant: []string{"oncall-assign"},
		},
		{
			name:      "GTOC L3 assignment is owned by the new service",
			payload:   jiraEvent("GTOC", "P2", "status", "", "ESCALATE TO L3", "TiDB"),
			doNotWant: []string{"oncall-assign"},
		},
		{
			name:      "TCOC L2 assignment is owned by the new service",
			payload:   jiraEvent("TCOC", "P2", "priority", "Undefined", "P2", "TiDB"),
			doNotWant: []string{"l2-assign", "l2-assign-direct-l3"},
		},
		{
			name:      "GTOC L2 assignment is owned by the new service",
			payload:   jiraEvent("GTOC", "P2", "priority", "Undefined", "P2", "TiDB"),
			doNotWant: []string{"l2-assign", "l2-assign-direct-l3"},
		},
		{
			name:      "TCOC unsupported-component L3 fallback is owned by the new service",
			payload:   jiraEvent("TCOC", "P2", "priority", "Undefined", "P2", "Unsupported"),
			doNotWant: []string{"l2-assign-direct-l3"},
		},
		{
			name:      "GTOC unsupported-component L3 fallback is owned by the new service",
			payload:   jiraEvent("GTOC", "P2", "priority", "Undefined", "P2", "Unsupported"),
			doNotWant: []string{"l2-assign-direct-l3"},
		},
		{
			name:      "TCOC urgent L3 fallback is owned by the new service",
			payload:   jiraEvent("TCOC", "P0", "priority", "P2", "P0", "TiDB"),
			doNotWant: []string{"l2-assign-direct-l3"},
		},
		{
			name:      "GTOC urgent L3 fallback is owned by the new service",
			payload:   jiraEvent("GTOC", "P0", "priority", "P2", "P0", "TiDB"),
			doNotWant: []string{"l2-assign-direct-l3"},
		},
		{
			name:    "TCOC standard assignee notification remains in JiraPort",
			payload: jiraEvent("TCOC", "P2", "assignee", "", "engineer", "TiDB"),
			want:    []string{"oncall"},
		},
		{
			name:    "TCOC L2 assignee notification remains in JiraPort",
			payload: jiraEvent("TCOC", "P2", "L2 assignee", "", "engineer", "TiDB"),
			want:    []string{"l2-notify"},
		},
		{
			name:    "TCOC urgent escalation remains in JiraPort",
			payload: jiraEvent("TCOC", "P0", "assignee", "", "engineer", "TiDB"),
			want:    []string{"oncall-urgent"},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			var event jirapi.JiraWebhookEvent
			if err := json.Unmarshal([]byte(tt.payload), &event); err != nil {
				t.Fatal(err)
			}

			receivers, err := m.Match(map[string]interface{}{"JiraWebhookEvent": event})
			if err != nil {
				t.Fatal(err)
			}
			got, err := receivers.TryToStringSlice()
			if err != nil {
				t.Fatal(err)
			}

			for _, receiver := range tt.want {
				if !contains(got, receiver) {
					t.Errorf("receivers %v do not contain %q", got, receiver)
				}
			}
			for _, receiver := range tt.doNotWant {
				if contains(got, receiver) {
					t.Errorf("receivers %v unexpectedly contain %q", got, receiver)
				}
			}
		})
	}
}

func jiraEvent(project, priority, field, from, to, component string) string {
	payload := map[string]interface{}{
		"webhookEvent": "jira:issue_updated",
		"issue": map[string]interface{}{
			"fields": map[string]interface{}{
				"project":    map[string]string{"key": project},
				"priority":   map[string]string{"name": priority},
				"components": []map[string]string{{"name": component}},
				"status": map[string]interface{}{
					"statusCategory": map[string]string{"key": "new"},
				},
			},
		},
		"changelog": map[string]interface{}{
			"items": []map[string]string{{
				"field":      field,
				"fromString": from,
				"toString":   to,
			}},
		},
	}
	b, _ := json.Marshal(payload)
	return string(b)
}

func contains(values []string, target string) bool {
	for _, value := range values {
		if value == target {
			return true
		}
	}
	return false
}
