package app

import (
	"encoding/json"
	"regexp"
	"strings"

	"git.echol.cn/loser/st/server/global"
	"git.echol.cn/loser/st/server/model/app"
	"go.uber.org/zap"
)

// WorldbookEngine 世界书触发引擎
type WorldbookEngine struct{}

// TriggeredEntry 触发的条目
type TriggeredEntry struct {
	Entry    *app.WorldbookEntry
	Position int
	Order    int
}

// ScanAndTrigger 扫描消息并触发匹配的世界书条目
func (e *WorldbookEngine) ScanAndTrigger(worldbookID uint, messages []string) ([]*TriggeredEntry, error) {
	// 获取世界书的所有启用条目
	var entries []app.WorldbookEntry
	err := global.GVA_DB.Where("worldbook_id = ? AND enabled = ?", worldbookID, true).
		Order("`order` ASC").
		Find(&entries).Error
	if err != nil {
		return nil, err
	}

	var triggered []*TriggeredEntry

	// 合并所有消息用于扫描
	var scanTexts []string
	for _, entry := range entries {
		// 根据 scanDepth 决定扫描范围
		if entry.ScanDepth > 0 && entry.ScanDepth < len(messages) {
			// 只扫描最近 N 条消息
			scanTexts = messages[len(messages)-entry.ScanDepth:]
		} else {
			// 扫描所有消息
			scanTexts = messages
		}

		// 检查是否触发
		if e.shouldTrigger(&entry, scanTexts) {
			triggered = append(triggered, &TriggeredEntry{
				Entry:    &entry,
				Position: entry.Position,
				Order:    entry.Order,
			})
		}
	}

	return triggered, nil
}

// shouldTrigger 判断条目是否应该被触发
func (e *WorldbookEngine) shouldTrigger(entry *app.WorldbookEntry, messages []string) bool {
	// 常驻条目总是触发
	if entry.Constant {
		return true
	}

	// 概率触发
	if entry.Probability < 100 {
		// 简单的概率判断（实际应用中可以使用更好的随机数生成器）
		if entry.Probability <= 0 {
			return false
		}
		// 这里简化处理，实际应该用随机数
		// 为了演示，我们假设概率大于 50 就触发
		if entry.Probability < 50 {
			return false
		}
	}

	// 解析关键词
	var keys []string
	if len(entry.Keys) > 0 {
		json.Unmarshal(entry.Keys, &keys)
	}

	// 如果没有关键词，不触发
	if len(keys) == 0 {
		return false
	}

	// 合并所有消息为一个文本
	fullText := strings.Join(messages, " ")

	// 检查主关键词是否匹配
	primaryMatch := e.matchKeys(keys, fullText, entry.UseRegex, entry.CaseSensitive, entry.MatchWholeWords)
	if !primaryMatch {
		return false
	}

	// 如果需要次要关键词
	if entry.Selective {
		var secondaryKeys []string
		if len(entry.SecondaryKeys) > 0 {
			json.Unmarshal(entry.SecondaryKeys, &secondaryKeys)
		}

		if len(secondaryKeys) > 0 {
			secondaryMatch := e.matchKeys(secondaryKeys, fullText, entry.UseRegex, entry.CaseSensitive, entry.MatchWholeWords)

			// SelectiveLogic: 0=AND, 1=NOT
			if entry.SelectiveLogic == 0 {
				// AND: 次要关键词也必须匹配
				return secondaryMatch
			} else {
				// NOT: 次要关键词不能匹配
				return !secondaryMatch
			}
		}
	}

	return true
}

// matchKeys 检查关键词是否匹配
func (e *WorldbookEngine) matchKeys(keys []string, text string, useRegex, caseSensitive, matchWholeWords bool) bool {
	for _, key := range keys {
		if key == "" {
			continue
		}

		if useRegex {
			// 正则表达式匹配
			flags := ""
			if !caseSensitive {
				flags = "(?i)"
			}
			pattern := flags + key
			matched, err := regexp.MatchString(pattern, text)
			if err != nil {
				global.GVA_LOG.Warn("正则表达式匹配失败", zap.String("pattern", pattern), zap.Error(err))
				continue
			}
			if matched {
				return true
			}
		} else {
			// 普通文本匹配
			searchText := text
			searchKey := key

			if !caseSensitive {
				searchText = strings.ToLower(searchText)
				searchKey = strings.ToLower(searchKey)
			}

			if matchWholeWords {
				// 全词匹配
				pattern := `\b` + regexp.QuoteMeta(searchKey) + `\b`
				matched, _ := regexp.MatchString(pattern, searchText)
				if matched {
					return true
				}
			} else {
				// 包含匹配
				if strings.Contains(searchText, searchKey) {
					return true
				}
			}
		}
	}

	return false
}

// BuildPromptWithWorldbook 构建包含世界书内容的 prompt
func (e *WorldbookEngine) BuildPromptWithWorldbook(basePrompt string, triggered []*TriggeredEntry) string {
	if len(triggered) == 0 {
		return basePrompt
	}

	// 按位置和顺序排序
	// Position: 0=系统提示词前, 1=系统提示词后, 4=指定深度
	// 这里简化处理，都插入到系统提示词后

	var worldbookContent strings.Builder
	worldbookContent.WriteString("\n\n[World Information]\n")

	for _, t := range triggered {
		if t.Entry.Content != "" {
			worldbookContent.WriteString(t.Entry.Content)
			worldbookContent.WriteString("\n\n")
		}
	}

	// 将世界书内容插入到 basePrompt 之后
	return basePrompt + worldbookContent.String()
}
