// package snsclient is the "tooling" component of the publisher logic.
//
// The containing package is all product level code that uses this low level
// implementation.
//
// This package allows developers to utilize the schema and client automation
// of the EventBus, but using their own private infrastructure and managed
// schema definitions.
package snsclient

import (
	"context"
	"math/rand"
	"strconv"

	eventbus "code.justin.tv/eventbus/client"
	"code.justin.tv/eventbus/client/internal/wire"
	"code.justin.tv/eventbus/client/lowlevel/snsmarshal"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/sns"
	"github.com/aws/aws-sdk-go/service/sns/snsiface"
)

// SNSClient is a low-level publisher to implement your own eventbus-formatted messages.
//
// NOTE that while this uses the same underlying format as the event bus, this is missing
// the following features that are present in the main publisher.Publisher, including:
//
// * Does not assume role. It is up to you to provide an implementation with the right creds.
// * No automatic topic lookup/routing. User must determine how to map events to topics.
// * Does not publish no-ops at startup for liveness verification.
type SNSClient struct {
	client snsiface.SNSAPI
}

func New(client snsiface.SNSAPI) *SNSClient {
	return &SNSClient{
		client: client,
	}
}

// Publish a message to the given SNS topic ARN.
//
// This will encode a message in the same format as the Event Bus and send it to SNS.
// If encoding fails, then Publish will return the encoding error and not the SNS error.
func (c *SNSClient) Publish(ctx context.Context, topicARN string, event eventbus.Message) error {
	return c.PublishWithEnvironment(ctx, topicARN, event, "")
}

func (c *SNSClient) PublishWithEnvironment(ctx context.Context, topicARN string, event eventbus.Message, env string) error {
	buf, err := wire.DefaultsEncode(event, env)
	if err != nil {
		return err
	}

	attributes := map[string]*sns.MessageAttributeValue{
		snsmarshal.AttrEventType: {
			DataType:    snsmarshal.DataTypeString,
			StringValue: aws.String(event.EventBusName()),
		},
		snsmarshal.AttrRandE6: {
			DataType:    snsmarshal.DataTypeNumber,
			StringValue: aws.String(strconv.Itoa(rand.Intn(1e6))),
		},
	}

	encoded := snsmarshal.EncodeToString(buf)
	_, err = c.client.PublishWithContext(ctx, &sns.PublishInput{
		Message:           aws.String(encoded),
		MessageAttributes: attributes,
		TopicArn:          aws.String(topicARN),
	})
	return err
}
