package eino

import (
	"testing"

	"github.com/sundynix/sundynix-dispatcher/internal/dsl"
)

func TestEvalCondition(t *testing.T) {
	b := &board{refs: []string{"a", "b"}, toolOut: []string{"x"}, answer: "12345", profile: "p"}
	cases := []struct {
		cond string
		want bool
	}{
		{"", true},            // 空 → 真
		{"2>1", true},         // 字面量比较
		{"1>2", false},        //
		{"refs > 0", true},    // refs=2
		{"refs == 2", true},   //
		{"refs < 1", false},   //
		{"tools >= 1", true},  // tools=1
		{"tools != 0", true},  //
		{"answer <= 5", true}, // answer 长度 5
		{"answer > 5", false}, //
		{"profile == 1", true},
		{"乱写的条件", true}, // 非空但无法解析 → 默认真
	}
	for _, c := range cases {
		if got := evalCondition(c.cond, b); got != c.want {
			t.Errorf("evalCondition(%q) = %v, want %v", c.cond, got, c.want)
		}
	}
}

func TestResolveOperand(t *testing.T) {
	b := &board{refs: []string{"a"}, toolOut: []string{"x", "y"}, answer: "汉字三", profile: ""}
	cases := []struct {
		s    string
		want float64
	}{
		{"refs", 1},
		{"tools", 2},
		{"answer", 3}, // 3 个 rune
		{"profile", 0},
		{"42", 42},
		{"unknown", 0}, // 非关键字非数字 → 0
	}
	for _, c := range cases {
		if got := resolveOperand(c.s, b); got != c.want {
			t.Errorf("resolveOperand(%q) = %v, want %v", c.s, got, c.want)
		}
	}
}

func TestAggregate(t *testing.T) {
	parts := []string{"甲", "乙", "甲", "  ", ""}
	if got := aggregate("拼接", parts); len(got) != 1 || got[0] != "甲\n---\n乙\n---\n甲" {
		t.Errorf("拼接 = %v", got)
	}
	if got := aggregate("去重合并", parts); len(got) != 1 || got[0] != "甲\n---\n乙" {
		t.Errorf("去重合并 = %v", got)
	}
	if got := aggregate("未知策略", []string{"a", "b"}); len(got) != 1 || got[0] != "a\n---\nb" {
		t.Errorf("默认应为拼接, got %v", got)
	}
	if got := aggregate("拼接", []string{"", "  "}); got != nil {
		t.Errorf("全空应返回 nil, got %v", got)
	}
}

func TestBranchNode_Labeled(t *testing.T) {
	o := &Orchestrator{}
	tr := &execTracer{} // sink 为 nil → 所有发射均空操作
	byID := map[string]dsl.Node{"A": {ID: "A"}, "B": {ID: "B"}}
	// true 边故意列在第二位，验证按标签而非边序选路。
	outs := []dsl.Edge{
		{Source: "br", Target: "B", SourceHandle: "false"},
		{Source: "br", Target: "A", SourceHandle: "true"},
	}
	n := dsl.Node{ID: "br", Kind: "branch", Config: map[string]any{"condition": "2>1"}}
	if got := o.branchNode(n, &board{}, outs, byID, tr); len(got) != 1 || got[0] != "A" {
		t.Errorf("条件真应走 true 标签(A), got %v", got)
	}
	n.Config["condition"] = "1>2"
	if got := o.branchNode(n, &board{}, outs, byID, tr); len(got) != 1 || got[0] != "B" {
		t.Errorf("条件假应走 false 标签(B), got %v", got)
	}
}

func TestBranchNode_OrderFallback(t *testing.T) {
	o := &Orchestrator{}
	tr := &execTracer{}
	byID := map[string]dsl.Node{"A": {ID: "A"}, "B": {ID: "B"}}
	outs := []dsl.Edge{{Source: "br", Target: "A"}, {Source: "br", Target: "B"}} // 无标签
	n := dsl.Node{ID: "br", Kind: "branch", Config: map[string]any{"condition": "2>1"}}
	if got := o.branchNode(n, &board{}, outs, byID, tr); len(got) != 1 || got[0] != "A" {
		t.Errorf("无标签条件真应走第一条边(A), got %v", got)
	}
	n.Config["condition"] = "1>2"
	if got := o.branchNode(n, &board{}, outs, byID, tr); len(got) != 1 || got[0] != "B" {
		t.Errorf("无标签条件假应走第二条边(B), got %v", got)
	}
	// 单出边 + 条件假 → 不继续。
	single := []dsl.Edge{{Source: "br", Target: "A"}}
	n.Config["condition"] = "1>2"
	if got := o.branchNode(n, &board{}, single, byID, tr); len(got) != 0 {
		t.Errorf("单边条件假应剪掉, got %v", got)
	}
}

func TestBranchNode_DefaultFallback(t *testing.T) {
	o := &Orchestrator{}
	tr := &execTracer{}
	byID := map[string]dsl.Node{"A": {ID: "A"}, "D": {ID: "D"}}
	// 只有 true 边 + default 边，无 false 边。
	outs := []dsl.Edge{
		{Source: "br", Target: "A", SourceHandle: "true"},
		{Source: "br", Target: "D", SourceHandle: "default"},
	}
	n := dsl.Node{ID: "br", Kind: "branch", Config: map[string]any{"condition": "1>2"}}
	// 条件假 → true 未命中 → 走 default(D)，而非悄悄落 END。
	if got := o.branchNode(n, &board{}, outs, byID, tr); len(got) != 1 || got[0] != "D" {
		t.Errorf("条件假且无 false 边应走 default(D), got %v", got)
	}
	// 条件真 → 正常走 true(A)，default 不介入。
	n.Config["condition"] = "2>1"
	if got := o.branchNode(n, &board{}, outs, byID, tr); len(got) != 1 || got[0] != "A" {
		t.Errorf("条件真应走 true(A), got %v", got)
	}
	// 无 default 边时，未命中仍返回空（兜底不凭空造边，交上层收口 END）。
	noDef := []dsl.Edge{{Source: "br", Target: "A", SourceHandle: "true"}}
	n.Config["condition"] = "1>2"
	if got := o.branchNode(n, &board{}, noDef, byID, tr); len(got) != 0 {
		t.Errorf("无 default 且未命中应为空, got %v", got)
	}
}

func TestSmallHelpers(t *testing.T) {
	cfg := map[string]any{"s": "  hi ", "n": 7, "b": true, "nil": nil}
	if cstr(cfg, "s") != "hi" {
		t.Error("cstr 应去空白")
	}
	if cstr(cfg, "n") != "7" {
		t.Error("cstr 非字符串应 fmt 转换")
	}
	if cstr(cfg, "missing") != "" || cstr(cfg, "nil") != "" || cstr(nil, "x") != "" {
		t.Error("cstr 缺失/nil 应空串")
	}
	if !cbool(cfg, "b") || cbool(cfg, "s") || cbool(nil, "b") {
		t.Error("cbool 行为不符")
	}
	if countLines("") != 0 || countLines("a") != 1 || countLines("a\nb\nc") != 3 {
		t.Error("countLines 行为不符")
	}
	if labelOf(dsl.Node{Label: "L"}, "d") != "L" || labelOf(dsl.Node{}, "d") != "d" {
		t.Error("labelOf 行为不符")
	}
	es := []dsl.Edge{{Target: "x"}, {Target: "y"}}
	got := targetsOf(es)
	if len(got) != 2 || got[0] != "x" || got[1] != "y" {
		t.Errorf("targetsOf = %v", got)
	}
}
