package app

import (
	"encoding/json"
	"errors"
	"fmt"
	"math/rand"
	"regexp"
	"strconv"
	"strings"
	"time"

	"git.echol.cn/loser/st/server/global"
	"git.echol.cn/loser/st/server/model/app"
	"git.echol.cn/loser/st/server/model/app/request"
	"git.echol.cn/loser/st/server/model/app/response"
	"go.uber.org/zap"
	"gorm.io/datatypes"
	"gorm.io/gorm"
)

type RegexScriptService struct{}

// CreateRegexScript 创建正则脚本
func (s *RegexScriptService) CreateRegexScript(userID uint, req *request.CreateRegexScriptRequest) (*response.RegexScriptResponse, error) {
	trimStringsJSON, _ := json.Marshal(req.TrimStrings)

	script := &app.RegexScript{
		UserID:          userID,
		Name:            req.Name,
		FindRegex:       req.FindRegex,
		ReplaceWith:     req.ReplaceWith,
		TrimStrings:     datatypes.JSON(trimStringsJSON),
		Placement:       req.Placement,
		Disabled:        req.Disabled,
		MarkdownOnly:    req.MarkdownOnly,
		RunOnEdit:       req.RunOnEdit,
		PromptOnly:      req.PromptOnly,
		SubstituteRegex: req.SubstituteRegex,
		MinDepth:        req.MinDepth,
		MaxDepth:        req.MaxDepth,
		Scope:           req.Scope,
		OwnerCharID:     req.OwnerCharID,
		OwnerPresetID:   req.OwnerPresetID,
		Order:           req.Order,
	}

	if err := global.GVA_DB.Create(script).Error; err != nil {
		global.GVA_LOG.Error("创建正则脚本失败", zap.Error(err))
		return nil, err
	}

	resp := response.ToRegexScriptResponse(script)
	return &resp, nil
}

// GetRegexScriptList 获取正则脚本列表
func (s *RegexScriptService) GetRegexScriptList(userID uint, req *request.GetRegexScriptListRequest) ([]response.RegexScriptResponse, int64, error) {
	var scripts []app.RegexScript
	var total int64

	db := global.GVA_DB.Model(&app.RegexScript{}).Where("user_id = ?", userID)

	if req.Keyword != "" {
		db = db.Where("name LIKE ?", "%"+req.Keyword+"%")
	}

	if req.Scope != nil {
		db = db.Where("scope = ?", *req.Scope)
	}

	if req.OwnerCharID != nil {
		db = db.Where("owner_char_id = ?", *req.OwnerCharID)
	}

	if err := db.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	offset := (req.Page - 1) * req.PageSize
	if err := db.Order("\"order\" ASC, created_at DESC").Offset(offset).Limit(req.PageSize).Find(&scripts).Error; err != nil {
		global.GVA_LOG.Error("获取正则脚本列表失败", zap.Error(err))
		return nil, 0, err
	}

	var list []response.RegexScriptResponse
	for i := range scripts {
		list = append(list, response.ToRegexScriptResponse(&scripts[i]))
	}
	return list, total, nil
}

// GetRegexScriptByID 获取正则脚本详情
func (s *RegexScriptService) GetRegexScriptByID(userID uint, id uint) (*response.RegexScriptResponse, error) {
	var script app.RegexScript
	if err := global.GVA_DB.Where("id = ? AND user_id = ?", id, userID).First(&script).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errors.New("正则脚本不存在或无权访问")
		}
		return nil, err
	}
	resp := response.ToRegexScriptResponse(&script)
	return &resp, nil
}

