package metrics

import (
	"fmt"
	"time"
)

// metric strings
const (
	metricCircuitDuration = "CircuitDuration"
	metricCircuitSuccess  = "CircuitSuccess"
	metricCircuitError    = "CircuitErr"
	metricCircuitOpen     = "CircuitOpened"
	metricCircuitClose    = "CircuitClosed"
)

func NewCircuitStatter(circuitName string, base Statter) *circuitStatter {
	return &circuitStatter{
		Statter:     base,
		timingName:  fmt.Sprintf("%s%s", circuitName, metricCircuitDuration),
		successName: fmt.Sprintf("%s%s", circuitName, metricCircuitSuccess),
		errorName:   fmt.Sprintf("%s%s", circuitName, metricCircuitError),
		openedName:  fmt.Sprintf("%s%s", circuitName, metricCircuitOpen),
		closedName:  fmt.Sprintf("%s%s", circuitName, metricCircuitClose),
	}
}

// circuitStatter implements the circuit.RunMetrics and circuit.FallbackMetrics interfaces
type circuitStatter struct {
	Statter
	// Keep track of all the metric names for this circuit.
	timingName  string
	successName string
	errorName   string
	openedName  string
	closedName  string
}

func (s *circuitStatter) timing(now time.Time, duration time.Duration) {
	if duration == 0 {
		return
	}
	s.TimingAt(s.timingName, duration, now)
}

func (s *circuitStatter) Success(now time.Time, duration time.Duration) {
	s.IncrementAt(s.successName, now)
	s.timing(now, duration)
}

func (s *circuitStatter) handleErr(now time.Time, duration time.Duration, reason string) {
	s.IncrementErrAt(s.errorName, reason, now)
	s.timing(now, duration)
}

func (s *circuitStatter) ErrFailure(now time.Time, duration time.Duration) {
	s.handleErr(now, duration, "Failure")
}

func (s *circuitStatter) ErrTimeout(now time.Time, duration time.Duration) {
	s.handleErr(now, duration, "Timeout")
}

func (s *circuitStatter) ErrBadRequest(now time.Time, duration time.Duration) {
	s.handleErr(now, duration, "BadRequest")
}

func (s *circuitStatter) ErrInterrupt(now time.Time, duration time.Duration) {
	s.handleErr(now, duration, "Interrupt")
}

func (s *circuitStatter) ErrConcurrencyLimitReject(now time.Time) {
	s.handleErr(now, 0, "ConcurrencyLimit")
}

func (s *circuitStatter) ErrShortCircuit(now time.Time) {
	s.handleErr(now, 0, "ShortCircuit")
}

func (s *circuitStatter) Opened(now time.Time) {
	s.IncrementAt(s.openedName, now)
}

func (s *circuitStatter) Closed(now time.Time) {
	s.IncrementAt(s.closedName, now)
}
