package validation

import (
	"context"

	"code.justin.tv/eventbus/controlplane/internal/clients/servicecatalog"
	"code.justin.tv/eventbus/controlplane/internal/db"
	"github.com/aws/aws-sdk-go/aws/client"
	"github.com/aws/aws-sdk-go/service/kms"
	"github.com/pkg/errors"
)

type Item interface {
	ID() string // uniquely identifies this piece of infra/policy
	Validate(context.Context) (*Report, error)
	Attributes() []*ItemAttribute
	Type() string
}

type ItemAttribute struct {
	Key   string `json:"key"`
	Value string `json:"value"`
}

type GrantFetcher interface {
	AllGrants(context.Context) ([]*kms.GrantListEntry, error)
	FindByGrantID(context.Context, string) (*kms.GrantListEntry, error)
}

type SQSManager interface {
	GetQueueAttributes(ctx context.Context, url string) (map[string]string, error)
	GetQueueURL(ctx context.Context, queueName, awsAccountID string) (string, error)
}

type SNSManager interface {
	SubscriptionExists(ctx context.Context, topicARN, subscriptionARN string) (bool, error)
}

type Config struct {
	DB                     db.DB
	ServiceCatalogClient   servicecatalog.Client
	GrantFetcher           GrantFetcher
	SQSManager             SQSManager
	SNSManager             SNSManager
	EncryptionAtRestKeyARN string
	EventBusAWSAccountID   string
}

func CollectItems(ctx context.Context, sess client.ConfigProvider, cfg Config) ([]Item, error) {
	targets, err := SubscriptionTargets(ctx, cfg.DB, cfg.SQSManager, cfg.EventBusAWSAccountID, cfg.EncryptionAtRestKeyARN)
	if err != nil {
		return nil, errors.Wrap(err, "could not fetch subscription targets")
	}
	eventStreams, err := EventStreams(ctx, sess, cfg.DB)
	if err != nil {
		return nil, errors.Wrap(err, "could not fetch event streams")
	}
	subscriptions, err := Subscriptions(ctx, sess, cfg.DB, cfg.SNSManager)
	if err != nil {
		return nil, errors.Wrap(err, "could not fetch subscriptions")
	}
	authorizedFieldGrants, err := AuthorizedFieldGrants(ctx, cfg.DB, cfg.GrantFetcher)
	if err != nil {
		return nil, errors.Wrap(err, "could not fetch authorized field grants")
	}
	publications, err := Publications(ctx, sess, cfg.DB)
	if err != nil {
		return nil, errors.Wrap(err, "could not fetch publications")
	}
	services, err := Services(ctx, sess, cfg.DB, cfg.ServiceCatalogClient)
	if err != nil {
		return nil, errors.Wrap(err, "could not fetch services")
	}

	items := make([]Item, 0)
	items = append(items, targets...)
	items = append(items, eventStreams...)
	items = append(items, subscriptions...)
	items = append(items, authorizedFieldGrants...)
	items = append(items, publications...)
	items = append(items, services...)
	// TODO: Re-enable this validator when IAM role management issues are better addressed: https://jira.twitch.com/browse/ASYNC-1216
	// items = append(items, iamRoles...)

	return items, nil
}
