package eino

import (
	"context"
	"encoding/base64"

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

	"github.com/hhs/camtalk/internal/ai/llm"
	"github.com/hhs/camtalk/internal/models"
	"github.com/hhs/camtalk/internal/store"
	"github.com/hhs/camtalk/internal/trace"
)

// NewHistoryLambda 创建历史组装 Lambda 节点。
// 输入: *STTOutput → 输出: []*schema.Message
//
// 从 PipelineState 读取请求元数据（SessionID、Scenario、ImageData 等），
// 构建系统提示词，组装历史消息和当前用户输入（含多模态图片）。
func NewHistoryLambda(
	historyFetcher func(ctx context.Context, sessionID string, limit int) ([]models.Message, error),
	scenarioRepo store.UserScenarioRepository,
	maxHistory int,
) *compose.Lambda {
	return compose.InvokableLambda(func(ctx context.Context, sttOut STTOutput) ([]*schema.Message, error) {
		log := trace.FromContext(ctx)

		// 从 State 读取请求元数据
		state := stateFromCtx(ctx)
		if state == nil {
			return []*schema.Message{}, nil
		}

		state.mu.Lock()
		sessionID := state.SessionID
		requestID := state.RequestID
		imageData := state.ImageData
		scenario := state.Scenario
		detailLevel := state.DetailLevel
		language := sttOut.Language
		userID := state.UserID
		state.mu.Unlock()

		// 加载用户自建情景（如果有 userID 和 scenarioRepo）
		var customScenarios map[string]string
		var customGreetings map[string]string
		if userID != "" && scenarioRepo != nil {
			scenarios, err := scenarioRepo.FindByUserID(ctx, userID)
			if err != nil {
				log.Warnw("load user scenarios failed", "user_id", userID, "error", err)
			} else if len(scenarios) > 0 {
				customScenarios = make(map[string]string, len(scenarios))
				customGreetings = make(map[string]string, len(scenarios))
				for _, s := range scenarios {
					customScenarios[s.ID] = s.Prompt
					if s.Greeting != "" {
						customGreetings[s.ID] = s.Greeting
					}
				}
				log.Debugw("loaded user scenarios", "user_id", userID, "count", len(scenarios))
			}
		}

		// 构建系统提示词（支持用户自建情景）
		scenarioPrompt := llm.GetScenarioPrompt(scenario, language, customScenarios)
		systemPrompt := llm.BuildSystemPrompt(language, detailLevel, scenarioPrompt)

		// 构建 system message（仅文本，多模态内容只能放在 user 角色）
		systemMsg := &schema.Message{
			Role:    schema.System,
			Content: systemPrompt,
		}

		messages := []*schema.Message{systemMsg}

		// 获取并追加历史消息
		if historyFetcher != nil && sessionID != "" {
			history, err := historyFetcher(ctx, sessionID, maxHistory)
			if err != nil {
				log.Warnw("fetch history failed, continuing", "error", err, "request_id", requestID)
			} else {
				for _, msg := range history {
					messages = append(messages, &schema.Message{
						Role:    schema.RoleType(msg.Role),
						Content: msg.Content,
					})
				}
			}
		}

		// 追加当前用户输入（含图片，多模态内容只能放在 user 角色）
		// 注意：不能同时设置 Content 和 UserInputMultiContent，需要统一放到 MultiContent 中
		if len(imageData) > 0 {
			base64Str := base64.StdEncoding.EncodeToString(imageData)
			mimeType := detectImageMimeType(imageData)
			parts := []schema.MessageInputPart{
				{
					Type: schema.ChatMessagePartTypeText,
					Text: sttOut.Text,
				},
				{
					Type: schema.ChatMessagePartTypeImageURL,
					Image: &schema.MessageInputImage{
						MessagePartCommon: schema.MessagePartCommon{
							Base64Data: &base64Str,
							MIMEType:   mimeType,
						},
						Detail: schema.ImageURLDetailAuto,
					},
				},
			}
			messages = append(messages, &schema.Message{
				Role:                schema.User,
				UserInputMultiContent: parts,
			})
		} else {
			messages = append(messages, &schema.Message{
				Role:    schema.User,
				Content: sttOut.Text,
			})
		}

		log.Debugw("history assembled",
			"message_count", len(messages),
			"has_image", len(imageData) > 0,
			"scenario", scenario)

		return messages, nil
	})
}

// detectImageMimeType 简单检测图片 MIME 类型。
func detectImageMimeType(data []byte) string {
	if len(data) < 4 {
		return "image/jpeg"
	}
	if data[0] == 0xFF && data[1] == 0xD8 && data[2] == 0xFF {
		return "image/jpeg"
	}
	if data[0] == 0x89 && data[1] == 0x50 && data[2] == 0x4E && data[3] == 0x47 {
		return "image/png"
	}
	if data[0] == 0x47 && data[1] == 0x49 && data[2] == 0x46 {
		return "image/gif"
	}
	if data[0] == 0x52 && data[1] == 0x49 && data[2] == 0x46 && data[3] == 0x46 {
		return "image/webp"
	}
	return "image/jpeg"
}
