package validation

import (
	"context"

	"code.justin.tv/eventbus/controlplane/internal/logger"
	"go.uber.org/zap"

	"code.justin.tv/eventbus/controlplane/internal/environment"

	"github.com/aws/aws-sdk-go/aws/client"
	"github.com/pkg/errors"

	"code.justin.tv/eventbus/controlplane/internal/db"
)

const ErrSNSSubscriptionNotFound = "sns subscription not found"

type Subscription struct {
	*db.Subscription
	SNSManager SNSManager

	TargetName  string
	EventType   string
	Environment string
	SNSTopicARN string
}

func (e *Subscription) ID() string {
	return itemID(e)
}

func (e *Subscription) Type() string {
	return "Subscription"
}

func (e *Subscription) Attributes() []*ItemAttribute {
	return []*ItemAttribute{
		{
			Key:   "EventType",
			Value: e.EventType,
		},
		{
			Key:   "Environment",
			Value: e.Environment,
		},
		{
			Key:   "TargetName",
			Value: e.TargetName,
		},
		{
			Key:   "ARN",
			Value: e.SNSSubscriptionARN,
		},
	}
}

func (e *Subscription) Validate(ctx context.Context) (*Report, error) {
	// decide on a severity based on the affected infrastructure
	reportWithSeverity := func(item Item, msg string) (*Report, error) {
		if e.Environment == environment.Production {
			return Error(item, msg), nil
		}
		return Warn(item, msg), nil
	}

	found, err := e.SNSManager.SubscriptionExists(ctx, e.SNSTopicARN, e.SNSSubscriptionARN)
	if err != nil {
		return nil, errors.Wrap(err, "could not list subscriptions for topic")
	} else if !found {
		return reportWithSeverity(e, "could not find subscription for event stream sns topic")
	}

	return Ok(e), nil
}

func Subscriptions(ctx context.Context, sess client.ConfigProvider, db db.DB, snsManager SNSManager) ([]Item, error) {
	subscriptions, err := db.Subscriptions(ctx)
	if err != nil {
		return nil, errors.Wrap(err, "could not get subscriptions")
	}

	eventStreamMap, err := getEventStreamMap(ctx, db)
	if err != nil {
		return nil, errors.Wrap(err, "could not get event streams")
	}

	subscriptionTargetMap, err := getSubscriptionTargetMap(ctx, db)
	if err != nil {
		return nil, errors.Wrap(err, "could not get subscription targets")
	}

	items := make([]Item, 0)
	for _, subscription := range subscriptions {
		eventStream, found := eventStreamMap.Get(subscription.EventStreamID)
		if !found {
			logger.FromContext(ctx).Warn("could not find event stream for subscription", zap.Object("subscription", subscription))
			continue
		}

		subscriptionTarget, found := subscriptionTargetMap.Get(subscription.SubscriptionTargetID)
		if !found {
			logger.FromContext(ctx).Warn("could not find subscription target for subscription", zap.Object("subscription", subscription))
			continue
		}

		items = append(items, &Subscription{
			Subscription: subscription,
			TargetName:   subscriptionTarget.Name,
			EventType:    eventStream.EventType.Name,
			Environment:  eventStream.Environment,
			SNSTopicARN:  eventStream.SNSDetails.SNSTopicARN,
			SNSManager:   snsManager,
		})
	}

	return items, nil
}