// UpdateRegexScript 更新正则脚本
func (s *RegexScriptService) UpdateRegexScript(userID uint, id uint, req *request.UpdateRegexScriptRequest) error {
	var script app.RegexScript
	if err := global.GVA_DB.Where("id = ? AND user_id = ?", id, userID).First(&script).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return errors.New("正则脚本不存在或无权修改")
		}
		return err
	}

	updates := make(map[string]interface{})
	if req.Name != nil {
		updates["name"] = *req.Name
	}
	if req.FindRegex != nil {
		updates["find_regex"] = *req.FindRegex
	}
	if req.ReplaceWith != nil {
		updates["replace_with"] = *req.ReplaceWith
	}
	if req.TrimStrings != nil {
		trimStringsJSON, _ := json.Marshal(req.TrimStrings)
		updates["trim_strings"] = datatypes.JSON(trimStringsJSON)
	}
	if req.Placement != nil {
		updates["placement"] = *req.Placement
	}
	if req.Disabled != nil {
		updates["disabled"] = *req.Disabled
	}
	if req.MarkdownOnly != nil {
		updates["markdown_only"] = *req.MarkdownOnly
	}
	if req.RunOnEdit != nil {
		updates["run_on_edit"] = *req.RunOnEdit
	}
	if req.PromptOnly != nil {
		updates["prompt_only"] = *req.PromptOnly
	}
	if req.SubstituteRegex != nil {
		updates["substitute_regex"] = *req.SubstituteRegex
	}
	if req.MinDepth != nil {
		updates["min_depth"] = req.MinDepth
	}
	if req.MaxDepth != nil {
		updates["max_depth"] = req.MaxDepth
	}
	if req.Scope != nil {
		updates["scope"] = *req.Scope
	}
	if req.OwnerCharID != nil {
		updates["owner_char_id"] = req.OwnerCharID
	}
	if req.OwnerPresetID != nil {
		updates["owner_preset_id"] = req.OwnerPresetID
	}
	if req.Order != nil {
		updates["order"] = *req.Order
	}

	return global.GVA_DB.Model(&script).Updates(updates).Error
}

// DeleteRegexScript 删除正则脚本
func (s *RegexScriptService) DeleteRegexScript(userID uint, id uint) error {
	result := global.GVA_DB.Where("id = ? AND user_id = ?", id, userID).Delete(&app.RegexScript{})
	if result.Error != nil {
		return result.Error
	}
	if result.RowsAffected == 0 {
		return errors.New("正则脚本不存在或无权删除")
	}
	return nil
}

// TestRegexScript 测试正则脚本
func (s *RegexScriptService) TestRegexScript(userID uint, id uint, testString string) (*response.TestRegexScriptResponse, error) {
	var script app.RegexScript
	if err := global.GVA_DB.Where("id = ? AND user_id = ?", id, userID).First(&script).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errors.New("正则脚本不存在或无权访问")
		}
		return nil, err
	}

	result, err := s.ExecuteScript(&script, testString, "", "")
	if err != nil {
		return &response.TestRegexScriptResponse{
			Original: testString,
			Result:   testString,
			Success:  false,
			Error:    err.Error(),
		}, nil
	}

	return &response.TestRegexScriptResponse{
		Original: testString,
		Result:   result,
		Success:  true,
	}, nil
}

// ExecuteScript 执行正则脚本
func (s *RegexScriptService) ExecuteScript(script *app.RegexScript, text string, userName string, charName string) (string, error) {
	if script.Disabled {
		return text, nil
	}

	result := text

	// 1. 宏替换
	if script.SubstituteRegex {
		result = s.substituteMacros(result, userName, charName)
	}

	// 2. 正则替换
	if script.FindRegex != "" {
		re, err := regexp.Compile(script.FindRegex)
		if err != nil {
			global.GVA_LOG.Warn("正则表达式编译失败", zap.String("pattern", script.FindRegex), zap.Error(err))
			return text, err
		}

		// 特殊处理：如果正则匹配 <Status_block>，需要提取 YAML 并注入到 HTML 模板中
		if strings.Contains(script.FindRegex, "Status_block") {
			result = s.replaceStatusBlockWithHTML(result, script.ReplaceWith, re)
		} else {
			result = re.ReplaceAllString(result, script.ReplaceWith)
		}
	}

	// 3. 修剪字符串
	if len(script.TrimStrings) > 0 {
		var trimStrings []string
		json.Unmarshal(script.TrimStrings, &trimStrings)
		for _, trimStr := range trimStrings {
			result = strings.ReplaceAll(result, trimStr, "")
		}
	}

	return result, nil
}

