package main

import (
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/cactus/go-statsd-client/statsd"

	"code.justin.tv/common/config"
	"code.justin.tv/release/trace/api"
)

var (
	statsdSampleRate float32 = 0.1
)

type StatsWriter interface {
	AddTransaction(tx *api.Transaction)
	AddQuery(call *api.Call)
}

/// NewStatsWriter creates a new statsWriter object and returns it
func NewStatsWriter() StatsWriter {
	sw := new(statsWriter)

	sw.statter = config.Statsd()
	sw.statter.SetPrefix(buildPrefix())

	return sw
}

type statsWriter struct {
	statter statsd.Statter
}

/// Add a transaction to the stats - usually this means incrementing
/// the tx count and measuring the lag time
func (sw *statsWriter) AddTransaction(tx *api.Transaction) {
	root := tx.GetRoot()

	if root == nil {
		return
	}

	sw.incTransactions()
	sw.timeTransactionLag(time.Since(api.ServiceEnd(tx.GetRoot())))
}

/// Add a query to the stats - usually this means incrementing the query
/// count and measuring the response time
func (sw *statsWriter) AddQuery(call *api.Call) {
	dbName := call.GetParams().GetSql().Dbname
	dbHost := metricify(call.RequestSentTo)

	LogDatabaseWrite(DatabaseWrite{
		databaseName: dbName,
		databaseHost: dbHost,
	})
	sw.incQueries(dbName, dbHost)
	sw.timeQuery(api.ClientDuration(call), dbName, dbHost)
}

func (sw *statsWriter) timeTransactionLag(lag time.Duration) {
	err := sw.statter.TimingDuration("transactions.lag", lag, statsdSampleRate)

	if err != nil {
		log.Println("Error writing transaction lag: ", err)
	}
}

func (sw *statsWriter) incTransactions() {
	err := sw.statter.Inc("transactions", 1, statsdSampleRate)

	if err != nil {
		log.Println("Error writing transaction count: ", err)
	}
}

func (sw *statsWriter) incQueries(dbName string, dbHost string) {
	metric := fmt.Sprintf("%s.%s.queries", dbName, dbHost)
	err := sw.statter.Inc(metric, 1, statsdSampleRate)

	if err != nil {
		log.Println("Error writing query count: ", err)
	}
}

func (sw *statsWriter) timeQuery(queryDuration time.Duration, dbName string, dbHost string) {
	metric := fmt.Sprintf("%s.%s.queries", dbName, dbHost)
	err := sw.statter.TimingDuration(metric, queryDuration, statsdSampleRate)

	if err != nil {
		log.Println("Error writing query timing: ", err)
	}
}

func buildPrefix() string {
	return fmt.Sprintf("alucard.%s.%s", config.Environment(), repoName())
}

func metricify(host string) string {
	replacer := strings.NewReplacer(".", "-", "/", "-", ":", "-")
	return replacer.Replace(host)
}

func repoName() string {
	serviceName := config.MustResolve("Service")

	if strings.HasPrefix(serviceName, "code.justin.tv/") {
		serviceName = serviceName[len("code.justin.tv/"):]
	}
	badCharReplace := strings.NewReplacer(".", "-", "/", "-")
	return badCharReplace.Replace(serviceName)
}
