package notification

import (
	"context"
	"testing"

	"code.justin.tv/eventbus/controlplane/infrastructure/validation"
	"code.justin.tv/eventbus/controlplane/internal/logger"
	"github.com/pkg/errors"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	identifier "code.justin.tv/amzn/TwitchProcessIdentifier"
	telemetry "code.justin.tv/amzn/TwitchTelemetry"
)

const (
	testService  = "test-service"
	testRegion   = "test-region"
	testStage    = "test-stage"
	testSubstage = "test-substage"

	testResourceType = "coolItemToValidate"
)

var builder = &telemetry.SampleBuilder{
	ProcessIdentifier: identifier.ProcessIdentifier{
		Service:  testService,
		Region:   testRegion,
		Stage:    testStage,
		Substage: testSubstage,
	},
}

type mockItem struct {
	resourceType string
}

func newItem(t string) *mockItem {
	return &mockItem{
		resourceType: t,
	}
}

func (m *mockItem) ID() string {
	return "id"
}
func (m *mockItem) Validate(context.Context) (*validation.Report, error) {
	return nil, errors.New("not implemented")
}
func (m *mockItem) Attributes() []*validation.ItemAttribute {
	return []*validation.ItemAttribute{}
}
func (m *mockItem) Type() string {
	return m.resourceType
}

type mockObserver struct {
	lastSeen *telemetry.Sample
}

func (m *mockObserver) ObserveSample(s *telemetry.Sample) {
	m.lastSeen = s
}

func (m *mockObserver) Flush() {}
func (m *mockObserver) Stop()  {}

func TestCloudwatch(t *testing.T) {
	ctx := context.Background()
	obs := &mockObserver{}
	cw := &Cloudwatch{
		builder:  builder,
		observer: obs,
		l:        logger.FromContext(ctx),
	}
	item := newItem(testResourceType)

	t.Run("Samples have the right name for OK", func(t *testing.T) {
		report := validation.Ok(item)
		cw.Add(report)
		err := cw.Submit(ctx)

		require.NoError(t, err)
		require.NotNil(t, obs.lastSeen)
		assert.Equal(t, MetricNameValidationOk, obs.lastSeen.MetricID.Name)
	})

	t.Run("Samples have the right name for WARN", func(t *testing.T) {
		report := validation.Warn(item, "warning message")
		cw.Add(report)
		err := cw.Submit(ctx)

		require.NoError(t, err)
		require.NotNil(t, obs.lastSeen)
		assert.Equal(t, MetricNameValidationWarn, obs.lastSeen.MetricID.Name)
	})

	t.Run("Samples have the right name for ERROR", func(t *testing.T) {
		report := validation.Error(item, "error message")
		cw.Add(report)
		err := cw.Submit(ctx)

		require.NoError(t, err)
		require.NotNil(t, obs.lastSeen)
		assert.Equal(t, MetricNameValidationError, obs.lastSeen.MetricID.Name)
	})
	t.Run("Samples have the right name for VALIDATION_ERROR", func(t *testing.T) {
		report := validation.ValidationError(item, "could not run validation")
		cw.Add(report)
		err := cw.Submit(ctx)

		require.NoError(t, err)
		require.NotNil(t, obs.lastSeen)
		assert.Equal(t, MetricNameValidationExecutionFailure, obs.lastSeen.MetricID.Name)
	})

	t.Run("Samples set the right ResourceType", func(t *testing.T) {
		report := validation.Ok(item)
		cw.Add(report)
		err := cw.Submit(ctx)

		require.NoError(t, err)
		require.NotNil(t, obs.lastSeen)
		assert.Equal(t, testResourceType, obs.lastSeen.MetricID.Dimensions["ResourceType"])
	})

	t.Run("Samples have a rollup on ResourceType", func(t *testing.T) {
		report := validation.Ok(item)
		cw.Add(report)
		err := cw.Submit(ctx)

		require.NoError(t, err)
		require.NotNil(t, obs.lastSeen)
		assert.Contains(t, obs.lastSeen.RollupDimensions, []string{"ResourceType"})
	})
}