// replaceStatusBlockWithHTML 替换 <Status_block> 为 HTML 模板，并注入 YAML 数据
func (s *RegexScriptService) replaceStatusBlockWithHTML(text string, htmlTemplate string, statusBlockRegex *regexp.Regexp) string {
	return statusBlockRegex.ReplaceAllStringFunc(text, func(match string) string {
		// 提取 YAML 数据
		yamlRegex := regexp.MustCompile(`<Status_block>\s*([\s\S]*?)\s*</Status_block>`)
		yamlMatches := yamlRegex.FindStringSubmatch(match)

		if len(yamlMatches) < 2 {
			global.GVA_LOG.Warn("无法提取 Status_block 中的 YAML 数据")
			return match
		}

		yamlData := strings.TrimSpace(yamlMatches[1])

		// 在 HTML 模板中查找 <script id="yaml-data-source" type="text/yaml"></script>
		// 并将 YAML 数据注入其中
		injectedHTML := strings.Replace(
			htmlTemplate,
			`<script id="yaml-data-source" type="text/yaml"></script>`,
			fmt.Sprintf(`<script id="yaml-data-source" type="text/yaml">%s</script>`, yamlData),
			1,
		)

		global.GVA_LOG.Info(fmt.Sprintf("[正则脚本] 已将 Status_block YAML 数据注入到 HTML 模板，YAML 长度: %d", len(yamlData)))

		return injectedHTML
	})
}

// substituteMacros 替换宏变量
func (s *RegexScriptService) substituteMacros(text string, userName string, charName string) string {
	result := text

	// 保存原始文本
	result = strings.ReplaceAll(result, "{{original}}", text)

	// 用户名变量
	if userName != "" {
		result = strings.ReplaceAll(result, "{{user}}", userName)
		result = strings.ReplaceAll(result, "{{User}}", userName)
	}

	// 角色名变量
	if charName != "" {
		result = strings.ReplaceAll(result, "{{char}}", charName)
		result = strings.ReplaceAll(result, "{{Char}}", charName)
	}

	// 时间变量
	now := time.Now()
	result = strings.ReplaceAll(result, "{{time}}", now.Format("15:04:05"))
	result = strings.ReplaceAll(result, "{{date}}", now.Format("2006-01-02"))
	result = strings.ReplaceAll(result, "{{datetime}}", now.Format("2006-01-02 15:04:05"))
	result = strings.ReplaceAll(result, "{{timestamp}}", fmt.Sprintf("%d", now.Unix()))
	result = strings.ReplaceAll(result, "{{time_12h}}", now.Format("03:04:05 PM"))
	result = strings.ReplaceAll(result, "{{date_short}}", now.Format("01/02/06"))
	result = strings.ReplaceAll(result, "{{weekday}}", now.Weekday().String())
	result = strings.ReplaceAll(result, "{{month}}", now.Month().String())
	result = strings.ReplaceAll(result, "{{year}}", fmt.Sprintf("%d", now.Year()))

	// 随机数变量
	result = regexp.MustCompile(`\{\{random:(\d+)-(\d+)\}\}`).ReplaceAllStringFunc(result, func(match string) string {
		re := regexp.MustCompile(`\{\{random:(\d+)-(\d+)\}\}`)
		matches := re.FindStringSubmatch(match)
		if len(matches) == 3 {
			min, _ := strconv.Atoi(matches[1])
			max, _ := strconv.Atoi(matches[2])
			if max > min {
				return fmt.Sprintf("%d", rand.Intn(max-min+1)+min)
			}
		}
		return match
	})

	// 简单随机数 {{random}}
	result = regexp.MustCompile(`\{\{random\}\}`).ReplaceAllStringFunc(result, func(match string) string {
		return fmt.Sprintf("%d", rand.Intn(100))
	})

	// 随机选择 {{pick:option1|option2|option3}}
	result = regexp.MustCompile(`\{\{pick:([^}]+)\}\}`).ReplaceAllStringFunc(result, func(match string) string {
		re := regexp.MustCompile(`\{\{pick:([^}]+)\}\}`)
		matches := re.FindStringSubmatch(match)
		if len(matches) == 2 {
			options := strings.Split(matches[1], "|")
			if len(options) > 0 {
				return options[rand.Intn(len(options))]
			}
		}
		return match
	})

	// 换行符变量
	result = strings.ReplaceAll(result, "{{newline}}", "\n")
	result = strings.ReplaceAll(result, "{{tab}}", "\t")
	result = strings.ReplaceAll(result, "{{space}}", " ")

	// 空值变量
	result = strings.ReplaceAll(result, "{{empty}}", "")

	return result
}

