package suite

import (
	"context"

	"code.justin.tv/eventbus/controlplane/e2e/internal/expected"
	"code.justin.tv/eventbus/controlplane/internal/arn"
	"code.justin.tv/eventbus/controlplane/internal/clients/sns"
	"code.justin.tv/eventbus/controlplane/internal/clients/sts"
	"code.justin.tv/eventbus/controlplane/internal/e2eaccounts"

	"code.justin.tv/eventbus/controlplane/e2e/internal/e2eutil"
	"code.justin.tv/eventbus/controlplane/e2e/internal/resource"
	"code.justin.tv/eventbus/controlplane/infrastructure/validation"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/endpoints"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/pkg/errors"
)

// Unsubscribing an SQS queue from an SNS topic prevents resubscribing to that topic for 72
// hours, unless the unsubscribe is done from the subscribing account. This test verifies that
// we don't regress the ability to unsubscribe and resubscribe.

var _ Runner = &PublicationValidationTestSuite{}

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

func NewPublicationValidationTestSuite(suiteName string) (Runner, error) {
	defaultTest, err := NewDefaultTestSuite(suiteName)
	if err != nil {
		return nil, err
	}
	return &PublicationValidationTestSuite{
		DefaultTestSuite: defaultTest,
	}, nil
}

func (s *PublicationValidationTestSuite) Test(ctx context.Context) {
	err := s.checkOk(ctx)
	if err != nil {
		s.Error(ctx, "Expected no validation errors for default publication handling", err)
	}

	err = s.checkMissingIAMRolePermission(ctx, s.Publications[1])
	if err != nil {
		s.Error(ctx, "Expected validation error when handling misconfigured validation", err)
	}
}

func (s *PublicationValidationTestSuite) checkOk(ctx context.Context) error {
	reports, err := e2eutil.Validate(ctx)
	if err != nil {
		return errors.Wrap(err, "validation routine failed")
	}

	if validation.NotOk(reports) != nil {
		return errors.Wrap(err, "expected no errors for basic publication validation")
	}
	return nil
}

func (s *PublicationValidationTestSuite) checkMissingIAMRolePermission(ctx context.Context, pub *resource.Publication) error {
	// Manually remove the account ID before validation
	account, err := arn.AccountID(pub.IAMRole)
	if err != nil {
		return errors.Wrap(err, "could not parse account id from iam role arn")
	}

	err = s.removePublishPermission(ctx, account, expected.SNSTopicARN(e2eutil.JobID(ctx), pub.EventType, pub.Environment))
	if err != nil {
		return errors.Wrap(err, "could not remove publication permission for test")
	}

	reports, err := e2eutil.Validate(ctx)
	if err != nil {
		return errors.Wrap(err, "validation routine failed")
	}

	if !e2eutil.ErrorsFor(account, reports) {
		return errors.New("expected validation error for publication")
	}
	return nil
}

func (s *PublicationValidationTestSuite) removePublishPermission(ctx context.Context, awsAccountID string, topicARN string) error {
	sess := session.Must(
		session.NewSession(
			e2eaccounts.AccountCredentials().MainConfig(),
			aws.NewConfig().WithSTSRegionalEndpoint(endpoints.RegionalSTSEndpoint),
		),
	)
	stsManager := sts.NewManager(sess, "e2e-test")
	snsManager := sns.NewManager(sess, stsManager, expected.EncryptionAtRestKeyARN, nil)
	return snsManager.DisallowAccountPublish(ctx, topicARN, awsAccountID)
}

func (s *PublicationValidationTestSuite) TestName() string {
	return "PublicationValidationTestSuite"
}
