package main

import (
	"crypto/sha1"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"time"
)

func runChatSync() error {
	logInfo("聊天记录同步 demo 启动")

	config, err := loadConfig("config.toml")
	if err != nil {
		return fmt.Errorf("读取 config.toml 失败：%w", err)
	}

	regionsPath := appDataPath(config, config.Agent.RegionsPath)
	screenshotPath := appDataPath(config, config.Agent.ScreenshotPath)
	archivePath := appDataPath(config, config.Agent.ChatRecordsPath)

	annotation, err := loadAnnotation(regionsPath)
	if err != nil {
		return fmt.Errorf("读取标注文件失败：%w", err)
	}
	summaries, warnings := inferMissingRegionTypes(summarizeRegions(annotation.Regions))
	for _, warning := range warnings {
		logWarning(warning)
	}
	if err := validateChatSyncRegions(summaries); err != nil {
		return err
	}

	archive, existed, err := loadChatArchive(archivePath)
	if err != nil {
		return err
	}
	if !existed || len(archive.Messages) == 0 {
		logInfo("本地没有聊天记录，先滚动到聊天记录顶部")
		if err := scrollChatToTop(config, summaries, screenshotPath); err != nil {
			return err
		}
	} else {
		logInfo(fmt.Sprintf("已读取本地聊天记录 messages=%d，将从当前视图继续向下读取", len(archive.Messages)))
	}

	if archive.App == "" {
		archive.App = "wechat"
		archive.CreatedAt = time.Now().Format(time.RFC3339)
	}
	archive.Source = TaskSource{RegionsPath: regionsPath, ScreenshotPath: screenshotPath}

	seenNoNewPages := 0
	for pageIndex := 0; pageIndex < config.Agent.ChatSyncMaxPages; pageIndex++ {
		window, err := findWeChatWindow()
		if err != nil {
			return err
		}
		if err := captureWindow(window, screenshotPath); err != nil {
			return err
		}

		result, err := extractChatPage(config, summaries, screenshotPath)
		if err != nil {
			return err
		}
		if result.ContactName != "" && archive.Contact == "" {
			archive.Contact = result.ContactName
		}

		newCount := appendChatMessages(&archive, result.Messages, pageIndex)
		archive.Pages = append(archive.Pages, ChatPage{
			PageIndex: pageIndex,
			Direction: "down",
			NewCount:  newCount,
			SeenAt:    time.Now().Format(time.RFC3339),
		})
		logTask(fmt.Sprintf("聊天页 page=%d 提取 messages=%d new=%d summary=%s", pageIndex+1, len(result.Messages), newCount, result.PageSummary))

		if newCount == 0 {
			seenNoNewPages++
		} else {
			seenNoNewPages = 0
		}
		if seenNoNewPages >= 2 {
			logInfo("连续页面没有新增聊天记录，停止同步")
			break
		}

		if pageIndex < config.Agent.ChatSyncMaxPages-1 {
			if err := scrollChat(config, summaries, -absInt(config.Agent.ScrollDeltaY)); err != nil {
				return err
			}
			time.Sleep(800 * time.Millisecond)
		}
	}

	archive.UpdatedAt = time.Now().Format(time.RFC3339)
	if err := saveChatArchive(archivePath, archive); err != nil {
		return err
	}
	logInfo(fmt.Sprintf("聊天记录同步完成，messages=%d path=%s", len(archive.Messages), archivePath))

	return nil
}

func validateChatSyncRegions(summaries []RegionSummary) error {
	index := regionByType(summaries)
	if _, exists := index["chat_content"]; !exists {
		return fmt.Errorf("missing required region: chat_content")
	}
	return nil
}

func scrollChatToTop(config Config, summaries []RegionSummary, screenshotPath string) error {
	for index := 0; index < config.Agent.ChatSyncTopScrolls; index++ {
		if err := scrollChat(config, summaries, absInt(config.Agent.ScrollDeltaY)); err != nil {
			return err
		}
		logTask(fmt.Sprintf("向上滚动到历史顶部 round=%d/%d", index+1, config.Agent.ChatSyncTopScrolls))
		time.Sleep(350 * time.Millisecond)
	}

	window, err := findWeChatWindow()
	if err != nil {
		return err
	}
	return captureWindow(window, screenshotPath)
}