// ExtractSetVars 从文本中提取 {{setvar::key::value}} 并返回变量映射和清理后的文本
func (s *RegexScriptService) ExtractSetVars(text string) (map[string]string, string) {
	vars := make(map[string]string)

	// 匹配 {{setvar::key::value}}
	re := regexp.MustCompile(`\{\{setvar::([^:]+)::([^}]*)\}\}`)
	matches := re.FindAllStringSubmatch(text, -1)

	for _, match := range matches {
		if len(match) == 3 {
			key := strings.TrimSpace(match[1])
			value := match[2]
			vars[key] = value
		}
	}

	// 从文本中移除所有 {{setvar::}} 标记
	cleanText := re.ReplaceAllString(text, "")

	return vars, cleanText
}

// SubstituteGetVars 替换文本中的 {{getvar::key}} 为实际变量值
func (s *RegexScriptService) SubstituteGetVars(text string, variables map[string]string) string {
	result := text

	// 匹配 {{getvar::key}}
	re := regexp.MustCompile(`\{\{getvar::([^}]+)\}\}`)
	result = re.ReplaceAllStringFunc(result, func(match string) string {
		matches := re.FindStringSubmatch(match)
		if len(matches) == 2 {
			key := strings.TrimSpace(matches[1])
			if value, ok := variables[key]; ok {
				return value
			}
		}
		return "" // 如果变量不存在，返回空字符串
	})

	return result
}

// ExtractStatusBlock 提取 <Status_block> 中的 YAML 数据
func (s *RegexScriptService) ExtractStatusBlock(text string) (string, string) {
	// 匹配 <Status_block>...</Status_block>
	re := regexp.MustCompile(`(?s)<Status_block>\s*(.*?)\s*</Status_block>`)
	matches := re.FindStringSubmatch(text)

	if len(matches) == 2 {
		statusBlock := strings.TrimSpace(matches[1])
		cleanText := re.ReplaceAllString(text, "")
		return statusBlock, cleanText
	}

	return "", text
}

// ExtractMaintext 提取 <maintext> 中的内容
func (s *RegexScriptService) ExtractMaintext(text string) (string, string) {
	// 匹配 <maintext>...</maintext>
	re := regexp.MustCompile(`(?s)<maintext>\s*(.*?)\s*</maintext>`)
	matches := re.FindStringSubmatch(text)

	if len(matches) == 2 {
		maintext := strings.TrimSpace(matches[1])
		cleanText := re.ReplaceAllString(text, "")
		return maintext, cleanText
	}

	return "", text
}

// GetScriptsForPlacement 获取指定阶段的脚本
func (s *RegexScriptService) GetScriptsForPlacement(userID uint, placement int, charID *uint, presetID *uint) ([]app.RegexScript, error) {
	var scripts []app.RegexScript

	// markdownOnly=true 脚本只在前端显示层执行，后端不应用
	db := global.GVA_DB.Where("user_id = ? AND placement = ? AND disabled = ? AND markdown_only = ?", userID, placement, false, false)

	// 作用域过滤：全局(0) 或 角色(1) 或 预设(2)
	// 使用参数化查询避免 SQL 注入
	if charID != nil && presetID != nil {
		db = db.Where("scope = 0 OR (scope = 1 AND owner_char_id = ?) OR (scope = 2 AND owner_preset_id = ?)", *charID, *presetID)
	} else if charID != nil {
		db = db.Where("scope = 0 OR (scope = 1 AND owner_char_id = ?)", *charID)
	} else if presetID != nil {
		db = db.Where("scope = 0 OR (scope = 2 AND owner_preset_id = ?)", *presetID)
	} else {
		db = db.Where("scope = 0")
	}

	if err := db.Order("\"order\" ASC").Find(&scripts).Error; err != nil {
		return nil, err
	}

	return scripts, nil
}

// ExecuteScripts 批量执行脚本
func (s *RegexScriptService) ExecuteScripts(scripts []app.RegexScript, text string, userName string, charName string) string {
	result := text
	for _, script := range scripts {
		executed, err := s.ExecuteScript(&script, result, userName, charName)
		if err != nil {
			global.GVA_LOG.Warn("执行正则脚本失败", zap.String("name", script.Name), zap.Error(err))
			continue
		}
		result = executed
	}
	return result
}
