package suite

import (
	"context"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/sns"
	"github.com/golang/protobuf/ptypes/timestamp"

	"code.justin.tv/eventbus/client/publisher"
	"code.justin.tv/eventbus/schema/pkg/clock"

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

// Our initial group of publishers' SNS topics remain unencrypted. This test suite mirrors that scenario
// by creating unencrypted SNS topics, then creating encrypted queues which subscribe to these topics.

var _ Runner = &UnencryptedTestSuite{}

type UnencryptedTestSuite struct {
	*DefaultTestSuite // Use the default suite as ground work
}

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

func (u *UnencryptedTestSuite) Setup(ctx context.Context) report.Error {
	ctx = e2eutil.AppendTestPath(ctx, u.TestName())

	for _, service := range u.Services {
		if err := service.Setup(ctx); err != nil {
			return err
		}
	}
	for _, eventDefinition := range u.EventDefinitions {
		if err := eventDefinition.Setup(ctx); err != nil {
			return err
		}
	}
	for _, publication := range u.Publications {
		if err := publication.Setup(ctx); err != nil {
			return err
		}
	}

	// Create the initial SNS topics
	u.Log(ctx, "Running controlplane converger")
	err := e2eutil.Converge()
	if err != nil {
		return report.ErrorFromContext(ctx, "Could not run converger", err)
	}

	// Modify the SNS topics to be unencrypted
	for _, eventDefinition := range u.EventDefinitions {
		time.Sleep(2 * time.Second)

		sess := session.Must(session.NewSession())
		snsClient := sns.New(sess, e2eaccounts.AccountCredentials().MainConfig())

		expectedARN := expected.SNSTopicARN(e2eutil.JobID(ctx), eventDefinition.EventType, "production")
		u.Log(ctx, "setting topic to be unencrypted")
		_, err := snsClient.SetTopicAttributesWithContext(ctx, &sns.SetTopicAttributesInput{
			TopicArn:       aws.String(expectedARN),
			AttributeName:  aws.String("KmsMasterKeyId"),
			AttributeValue: aws.String(""),
		})
		if err != nil {
			return report.ErrorFromContext(ctx, "Could not disable KMS on topic", err)
		}
	}

	// Create encrypted queues which subscribe to the unencrypted topics
	for _, st := range u.SubscriptionTargets {
		if err := st.Setup(ctx); err != nil {
			return err
		}
	}
	for _, sub := range u.Subscriptions {
		if err := sub.Setup(ctx); err != nil {
			return err
		}
	}

	u.Log(ctx, "Running controlplane converger")
	err = e2eutil.Converge()
	if err != nil {
		return report.ErrorFromContext(ctx, "Could not run converger after disabling KMS for topic", err)
	}
	return nil
}

// Test includes the default testing, plus an extra test to publish
// an event to the stream
func (u *UnencryptedTestSuite) Test(ctx context.Context) {
	ctx = e2eutil.AppendTestPath(ctx, u.TestName())

	u.DefaultTestSuite.Test(ctx)

	p, err := e2eutil.PublisherWithJobID(e2eaccounts.AccountCredentials().PublisherConfig(), e2eutil.JobID(ctx), publisher.EnvProduction, []string{"ClockUpdate"})
	if err != nil {
		u.Error(ctx, "Could not create eventbus publisher client", err)
		return
	}
	u.Log(ctx, "Checking authorized publisher account can publish to event stream")
	err = p.Publish(context.Background(), &clock.ClockUpdate{
		Time: &timestamp.Timestamp{
			Seconds: time.Now().Unix(),
		},
	})
	if err != nil {
		u.Error(ctx, "Could not publish to event stream", err)
	}
}

func (u *UnencryptedTestSuite) TestName() string {
	return "UnencryptedTestSuite"
}
