package eino

import (
	"context"
	"time"

	openaiImpl "github.com/cloudwego/eino-ext/components/model/openai"
	"github.com/cloudwego/eino/compose"

	"github.com/hhs/camtalk/internal/ai/stt"
	"github.com/hhs/camtalk/internal/ai/tts"
	"github.com/hhs/camtalk/internal/config"
	"github.com/hhs/camtalk/internal/logger"
	"github.com/hhs/camtalk/internal/models"
	"github.com/hhs/camtalk/internal/session"
	"github.com/hhs/camtalk/internal/store"
)

const (
	nodeSTT      = "stt"
	nodeHistory  = "history"
	nodeLLM            = "llm"
	nodeMessageToString = "msg2str"
	nodeSplitter       = "splitter"
	nodeTTS      = "tts"
	nodeDone     = "done"
)

// PipelineGraph 封装编译后的 Eino Graph。
type PipelineGraph struct {
	Runnable compose.Runnable[PipelineInput, PipelineOutput]
}

// NewPipelineGraph 构建 CamTalk AI 编排 Graph。
//
// 拓扑：START → STT → History → ChatModel → Splitter → TTS → Done → END
//
// Graph 使用 Stream 模式调用，ChatModel 实现真正的 token 级流式输出。
// LLM token 通过 Callback 的 OnEndWithStreamOutput 实时推送到客户端。
func NewPipelineGraph(
	ctx context.Context,
	cfg *config.Config,
	sttService stt.Service,
	ttsService tts.Service,
	sessionMgr session.Manager,
	scenarioRepo store.UserScenarioRepository,
) (*PipelineGraph, error) {
	log := logger.Log

	// 1. 创建 eino-ext ChatModel（对接 DashScope OpenAI 兼容接口）
	chatModel, err := openaiImpl.NewChatModel(ctx, &openaiImpl.ChatModelConfig{
		APIKey:  cfg.AI.LLM.APIKey,
		Model:   cfg.AI.LLM.Model,
		BaseURL: cfg.AI.LLM.Endpoint,
		Timeout: time.Duration(cfg.AI.LLM.Timeout) * time.Second,
	})
	if err != nil {
		return nil, err
	}
	log.Infow("Eino ChatModel 初始化成功",
		"model", cfg.AI.LLM.Model,
		"endpoint", cfg.AI.LLM.Endpoint)

	// 2. 构建 Graph（值类型，非指针）
	g := compose.NewGraph[PipelineInput, PipelineOutput](
		compose.WithGenLocalState(genLocalState),
	)

	// 3. 添加节点
	maxHistory := cfg.Session.MaxHistory

	_ = g.AddLambdaNode(nodeSTT, NewSTTLambda(sttService))
	_ = g.AddLambdaNode(nodeHistory, NewHistoryLambda(sessionMgr.GetHistory, scenarioRepo, maxHistory))
	_ = g.AddChatModelNode(nodeLLM, chatModel)
	_ = g.AddLambdaNode(nodeMessageToString, NewMessageToStringLambda())
	_ = g.AddLambdaNode(nodeSplitter, NewSplitterLambda())
	_ = g.AddLambdaNode(nodeTTS, NewTTSLambda(
		ttsService,
		cfg.AI.TTS.Voice,
		cfg.AI.TTS.Speed,
		cfg.AI.TTS.OutputFormat,
		cfg.AI.TTS.SampleRate,
	))
	_ = g.AddLambdaNode(nodeDone, NewDoneLambda(cfg.AI.LLM.Model))

	// 4. 连接边
	_ = g.AddEdge(compose.START, nodeSTT)
	_ = g.AddEdge(nodeSTT, nodeHistory)
	_ = g.AddEdge(nodeHistory, nodeLLM)
	_ = g.AddEdge(nodeLLM, nodeMessageToString)
	_ = g.AddEdge(nodeMessageToString, nodeSplitter)
	_ = g.AddEdge(nodeSplitter, nodeTTS)
	_ = g.AddEdge(nodeTTS, nodeDone)
	_ = g.AddEdge(nodeDone, compose.END)

	// 5. 编译（回调在运行时通过 Stream option 传入）
	runnable, err := g.Compile(ctx)
	if err != nil {
		return nil, err
	}

	log.Infow("Eino Graph 编译成功", "nodes", 7)
	return &PipelineGraph{Runnable: runnable}, nil
}

// buildPipelineInput 从 WebSocket 请求和会话配置构建 Graph 输入。
func buildPipelineInput(req models.WsQuery, sessionID string, sess *models.Session, audioData, imageData []byte) PipelineInput {
	return PipelineInput{
		AudioData:  audioData,
		ImageData:  imageData,
		Text:       req.Text,
		SessionID:  sessionID,
		RequestID:  req.RequestID,
		Language:   sess.Config.Language,
		Scenario:   sess.Config.Scenario,
		TTSEnabled: sess.Config.TTSEnabled,
		UserID:     sess.UserID,
	}
}
