package automation

import (
	"sync"
	"testing"

	"github.com/nexuscrm/shared/pkg/models"
	"github.com/stretchr/testify/assert"
)

// stubHandler is a minimal ActionHandler for testing the registry.
type stubHandler struct {
	actionType string
}

func (h *stubHandler) Execute(_ *models.ActionMetadata, _ *ActionContext) error { return nil }
func (h *stubHandler) Type() string                                             { return h.actionType }
func (h *stubHandler) Validate(_ *models.ActionMetadata) error                  { return nil }

func TestActionHandlerRegistry_RegisterAndGet(t *testing.T) {
	tests := []struct {
		name      string
		register  []string
		lookup    string
		expectNil bool
	}{
		{
			name:      "get registered handler",
			register:  []string{"email"},
			lookup:    "email",
			expectNil: false,
		},
		{
			name:      "get unregistered handler returns nil",
			register:  []string{"email"},
			lookup:    "sms",
			expectNil: true,
		},
		{
			name:      "empty registry returns nil",
			register:  nil,
			lookup:    "email",
			expectNil: true,
		},
		{
			name:      "overwrite existing handler",
			register:  []string{"email", "email"},
			lookup:    "email",
			expectNil: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			reg := NewActionHandlerRegistry()
			for _, actionType := range tt.register {
				reg.Register(&stubHandler{actionType: actionType})
			}

			handler := reg.Get(tt.lookup)
			if tt.expectNil {
				assert.Nil(t, handler)
			} else {
				assert.NotNil(t, handler)
				assert.Equal(t, tt.lookup, handler.Type())
			}
		})
	}
}

func TestActionHandlerRegistry_Has(t *testing.T) {
	tests := []struct {
		name     string
		register []string
		check    string
		expected bool
	}{
		{"registered type", []string{"webhook"}, "webhook", true},
		{"unregistered type", []string{"webhook"}, "email", false},
		{"empty registry", nil, "anything", false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			reg := NewActionHandlerRegistry()
			for _, at := range tt.register {
				reg.Register(&stubHandler{actionType: at})
			}
			assert.Equal(t, tt.expected, reg.Has(tt.check))
		})
	}
}

func TestActionHandlerRegistry_Types(t *testing.T) {
	tests := []struct {
		name     string
		register []string
		expected int
	}{
		{"empty registry", nil, 0},
		{"single handler", []string{"email"}, 1},
		{"multiple handlers", []string{"email", "webhook", "sms"}, 3},
		{"duplicate registration counts once", []string{"email", "email"}, 1},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			reg := NewActionHandlerRegistry()
			for _, at := range tt.register {
				reg.Register(&stubHandler{actionType: at})
			}
			types := reg.Types()
			assert.Len(t, types, tt.expected)
		})
	}
}

func TestActionHandlerRegistry_ConcurrentAccess(t *testing.T) {
	reg := NewActionHandlerRegistry()
	var wg sync.WaitGroup

	// Concurrent writes
	for i := 0; i < 100; i++ {
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			reg.Register(&stubHandler{actionType: "type"})
		}(i)
	}

	// Concurrent reads
	for i := 0; i < 100; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			_ = reg.Get("type")
			_ = reg.Has("type")
			_ = reg.Types()
		}()
	}

	wg.Wait()
	// If we get here without a race condition, the test passes
	assert.True(t, reg.Has("type"))
}
