package eino

import (
	"context"
	"strings"
	"testing"

	"github.com/cloudwego/eino/components/model"
	"github.com/cloudwego/eino/compose"
	"github.com/cloudwego/eino/schema"

	"github.com/sundynix/sundynix-dispatcher/internal/harness"
	"github.com/sundynix/sundynix-dispatcher/internal/llm"
)

// stubModel 是实现 Eino model.BaseChatModel 的测试桩，固定回一段文本（确定性）。
type stubModel struct{ reply string }

func (s *stubModel) Generate(_ context.Context, _ []*schema.Message, _ ...model.Option) (*schema.Message, error) {
	return schema.AssistantMessage(s.reply, nil), nil
}

func (s *stubModel) Stream(_ context.Context, _ []*schema.Message, _ ...model.Option) (*schema.StreamReader[*schema.Message], error) {
	sr, sw := schema.Pipe[*schema.Message](1)
	go func() {
		sw.Send(schema.AssistantMessage(s.reply, nil), nil)
		sw.Close()
	}()
	return sr, nil
}

// TestComposeGraphRuns 直接验证 compose.Graph（START→ChatModel→END）能编译并流式产出。
func TestComposeGraphRuns(t *testing.T) {
	g := compose.NewGraph[[]*schema.Message, *schema.Message]()
	if err := g.AddChatModelNode("model", &stubModel{reply: "你好世界"}); err != nil {
		t.Fatal(err)
	}
	_ = g.AddEdge(compose.START, "model")
	_ = g.AddEdge("model", compose.END)
	r, err := g.Compile(context.Background())
	if err != nil {
		t.Fatalf("compile: %v", err)
	}
	out, err := r.Invoke(context.Background(), []*schema.Message{schema.UserMessage("hi")})
	if err != nil {
		t.Fatalf("invoke: %v", err)
	}
	if out.Content != "你好世界" {
		t.Fatalf("got %q", out.Content)
	}
}

// TestComposeConversation 验证对话主流程走 compose 路径：模型输出流式回流到 sink 并累计成稿。
func TestComposeConversation(t *testing.T) {
	fs := &fakeSink{}
	o := &Orchestrator{
		pool:    &fakeLLM{ready: true, cm: &stubModel{reply: "我是 compose 路径的回答"}},
		breaker: harness.NewCircuitBreaker(),
		sink:    fs,
	}
	b := &board{query: "你好"}
	o.runComposeConversation(context.Background(), "task_compose", b, "", &execTracer{}, "agent")

	if !strings.Contains(b.answer, "compose 路径") {
		t.Fatalf("成稿未含模型输出: %q", b.answer)
	}
	if !strings.Contains(fs.text(), "compose 路径") {
		t.Fatalf("sink 未收到流式 token: %q", fs.text())
	}
}

// TestComposeConversationDegradesToRunAgent 验证无 Eino ChatModel 时降级回 runAgent（流式回流不丢）。
func TestComposeConversationDegradesToRunAgent(t *testing.T) {
	fs := &fakeSink{}
	o := &Orchestrator{
		// cm 为 nil → ChatModel() 返回 nil → runComposeConversation 降级 runAgent（走 stream 桩）。
		pool:    &fakeLLM{ready: true, stream: func([]llm.ChatMessage) string { return "降级路径回答" }},
		breaker: harness.NewCircuitBreaker(),
		sink:    fs,
	}
	b := &board{query: "你好"}
	o.runComposeConversation(context.Background(), "task_degrade", b, "", &execTracer{}, "agent")
	if !strings.Contains(b.answer, "降级路径") {
		t.Fatalf("无 ChatModel 应降级 runAgent 出稿: %q", b.answer)
	}
}
