package suite

import (
	"context"
	"fmt"

	"code.justin.tv/eventbus/controlplane/rpc"

	"code.justin.tv/eventbus/controlplane/e2e/internal/expected"

	"code.justin.tv/eventbus/controlplane/e2e/internal/e2eutil"
	"code.justin.tv/eventbus/controlplane/e2e/internal/httpserver"
	"code.justin.tv/eventbus/controlplane/e2e/internal/report"
)

var _ Runner = &EventPubsubInformation{}

// ClientsTestSuite ensures two services can be set up, one to publish to an
// event stream and another to subscribe to that event stream. The resulting
// services should be able to publish an event to that stream, and the subscriber
// should be able to use the client to receive the published event.
type EventPubsubInformation struct {
	*DefaultTestSuite // Use the default suite as ground work
}

func NewEventPubsubInformation(suiteName string) (Runner, error) {
	// Base setup, testing, and cleaning procedures
	defaultTestSuite, err := NewDefaultTestSuite(suiteName)
	if err != nil {
		return nil, err
	}
	// Wrap the basic tooling with some extra tests
	return &EventPubsubInformation{
		DefaultTestSuite: defaultTestSuite,
	}, nil
}

// Setup is nothing on top of the default setup phase
func (t *EventPubsubInformation) Setup(ctx context.Context) report.Error {
	ctx = e2eutil.AppendTestPath(ctx, t.TestName())
	return t.DefaultTestSuite.Setup(ctx)
}

// Test includes the default testing, plus extra tests to publish
// and receive an event using the client libraries
func (t *EventPubsubInformation) Test(ctx context.Context) {
	ctx = e2eutil.AppendTestPath(ctx, t.TestName())
	t.DefaultTestSuite.Test(ctx)

	for _, eventDefinition := range t.EventDefinitions {
		for _, env := range expected.Environments {
			stats, err := httpserver.EventStreamStats(e2eutil.JobID(ctx), eventDefinition.EventType, env)
			if err != nil {
				t.Error(ctx, fmt.Sprintf("could not fetch event stream stats for (%s,%s)", eventDefinition.EventType, env), err)
				return
			}

			for _, svc := range t.Services {
				t.Log(ctx, fmt.Sprintf("Checking for service '%s' in publisher stats for event stream (%s,%s)", svc.ServiceCatalogID, eventDefinition.EventType, env))
				if !e2eutil.StringInArray(publisherURLs(stats.Publishers), expected.ServiceCatalogURL(svc.ServiceCatalogID)) {
					t.Error(ctx, fmt.Sprintf("publisher service '%s' not in event stream stats", svc.ServiceCatalogID), nil)
					return
				}
			}
		}
	}
}

func (t *EventPubsubInformation) Clean(ctx context.Context) []report.Error {
	ctx = e2eutil.AppendTestPath(ctx, t.TestName())
	return t.DefaultTestSuite.Clean(ctx)
}

func (t *EventPubsubInformation) TestName() string {
	return "EventPubsubInformation"
}

func publisherURLs(publishers []*rpc.PublisherServiceInfo) []string {
	urls := make([]string, len(publishers))
	for i, pub := range publishers {
		urls[i] = pub.ServiceCatalogUrl
	}
	return urls
}
