package adapters

import (
	"testing"

	"google.golang.org/protobuf/types/known/structpb"

	iop "iop/proto/gen/iop"
)

func TestDiffConfigSetsAddedRemovedUnchanged(t *testing.T) {
	currPayload := &iop.NodeConfigPayload{
		Adapters: []*iop.AdapterConfig{
			{Name: "mock1", Type: "mock", Enabled: true},
			{Name: "mock2", Type: "mock", Enabled: true},
		},
	}
	nextPayload := &iop.NodeConfigPayload{
		Adapters: []*iop.AdapterConfig{
			{Name: "mock2", Type: "mock", Enabled: true},
			{Name: "mock3", Type: "mock", Enabled: true},
		},
	}

	currSet, err := BuildConfigSet(currPayload, nil)
	if err != nil {
		t.Fatalf("BuildConfigSet curr: %v", err)
	}
	nextSet, err := BuildConfigSet(nextPayload, nil)
	if err != nil {
		t.Fatalf("BuildConfigSet next: %v", err)
	}

	diff := DiffConfigSets(currSet, nextSet)

	if len(diff.Added) != 1 || diff.Added[0] != "mock3" {
		t.Errorf("expected added [mock3], got %v", diff.Added)
	}
	if len(diff.Removed) != 1 || diff.Removed[0] != "mock1" {
		t.Errorf("expected removed [mock1], got %v", diff.Removed)
	}
	if len(diff.Unchanged) != 1 || diff.Unchanged[0] != "mock2" {
		t.Errorf("expected unchanged [mock2], got %v", diff.Unchanged)
	}
	if len(diff.Updated) != 0 {
		t.Errorf("expected updated empty, got %v", diff.Updated)
	}
}

func TestDiffConfigSetsOpenAICompatCapacityEndpoint(t *testing.T) {
	currPayload := &iop.NodeConfigPayload{
		Adapters: []*iop.AdapterConfig{
			{
				Name:    "oai",
				Type:    "openai_compat",
				Enabled: true,
				Config: &iop.AdapterConfig_OpenaiCompat{
					OpenaiCompat: &iop.OpenAICompatAdapterConfig{
						Provider: "openai",
						Endpoint: "http://endpoint-old",
						Capacity: 2,
					},
				},
			},
		},
	}

	// 1. Capacity 변경
	nextPayload1 := &iop.NodeConfigPayload{
		Adapters: []*iop.AdapterConfig{
			{
				Name:    "oai",
				Type:    "openai_compat",
				Enabled: true,
				Config: &iop.AdapterConfig_OpenaiCompat{
					OpenaiCompat: &iop.OpenAICompatAdapterConfig{
						Provider: "openai",
						Endpoint: "http://endpoint-old",
						Capacity: 5,
					},
				},
			},
		},
	}

	// 2. Endpoint 변경
	nextPayload2 := &iop.NodeConfigPayload{
		Adapters: []*iop.AdapterConfig{
			{
				Name:    "oai",
				Type:    "openai_compat",
				Enabled: true,
				Config: &iop.AdapterConfig_OpenaiCompat{
					OpenaiCompat: &iop.OpenAICompatAdapterConfig{
						Provider: "openai",
						Endpoint: "http://endpoint-new",
						Capacity: 2,
					},
				},
			},
		},
	}

	currSet, _ := BuildConfigSet(currPayload, nil)

	// Test case 1
	nextSet1, _ := BuildConfigSet(nextPayload1, nil)
	diff1 := DiffConfigSets(currSet, nextSet1)
	if len(diff1.Updated) != 1 || diff1.Updated[0] != "oai" {
		t.Errorf("expected capacity change to trigger updated, got updated=%v", diff1.Updated)
	}

	// Test case 2
	nextSet2, _ := BuildConfigSet(nextPayload2, nil)
	diff2 := DiffConfigSets(currSet, nextSet2)
	if len(diff2.Updated) != 1 || diff2.Updated[0] != "oai" {
		t.Errorf("expected endpoint change to trigger updated, got updated=%v", diff2.Updated)
	}
}

func TestBuildConfigSet_ProviderIDAsRegistryKey(t *testing.T) {
	payload := &iop.NodeConfigPayload{
		Adapters: []*iop.AdapterConfig{
			{
				Name:    "my-provider",
				Type:    "openai_compat",
				Enabled: true,
				Config: &iop.AdapterConfig_OpenaiCompat{
					OpenaiCompat: &iop.OpenAICompatAdapterConfig{
						Provider: "openai",
						Endpoint: "https://api.openai.com/v1",
						Capacity: 2,
					},
				},
			},
		},
	}

	cs, err := BuildConfigSet(payload, nil)
	if err != nil {
		t.Fatalf("BuildConfigSet: %v", err)
	}

	item, ok := cs.Items["my-provider"]
	if !ok {
		t.Fatal("expected registry item keyed by provider id 'my-provider'")
	}
	if item.Type != "openai_compat" {
		t.Errorf("expected type 'openai_compat', got %q", item.Type)
	}

	_, err = cs.Registry.Lookup("my-provider")
	if err != nil {
		t.Fatalf("expected to lookup adapter by provider id 'my-provider': %v", err)
	}
}

func TestDiffConfigSetsLegacySettings(t *testing.T) {
	currSt, _ := structpb.NewStruct(map[string]any{
		"provider": "openai",
		"endpoint": "http://legacy",
		"capacity": float64(2),
	})
	currPayload := &iop.NodeConfigPayload{
		Adapters: []*iop.AdapterConfig{
			{Name: "oai", Type: "openai_compat", Enabled: true, Settings: currSt},
		},
	}

	nextSt, _ := structpb.NewStruct(map[string]any{
		"provider": "openai",
		"endpoint": "http://legacy-changed",
		"capacity": float64(2),
	})
	nextPayload := &iop.NodeConfigPayload{
		Adapters: []*iop.AdapterConfig{
			{Name: "oai", Type: "openai_compat", Enabled: true, Settings: nextSt},
		},
	}

	currSet, _ := BuildConfigSet(currPayload, nil)
	nextSet, _ := BuildConfigSet(nextPayload, nil)
	diff := DiffConfigSets(currSet, nextSet)

	if len(diff.Updated) != 1 || diff.Updated[0] != "oai" {
		t.Errorf("expected legacy settings change to trigger updated, got updated=%v", diff.Updated)
	}
}