func scrollChat(config Config, summaries []RegionSummary, deltaY int) error {
	chat, ok := regionByType(summaries)["chat_content"]
	if !ok {
		return fmt.Errorf("chat_content region not found")
	}
	window, err := findWeChatWindow()
	if err != nil {
		return err
	}
	pointX := window.X + chat.CenterSource[0]
	pointY := window.Y + chat.CenterSource[1]
	logTask(fmt.Sprintf("滚动聊天区域 point=[%.1f, %.1f] delta_y=%d", pointX, pointY, deltaY))
	return scrollAt(pointX, pointY, deltaY)
}

func extractChatPage(config Config, summaries []RegionSummary, screenshotPath string) (ChatExtractResult, error) {
	imageDataURL, err := loadImageDataURL(screenshotPath)
	if err != nil {
		return ChatExtractResult{}, err
	}
	prompt, err := buildChatExtractPrompt(summaries)
	if err != nil {
		return ChatExtractResult{}, err
	}
	logThink("正在用豆包多模态抽取当前聊天区域消息")
	raw, err := requestLLM(config, prompt, imageDataURL)
	if err != nil {
		return ChatExtractResult{}, err
	}
	jsonText, err := extractJSON(raw)
	if err != nil {
		_ = saveRawResponse(config, raw)
		return ChatExtractResult{}, err
	}
	var result ChatExtractResult
	if err := json.Unmarshal([]byte(jsonText), &result); err != nil {
		_ = saveRawResponse(config, raw)
		return ChatExtractResult{}, err
	}
	return result, nil
}

func buildChatExtractPrompt(summaries []RegionSummary) (string, error) {
	regionsJSON, err := json.MarshalIndent(summaries, "", "  ")
	if err != nil {
		return "", err
	}
	return `你是微信聊天记录抽取器。

请只识别截图中 chat_content 标注区域里的聊天消息，不要识别联系人列表、输入框或其它区域。

要求：
- 按消息在页面中的从上到下顺序输出。
- sender 可填 "me"、"other" 或能识别到的昵称；无法判断填 "unknown"。
- role 可填 "me"、"other"、"system"。
- time 如果画面中没有明确时间则留空字符串。
- content 必须是消息文本，不能编造。
- 只能返回合法 JSON，不要 Markdown，不要解释。

标注区域：
` + string(regionsJSON) + `

返回结构：
{
  "contact_name": "unknown",
  "page_summary": "当前页聊天内容摘要",
  "messages": [
    {"sender":"other","role":"other","time":"","content":"消息文本"}
  ]
}`, nil
}

func loadChatArchive(path string) (ChatArchive, bool, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		if os.IsNotExist(err) {
			return ChatArchive{}, false, nil
		}
		return ChatArchive{}, false, err
	}
	var archive ChatArchive
	if err := json.Unmarshal(data, &archive); err != nil {
		return ChatArchive{}, false, err
	}
	return archive, true, nil
}

func saveChatArchive(path string, archive ChatArchive) error {
	if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
		return err
	}
	data, err := json.MarshalIndent(archive, "", "  ")
	if err != nil {
		return err
	}
	return os.WriteFile(path, data, 0644)
}

func appendChatMessages(archive *ChatArchive, messages []ChatMessage, pageIndex int) int {
	existing := make(map[string]bool, len(archive.Messages))
	for _, message := range archive.Messages {
		existing[message.ID] = true
	}

	newCount := 0
	for _, message := range messages {
		message.Content = strings.TrimSpace(message.Content)
		if message.Content == "" {
			continue
		}
		message.PageIndex = pageIndex
		message.SeenAt = time.Now().Format(time.RFC3339)
		message.ID = chatMessageID(message)
		if existing[message.ID] {
			continue
		}
		existing[message.ID] = true
		archive.Messages = append(archive.Messages, message)
		newCount++
	}
	return newCount
}

func chatMessageID(message ChatMessage) string {
	key := strings.Join([]string{
		strings.TrimSpace(message.Sender),
		strings.TrimSpace(message.Role),
		strings.TrimSpace(message.Time),
		strings.TrimSpace(message.Content),
	}, "|")
	sum := sha1.Sum([]byte(key))
	return hex.EncodeToString(sum[:])
}

func absInt(value int) int {
	if value < 0 {
		return -value
	}
	return value
}
