package stat

import (
	"encoding/json"
	"errors"
	"time"

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

type timeBucket time.Time

// metricKey is a hashable object used to store metrics in a map
type metricKey struct {
	DimensionsHash    string
	MetricName        string
	TimeBucket        timeBucket
	StorageResolution time.Duration
}

// Metric represents a metric that is countable or statable (i.e. we can
// calculate the mean)
type Metric struct {
	// cloudwatch dimensions for the metric to record
	Dimensions []*cloudwatch.Dimension
	// name of the metric
	MetricName string
	// (optional) resolution of the metric. defaults to minute resolution.
	StorageResolution time.Duration

	timeBucket timeBucket
}

func (m Metric) toKey() (metricKey, error) {
	dimensions := make([]string, 0)
	for _, dimension := range m.Dimensions {
		dimensions = append(
			dimensions,
			aws.StringValue(dimension.Name),
			aws.StringValue(dimension.Value))
	}
	dimensionsHash, err := json.Marshal(dimensions)
	if err != nil {
		return metricKey{}, err
	}

	return metricKey{
		DimensionsHash:    string(dimensionsHash),
		MetricName:        m.MetricName,
		TimeBucket:        m.timeBucket,
		StorageResolution: m.StorageResolution,
	}, nil
}

func (m *Metric) fromKey(key metricKey) error {
	dimensions := make([]string, 0)
	if err := json.Unmarshal([]byte(key.DimensionsHash), &dimensions); err != nil {
		return err
	}

	if len(dimensions)%2 != 0 {
		return errors.New("invalid dimension key")
	}

	cwDimensions := make([]*cloudwatch.Dimension, 0)
	for n := 0; n < len(dimensions); n++ {
		cwDimensions = append(cwDimensions, &cloudwatch.Dimension{
			Name:  aws.String(dimensions[n]),
			Value: aws.String(dimensions[n+1]),
		})

		n++
	}

	m.MetricName = key.MetricName
	m.timeBucket = key.TimeBucket
	m.Dimensions = cwDimensions
	m.StorageResolution = key.StorageResolution
	return nil
}
