package notification

import (
	"context"
	"sync"

	"code.justin.tv/eventbus/controlplane/internal/logger"
	"code.justin.tv/eventbus/fultonlog"

	identifier "code.justin.tv/amzn/TwitchProcessIdentifier"
	cloudwatch "code.justin.tv/amzn/TwitchTelemetryCloudWatchMetricsSender"

	telemetry "code.justin.tv/amzn/TwitchTelemetry"
	"code.justin.tv/eventbus/controlplane/infrastructure/validation"
)

const (
	MetricNameValidationOk               = "ValidationOk"
	MetricNameValidationWarn             = "ValidationWarn"
	MetricNameValidationError            = "ValidationError"
	MetricNameValidationExecutionFailure = "ValidationExecutionError"
	MetricNameValidationStatusUnknown    = "ValidationStatusUnknown"
)

type Cloudwatch struct {
	observer telemetry.SampleObserver
	builder  *telemetry.SampleBuilder
	reports  []*validation.Report
	l        *logger.Logger
	mutex    sync.Mutex
}

func NewCloudwatch(id identifier.ProcessIdentifier, l *logger.Logger) *Cloudwatch {
	return &Cloudwatch{
		observer: cloudwatch.New(&id, fultonlog.FromZapLogger(l.Logger)),
		builder: &telemetry.SampleBuilder{
			ProcessIdentifier: id,
		},
		reports: make([]*validation.Report, 0),
		l:       l,
	}
}

func (c *Cloudwatch) Add(report *validation.Report) {
	c.mutex.Lock()
	c.reports = append(c.reports, report)
	c.mutex.Unlock()
}

func (c *Cloudwatch) Submit(context.Context) error {
	c.mutex.Lock()
	reports := c.reports
	c.reset()
	c.mutex.Unlock()

	for _, report := range reports {
		c.submitReport(report)
	}

	return nil
}

func (c *Cloudwatch) submitReport(report *validation.Report) {
	sampleName := metricName(report.Status)
	s, err := c.builder.Build(sampleName, 1, telemetry.UnitCount)
	if err != nil {
		c.l.Warn("unable to build sample for cloudwatch notification channel")
		return
	}

	s.MetricID.AddDimension("ResourceType", report.Item.Type())
	s.RollupDimensions = append(s.RollupDimensions, []string{"ResourceType"})

	c.observer.ObserveSample(s)
}

func (c *Cloudwatch) reset() {
	c.reports = nil
}

func metricName(status validation.Status) string {
	switch status {
	case validation.StatusOk:
		return MetricNameValidationOk
	case validation.StatusWarn:
		return MetricNameValidationWarn
	case validation.StatusError:
		return MetricNameValidationError
	case validation.StatusValidationError:
		return MetricNameValidationExecutionFailure
	default:
		return MetricNameValidationStatusUnknown //should never happen
	}
}
