package maintnotifications

import (
	"context"
	"net"
	"testing"
	"time"

	"github.com/redis/go-redis/v9/internal/interfaces"
	"github.com/redis/go-redis/v9/push"
)

// testOptions implements interfaces.OptionsInterface for testing SMIGRATED
type testOptions struct {
	nodeAddress string
}

func (to *testOptions) GetReadTimeout() time.Duration  { return 5 * time.Second }
func (to *testOptions) GetWriteTimeout() time.Duration { return 5 * time.Second }
func (to *testOptions) GetNetwork() string             { return "tcp" }
func (to *testOptions) GetAddr() string                { return "localhost:6379" }
func (to *testOptions) GetNodeAddress() string         { return to.nodeAddress }
func (to *testOptions) IsTLSEnabled() bool             { return false }
func (to *testOptions) GetProtocol() int               { return 3 }
func (to *testOptions) GetPoolSize() int               { return 10 }
func (to *testOptions) NewDialer() func(context.Context) (net.Conn, error) {
	return func(ctx context.Context) (net.Conn, error) { return nil, nil }
}

func createTestManager(nodeAddress string) *Manager {
	config := DefaultConfig()
	var opts interfaces.OptionsInterface
	if nodeAddress != "" {
		opts = &testOptions{nodeAddress: nodeAddress}
	}
	return &Manager{
		config:  config,
		options: opts,
	}
}

// TestHandleSMigrated_CorrectFormat tests that handleSMigrated correctly parses the nested array format
func TestHandleSMigrated_CorrectFormat(t *testing.T) {
	// Create a manager with matching original endpoint
	manager := createTestManager("127.0.0.1:6379")

	handler := &NotificationHandler{
		manager: manager,
	}

	// Create notification in the correct nested array format:
	// ["SMIGRATED", SeqID, [[source, target, slots], [source, target, slots], ...]]
	notification := []interface{}{
		"SMIGRATED",
		int64(12346),
		[]interface{}{
			[]interface{}{"127.0.0.1:6379", "127.0.0.1:6380", "123,456,789-1000"},
			[]interface{}{"127.0.0.1:6379", "127.0.0.1:6381", "124,457,300-500"},
		},
	}

	ctx := context.Background()
	handlerCtx := push.NotificationHandlerContext{
		Conn: nil, // No connection needed for this test
	}

	// This should not return an error
	err := handler.handleSMigrated(ctx, handlerCtx, notification)
	if err != nil {
		t.Errorf("handleSMigrated failed with correct format: %v", err)
	}
}

// TestHandleSMigrated_SingleTriplet tests parsing with a single triplet
func TestHandleSMigrated_SingleTriplet(t *testing.T) {
	manager := createTestManager("127.0.0.1:6379")

	handler := &NotificationHandler{
		manager: manager,
	}

	// Single triplet in nested array format
	notification := []interface{}{
		"SMIGRATED",
		int64(100),
		[]interface{}{
			[]interface{}{"127.0.0.1:6379", "127.0.0.1:6380", "1000,2000-3000"},
		},
	}

	ctx := context.Background()
	handlerCtx := push.NotificationHandlerContext{
		Conn: nil,
	}

	err := handler.handleSMigrated(ctx, handlerCtx, notification)
	if err != nil {
		t.Errorf("handleSMigrated failed with single triplet: %v", err)
	}
}

// TestHandleSMigrated_NoMatchingSource tests that notification is ignored when source doesn't match
func TestHandleSMigrated_NoMatchingSource(t *testing.T) {
	// Create a manager with a different original endpoint
	manager := createTestManager("127.0.0.1:9999")

	handler := &NotificationHandler{
		manager: manager,
	}

	// Notification with source that doesn't match our endpoint
	notification := []interface{}{
		"SMIGRATED",
		int64(200),
		[]interface{}{
			[]interface{}{"127.0.0.1:6379", "127.0.0.1:6380", "1000,2000-3000"},
		},
	}

	ctx := context.Background()
	handlerCtx := push.NotificationHandlerContext{
		Conn: nil,
	}

	// Should not return an error, just silently ignore
	err := handler.handleSMigrated(ctx, handlerCtx, notification)
	if err != nil {
		t.Errorf("handleSMigrated should not error when source doesn't match: %v", err)
	}
}

// TestHandleSMigrated_IncompleteTriplet tests that incomplete triplets are skipped
func TestHandleSMigrated_IncompleteTriplet(t *testing.T) {
	manager := createTestManager("127.0.0.1:6379")

	handler := &NotificationHandler{
		manager: manager,
	}

	// Triplet with only 2 elements (missing slots) - should be skipped
	notification := []interface{}{
		"SMIGRATED",
		int64(300),
		[]interface{}{
			[]interface{}{"127.0.0.1:6379", "127.0.0.1:6380"}, // Missing slots
		},
	}

	ctx := context.Background()
	handlerCtx := push.NotificationHandlerContext{
		Conn: nil,
	}

	// Should not error, just skip the malformed triplet
	err := handler.handleSMigrated(ctx, handlerCtx, notification)
	if err != nil {
		t.Errorf("handleSMigrated should not error with incomplete triplet (just skip it): %v", err)
	}
}

// TestHandleSMigrated_TooFewElements tests that notifications with too few elements are rejected
func TestHandleSMigrated_TooFewElements(t *testing.T) {
	manager := createTestManager("127.0.0.1:6379")

	handler := &NotificationHandler{
		manager: manager,
	}

	// Only SMIGRATED and SeqID, no triplets array
	notification := []interface{}{
		"SMIGRATED",
		int64(400),
	}

	ctx := context.Background()
	handlerCtx := push.NotificationHandlerContext{
		Conn: nil,
	}

	err := handler.handleSMigrated(ctx, handlerCtx, notification)
	if err == nil {
		t.Error("handleSMigrated should error with too few elements")
	}
}

// TestHandleSMigrated_EmptyTripletsArray tests that empty triplets array is rejected
func TestHandleSMigrated_EmptyTripletsArray(t *testing.T) {
	manager := createTestManager("127.0.0.1:6379")

	handler := &NotificationHandler{
		manager: manager,
	}

	// Empty triplets array
	notification := []interface{}{
		"SMIGRATED",
		int64(500),
		[]interface{}{},
	}

	ctx := context.Background()
	handlerCtx := push.NotificationHandlerContext{
		Conn: nil,
	}

	err := handler.handleSMigrated(ctx, handlerCtx, notification)
	if err == nil {
		t.Error("handleSMigrated should error with empty triplets array")
	}
}

// TestHandleSMigrated_InvalidTripletsType tests that non-array triplets is rejected
func TestHandleSMigrated_InvalidTripletsType(t *testing.T) {
	manager := createTestManager("127.0.0.1:6379")

	handler := &NotificationHandler{
		manager: manager,
	}

	// Triplets is a string instead of array
	notification := []interface{}{
		"SMIGRATED",
		int64(600),
		"not an array",
	}

	ctx := context.Background()
	handlerCtx := push.NotificationHandlerContext{
		Conn: nil,
	}

	err := handler.handleSMigrated(ctx, handlerCtx, notification)
	if err == nil {
		t.Error("handleSMigrated should error with invalid triplets type")
	}
}
