package metric

//go:generate ./generate.sh

import (
	"context"
	"time"

	"github.com/golang/protobuf/ptypes/timestamp"
	"google.golang.org/grpc"

	pb "code.justin.tv/dta/rockpaperscissors/proto"
)

// BucketSize is how granular to report metrics.
type BucketSize pb.GetMetricRequest_BucketSize

const (
	// DAY will bucket the metric to be X/day.
	DAY BucketSize = BucketSize(pb.GetMetricRequest_DAY)
	// WEEK will bucket the metric to be X/week.
	WEEK BucketSize = BucketSize(pb.GetMetricRequest_WEEK)
	// MONTH will bucket the metric to be X/month.
	MONTH BucketSize = BucketSize(pb.GetMetricRequest_MONTH)
	// QUARTER will bucket the metric to be X/quarter.
	QUARTER BucketSize = BucketSize(pb.GetMetricRequest_QUARTER)
	// YEAR will bucket the metric to be X/year.
	YEAR BucketSize = BucketSize(pb.GetMetricRequest_YEAR)
)

// Client is a client for the MetricService gRPC service.
type Client struct {
	conn   *grpc.ClientConn
	client pb.MetricServiceClient
}

// NewClient is a constructor for the client. Please call Close() when done.
func NewClient(address string) (*Client, error) {
	conn, err := grpc.Dial(address, grpc.WithInsecure())
	if err != nil {
		return nil, err
	}

	return &Client{
		conn:   conn,
		client: pb.NewMetricServiceClient(conn),
	}, nil
}

// Close closes the client connection.
func (c *Client) Close() error {
	return c.conn.Close()
}

// ListMetrics returns a list of MetricInfo protobufs.
func (c *Client) ListMetrics() ([]*pb.MetricInfo, error) {
	response, err := c.client.ListMetrics(
		context.Background(),
		&pb.ListMetricsRequest{},
	)
	if err != nil {
		return nil, err
	}

	return response.GetMetrics(), nil
}

// GetMetricInfo simply returns the the given MetricInfo protobufs.
func (c *Client) GetMetricInfo(id string) (*pb.MetricInfo, error) {
	response, err := c.client.GetMetricInfo(
		context.Background(),
		&pb.GetMetricInfoRequest{
			MetricId: id,
		},
	)
	if err != nil {
		return nil, err
	}

	return response.Metric, nil
}

// SelectorOpt specifies one of ProjectID, Developer, or Team to get metrics for.
type SelectorOpt struct {
	ProjectID string
	Developer string
	Team      string
	Org       string
}

// GetMetric gets a timeseries for the specified metric_info.
func (c *Client) GetMetric(id string, start, end time.Time, bucketSize BucketSize, selector SelectorOpt) (*pb.GetMetricResponse, error) {
	req := &pb.GetMetricRequest{
		MetricId: id,
		Timerange: &pb.TimeRange{
			Start: &timestamp.Timestamp{
				Seconds: start.Unix(),
			},
			End: &timestamp.Timestamp{
				Seconds: end.Unix(),
			},
		},
		BucketSize: pb.GetMetricRequest_BucketSize(bucketSize),
	}

	if selector.ProjectID != "" {
		req.ProjectId = selector.ProjectID
	} else if selector.Developer != "" {
		req.Developer = selector.Developer
	} else if selector.Team != "" {
		req.Team = selector.Team
	} else if selector.Org != "" {
		req.Org = selector.Org
	}

	response, err := c.client.GetMetric(context.Background(), req)
	if err != nil {
		return nil, err
	}

	return response, nil
}
