package plan

import (
	"strings"
	"testing"

	"gopkg.in/yaml.v3"
)

func TestResponderSelectOneStrategyYAMLRoundTrip(t *testing.T) {
	cases := []struct {
		name string
		yaml string
		want SelectOneStrategy
	}{
		{
			name: "omitted strategy stays ordered",
			yaml: "selectOne: {value: ['agent-a', 'agent-b']}",
			want: SelectOneStrategyOrdered,
		},
		{
			name: "explicit ordered",
			yaml: "selectOne: {value: ['agent-a', 'agent-b']}\nstrategy: ordered",
			want: SelectOneStrategyOrdered,
		},
		{
			name: "random",
			yaml: "selectOne: {value: ['agent-a', 'agent-b']}\nstrategy: random",
			want: SelectOneStrategyRandom,
		},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			var selection ResponderSelection
			if err := yaml.Unmarshal([]byte(tc.yaml), &selection); err != nil {
				t.Fatalf("unmarshal: %v", err)
			}
			if selection.SelectOne == nil {
				t.Fatal("expected selectOne pool")
			}
			if got := selection.Strategy.Normalized(); got != tc.want {
				t.Fatalf("strategy = %q, want %q", got, tc.want)
			}
		})
	}
}

func TestResponderSelectOneStrategyRejectsInvalid(t *testing.T) {
	var selection ResponderSelection
	if err := yaml.Unmarshal([]byte("selectOne: {value: ['a']}\nstrategy: bogus"), &selection); err == nil {
		t.Fatal("expected invalid strategy to be rejected")
	}
	// strategy without selectOne is invalid.
	if err := yaml.Unmarshal([]byte("strategy: random"), &selection); err == nil {
		t.Fatal("expected strategy without selectOne to be rejected")
	}
}

func TestResponderSelectOneStrategyMarshal(t *testing.T) {
	value := any([]string{"agent-a", "agent-b"})
	selection := ResponderSelection{SelectOne: &ValueExpr{Value: &value}, Strategy: SelectOneStrategyRandom}
	out, err := yaml.Marshal(selection)
	if err != nil {
		t.Fatal(err)
	}
	text := string(out)
	if !strings.Contains(text, "strategy: random") {
		t.Fatalf("marshal must emit strategy: random, got %q", text)
	}
}

// TestResponderSelectOneOrderedParityRoundTrip proves an explicitly-declared
// `strategy: ordered` is preserved losslessly on marshal (Go and Web round-trip
// parity), while a truly omitted strategy stays the legacy shape without the
// strategy key.
func TestResponderSelectOneOrderedParityRoundTrip(t *testing.T) {
	// Explicit ordered must survive a marshal round-trip.
	explicit := ResponderSelection{SelectOne: &ValueExpr{Value: func() *any { v := any([]string{"a", "b"}); return &v }()}, Strategy: SelectOneStrategyOrdered}
	out, err := yaml.Marshal(explicit)
	if err != nil {
		t.Fatal(err)
	}
	if !strings.Contains(string(out), "strategy: ordered") {
		t.Fatalf("explicit ordered must be preserved on marshal, got %q", string(out))
	}
	// Omitted strategy must stay legacy (no strategy key).
	omitted := ResponderSelection{SelectOne: &ValueExpr{Value: func() *any { v := any([]string{"a", "b"}); return &v }()}}
	out, err = yaml.Marshal(omitted)
	if err != nil {
		t.Fatal(err)
	}
	if strings.Contains(string(out), "strategy") {
		t.Fatalf("omitted strategy must not emit a strategy key, got %q", string(out))
	}
}
