package bufferedstats

import (
	"time"

	"code.justin.tv/sse/malachai/pkg/config"
	"code.justin.tv/sse/malachai/pkg/internal/stats"
	"code.justin.tv/sse/malachai/pkg/internal/stats/bufferedstats/statsrunner"
	"code.justin.tv/sse/malachai/pkg/log"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/cloudwatch"
)

// Client writes statistics to cloudwatch
type Client struct {
	config Config
	logger log.S2SLogger
	runner statsrunner.RunnerAPI
}

// Config is the configuration for client
type Config struct {
	Environment    string
	ServiceRoleArn string

	AWSConfigBase *aws.Config

	flushRate time.Duration
}

func (cfg *Config) fillDefaults() {
	if cfg.flushRate == 0 {
		cfg.flushRate = time.Minute
	}
}

// New returns a new Client
func New(
	cfg Config,
	logger log.S2SLogger,
) (c *Client, err error) {
	cfg.fillDefaults()

	res, err := config.GetResources(cfg.Environment)
	if err != nil {
		return
	}

	sess, err := session.NewSession(config.AWSConfig(
		cfg.AWSConfigBase,
		res.Region,
		cfg.ServiceRoleArn,
		res.CloudWatchReporterRoleArn,
	))
	if err != nil {
		return
	}

	c = &Client{
		config: cfg,
		runner: &statsrunner.Runner{
			Config: statsrunner.Config{
				FlushRate: cfg.flushRate,
			},
			CloudWatch: cloudwatch.New(sess),
			Logger:     logger,
		},
		logger: logger,
	}
	return
}

// Run is the goroutine that is required for this client
func (c *Client) Run() {
	c.runner.Run()
}

// Stop stops Run
func (c *Client) Stop(timeout time.Duration) error {
	if timeout == 0 {
		timeout = time.Second
	}
	return c.runner.Stop(timeout)
}

// WithSuccessStatter wraps a function in a statistics call. if the function
// returns error, we will log an Error statistic. Else, we will record a Success
// statistic
func (c *Client) WithSuccessStatter(
	metricNamePrefix string,
	dimensions []*stats.Dimension,
	fn stats.Func,
) (err error) {
	metricNameSuffix := "Success"

	err = fn()
	if err != nil {
		metricNameSuffix = "Error"
	}

	if err := c.runner.IncrementMetric(&statsrunner.IncrementableMetric{
		IncrementableMetric: &stats.IncrementableMetric{
			MetricName: metricNamePrefix + metricNameSuffix,
			Dimensions: append(dimensions, &stats.Dimension{
				Name:  "Environment",
				Value: c.config.Environment,
			}),
		},
	}); err != nil {
		c.logger.Errorf("stats: failed to update metrics cache: %s", err.Error())
	}
	return
}
