package api

import (
	"io/ioutil"
	"os"
	"sync"

	log "github.com/Sirupsen/logrus"
	"github.com/aws/aws-sdk-go/aws/client"
	"github.com/pkg/errors"
	"google.golang.org/grpc"

	"code.justin.tv/common/config"

	"code.justin.tv/dta/rockpaperscissors/internal/api/contextlogger"
	"code.justin.tv/dta/rockpaperscissors/internal/api/event"
	"code.justin.tv/dta/rockpaperscissors/internal/api/metric"
	"code.justin.tv/dta/rockpaperscissors/internal/api/projectmetadata"
	"code.justin.tv/dta/rockpaperscissors/internal/elasticache"
	"code.justin.tv/dta/rockpaperscissors/internal/metrics"
	pb "code.justin.tv/dta/rockpaperscissors/proto"
)

var grpcLogger = log.New()
var initGrpcLog sync.Once

func init() {
	config.Register(map[string]string{
		"events-table-name":         "rockpaperscissors-development-events",
		"projects-table-name":       "rockpaperscissors-development-projects",
		"grpc-request-log":          "",
		"grpc-request-log-format":   "text",
		"memcached-config-endpoint": "",
	})
}

// NewAPIServer properly initializes the microservices.
// sess is an AWS Session used by the microservices to use the datastores.
func NewAPIServer(sess client.ConfigProvider) (*grpc.Server, error) {
	initGrpcLog.Do(func() {
		logfile := config.Resolve("grpc-request-log")
		if logfile == "" {
			grpcLogger.Out = ioutil.Discard
		} else if logfile != "-" {
			file, err := os.OpenFile(
				logfile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
			if err != nil {
				log.Fatal(errors.Wrapf(err, "Error opening logfile %q", logfile))
			}
			grpcLogger.Out = file

			if config.MustResolve("grpc-request-log-format") == "json" {
				grpcLogger.Formatter = &log.JSONFormatter{}
			}
		}
	})

	elasticacheConfigEndpoint := config.Resolve("memcached-config-endpoint")
	var cache elasticache.Memcache
	if elasticacheConfigEndpoint != "" {
		var err error
		cache, err = elasticache.New(elasticacheConfigEndpoint)
		if err != nil {
			return nil, err
		}
	}

	var eventDatastore event.Datastore = event.NewDynamoDBDatastore(
		sess, config.MustResolve("events-table-name"))
	if cache != nil {
		eventDatastore = event.NewCachingDatastore(eventDatastore, cache)
	}

	grpcServer := grpc.NewServer(
		grpc.StatsHandler(&contextlogger.GrpcStatsLogger{Logger: grpcLogger}))
	projectMetadataServer := projectmetadata.NewServer(
		sess, config.MustResolve("projects-table-name"))
	eventServer := event.NewServer(eventDatastore)
	metricsDispatcher, err := metrics.NewDispatcher(
		projectMetadataServer, eventServer)
	if err != nil {
		return nil, err
	}
	metricServer := metric.NewServer(metricsDispatcher)

	pb.RegisterProjectMetadataServiceServer(grpcServer, projectMetadataServer)
	pb.RegisterEventServiceServer(grpcServer, eventServer)
	pb.RegisterMetricServiceServer(grpcServer, metricServer)

	return grpcServer, nil
}
