package historyin

import (
	"context"
	"encoding/json"
	"fmt"
	"sync"
	"time"

	"code.justin.tv/foundation/history.v2/internal/config"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/firehose"
	"github.com/aws/aws-sdk-go/service/firehose/firehoseiface"
)

// size when batch will be flushed to firehose
const flushBatchSize = 250

// batch age when batch will be flushed to firehose
const flushBatchAge = time.Minute

// Client adds history events
type Client struct {
	// Environment is the history stack to use. Defaults to production.
	Environment    string
	FlushBatchSize int
	Logger         Logger

	initSync           sync.Once
	deliveryStreamName string
	firehose           firehoseiface.FirehoseAPI
}

func (c *Client) init() (err error) {
	c.initSync.Do(func() {
		var cfg config.Config
		if cfg, err = config.Environment(c.Environment); err != nil {
			return
		}

		c.deliveryStreamName = cfg.DeliveryStreamName
		c.firehose = firehose.New(session.New(&aws.Config{
			Region: aws.String(cfg.AWSRegion),
			Credentials: stscreds.NewCredentials(
				session.New(&aws.Config{Region: aws.String(cfg.AWSRegion)}),
				cfg.RoleARN),
		}))

		if c.Logger == nil {
			c.Logger = nopLogger{}
		}

		if c.FlushBatchSize == 0 {
			c.FlushBatchSize = flushBatchSize
		}

		if c.FlushBatchSize > firehoseBatchMaxRecords {
			err = fmt.Errorf("FlushBatchSize must be less than %d", c.FlushBatchSize)
			return
		}
	})

	return
}

// Add submits a new audit to history service
func (c *Client) Add(ctx context.Context, audit *Audit) error {
	if err := c.init(); err != nil {
		return err
	}

	if err := audit.fillOptional(); err != nil {
		return err
	}

	data, err := json.Marshal(audit)
	if err != nil {
		return err
	}

	if _, err := c.firehose.PutRecordWithContext(ctx, &firehose.PutRecordInput{
		Record: &firehose.Record{
			Data: data,
		},
		DeliveryStreamName: aws.String(c.deliveryStreamName),
	}); err != nil {
		return err
	}

	return nil
}

// Batcher returns a new batcher
func (c *Client) Batcher() (Batcher, error) {
	if err := c.init(); err != nil {
		return nil, err
	}
	return &batchRunner{
		DeliveryStreamName: c.deliveryStreamName,
		Firehose:           c.firehose,
		Batch: batch{
			Threshold: flushBatchSize,
		},
		MaxBatchAge: flushBatchAge,
		Logger:      c.Logger,
	}, nil
}
