package main

import (
	"context"
	"strconv"

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

	"go.uber.org/zap"

	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/sns"
	"github.com/aws/aws-sdk-go/service/sqs"
	"github.com/pkg/errors"

	"code.justin.tv/eventbus/controlplane/internal/e2eaccounts"
	"code.justin.tv/eventbus/controlplane/internal/logger"
)

var (
	baseSession *session.Session
	log         *logger.Logger
	baseCtx     context.Context
)

func init() {
	baseCtx = context.Background()
	log = logger.FromContext(baseCtx)

	baseSession = session.Must(session.NewSession())

	log.Info("Generating all required account credentials")
	err := e2eaccounts.InitializeCredentials(baseSession)
	if err != nil {
		log.Fatal("Could not get credentials for all required E2E test AWS accounts", zap.Error(err))
	}
}

func main() {
	log.Info("Clearing all event bus e2e resources")

	err := e2eaccounts.InitializeCredentials(baseSession)
	if err != nil {
		log.Fatal("Could not initialize AWS credentials")
	}

	err = removeTopics()
	if err != nil {
		log.Error("Could not remove topics", zap.Error(err))
	}

	err = removeQueues()
	if err != nil {
		log.Error("Could not remove queues", zap.Error(err))
	}

	err = removeSubscriptions()
	if err != nil {
		log.Error("Could not remove subscriptions", zap.Error(err))
	}

	err = removeGrants()
	if err != nil {
		log.Error("Could not remove KMS grants", zap.Error(err))
	}
}

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

	topics := make([]*sns.Topic, 0)
	err := snsClient.ListTopicsPagesWithContext(baseCtx, &sns.ListTopicsInput{}, func(topicsPage *sns.ListTopicsOutput, lastPage bool) bool {
		topics = append(topics, topicsPage.Topics...)
		return !lastPage
	})
	if err != nil {
		return err
	}
	log.Info("Removing " + strconv.Itoa(len(topics)) + " topics")
	for _, t := range topics {
		_, err := snsClient.DeleteTopicWithContext(baseCtx, &sns.DeleteTopicInput{
			TopicArn: t.TopicArn,
		})
		if err != nil {
			log.Error("Could not remove topic", zap.Error(err), zap.String("topicArn", *t.TopicArn))
		}
	}
	log.Info("Topics removed")
	return nil
}

func removeQueues() error {
	sess := session.Must(session.NewSession())
	sqsClient := sqs.New(sess, e2eaccounts.AccountCredentials().SubscriberConfig())

	// ListQueues returns up to 1k queues and does not support pagination.
	queues, err := sqsClient.ListQueuesWithContext(baseCtx, &sqs.ListQueuesInput{})
	if err != nil {
		return err
	}

	log.Info("Removing " + strconv.Itoa(len(queues.QueueUrls)) + " queues")
	for _, u := range queues.QueueUrls {
		_, err := sqsClient.DeleteQueueWithContext(baseCtx, &sqs.DeleteQueueInput{
			QueueUrl: u,
		})
		if err != nil {
			log.Error("Could not delete queue", zap.Error(err))
		}
	}

	return nil
}

func removeSubscriptions() error {
	sess := session.Must(session.NewSession())
	snsClient := sns.New(sess, e2eaccounts.AccountCredentials().SubscriberConfig())

	subscriptions := make([]*sns.Subscription, 0)
	err := snsClient.ListSubscriptionsPagesWithContext(baseCtx, &sns.ListSubscriptionsInput{}, func(subsPage *sns.ListSubscriptionsOutput, lastPage bool) bool {
		subscriptions = append(subscriptions, subsPage.Subscriptions...)
		return !lastPage
	})
	if err != nil {
		return err
	}
	log.Info("Removing " + strconv.Itoa(len(subscriptions)) + " subscriptions")

	for _, s := range subscriptions {
		_, err = snsClient.UnsubscribeWithContext(baseCtx, &sns.UnsubscribeInput{
			SubscriptionArn: s.SubscriptionArn,
		})
		if err != nil {
			log.Error("Could not unsubscribe", zap.Error(err))
		}
	}

	return nil
}

func removeGrants() error {
	grants, err := e2eutil.KMSGrants(expected.EncryptionAtRestKeyARN)
	if err != nil {
		return errors.Wrap(err, "could not fetch all kms grants")
	}

	log.Info("Removing " + strconv.Itoa(len(grants)) + " kms grants")
	err = e2eutil.KMSRevokeGrants(expected.EncryptionAtRestKeyARN, grants)
	if err != nil {
		log.Error("Could not revoke grant", zap.Error(err))
	}

	grants, err = e2eutil.KMSGrants(expected.AuthorizedFieldKeyARN)
	if err != nil {
		return errors.Wrap(err, "could not fetch all kms grants")
	}

	log.Info("Removing " + strconv.Itoa(len(grants)) + " kms grants")
	err = e2eutil.KMSRevokeGrants(expected.AuthorizedFieldKeyARN, grants)
	if err != nil {
		log.Error("Could not revoke grant", zap.Error(err))
	}

	return nil
}
