package service

import (
	"fmt"
	"html"
	"strings"

	"metazone.cc/metalab/internal/model"
)

// =============================================================================
// ShortcodeService — Markdown shortcode 解析与渲染
//
// 职责：
//   1. 从 Markdown body 中提取所有 [zone:type:params] 语法
//   2. 将合法的 shortcode 替换为前端可识别的占位 HTML（<div class="zone-card">）
//   3. 未注册的 shortcode 保持不变（不报错，避免破坏用户输入）
//
// 扩展新类型：
//   1. 在 model/shortcode.go 中注册新 ShortcodeType 常量
//   2. 在本文件的 renderPlaceholder() 中添加 case 分支
//   3. 在 templates/.../shortcode.js 中添加前端卡片渲染逻辑
// =============================================================================

// ShortcodeService 无状态，纯函数集合
type ShortcodeService struct{}

// NewShortcodeService 构造函数
func NewShortcodeService() *ShortcodeService {
	return &ShortcodeService{}
}

// =============================================================================
// 主入口：解析 + 替换
// =============================================================================

// Process 从 MD body 中提取 shortcode 并替换为占位 HTML
// 返回的 ProcessedBody 可直接传给模板渲染（Go template 会自动对 HTML 实体转义，
// 因此占位 div 需要用 template.HTML 类型标记为安全）
func (s *ShortcodeService) Process(body string) model.ShortcodeResult {
	result := model.ShortcodeResult{
		ProcessedBody: body,
	}

	// 一次遍历替换所有合法的 shortcode
	result.ProcessedBody = model.ShortcodePattern.ReplaceAllStringFunc(body, func(match string) string {
		sc, ok := parseShortcode(match)
		if !ok {
			return match // 格式不合法，保留原文
		}

		if !model.IsValidType(string(sc.Type)) {
			return match // 类型未注册，保留原文
		}

		// 生成占位 HTML
		placeholder := renderPlaceholder(sc)
		if placeholder == "" {
			return match
		}

		result.Shortcodes = append(result.Shortcodes, sc)
		return placeholder
	})

	return result
}

// =============================================================================
// 解析
// =============================================================================

// parseShortcode 从 "[zone:type:params]" 字符串中解析出 Shortcode
// 返回 false 表示格式不合法
func parseShortcode(raw string) (model.Shortcode, bool) {
	matches := model.ShortcodePattern.FindStringSubmatch(raw)
	if len(matches) != 3 {
		return model.Shortcode{}, false
	}

	sc := model.Shortcode{
		Raw:    raw,
		Type:   model.ShortcodeType(matches[1]),
		Params: strings.TrimSpace(matches[2]),
	}

	if sc.Params == "" {
		return model.Shortcode{}, false
	}

	return sc, true
}

// =============================================================================
// 占位 HTML 渲染器（每个类型一个 case）
// =============================================================================

// renderPlaceholder 根据 shortcode 类型生成前端占位 HTML
//
// 占位 div 结构约定：
//   <div class="zone-card" data-zone-type="类型" data-zone-id="参数"
//        data-zone-extra="附加信息">
//     <span class="zone-card-loading">卡片加载中...</span>
//   </div>
//
// 前端 shortcode.js 根据 data-zone-* 属性决定如何渲染。
// "加载中..." 文本会被 shortcode.js 在渲染时替换，仅作降级显示。
func renderPlaceholder(sc model.Shortcode) string {
	switch sc.Type {
	case model.ShortcodeEvent:
		// [zone:event:活动ID]
		// 前端根据活动ID获取标题、时间、封面等信息
		return fmt.Sprintf(
			`<div class="zone-card" data-zone-type="event" data-zone-id="%s"><span class="zone-card-loading">活动卡片加载中...</span></div>`,
			html.EscapeString(sc.Params),
		)

	case model.ShortcodeGame:
		// [zone:game:游戏slug]
		// 前端根据游戏slug获取名称、封面、标签等信息
		return fmt.Sprintf(
			`<div class="zone-card" data-zone-type="game" data-zone-id="%s"><span class="zone-card-loading">游戏卡片加载中...</span></div>`,
			html.EscapeString(sc.Params),
		)

	case model.ShortcodePoll:
		// [zone:poll:投票ID] ※预留
		return fmt.Sprintf(
			`<div class="zone-card" data-zone-type="poll" data-zone-id="%s"><span class="zone-card-loading">投票组件加载中...</span></div>`,
			html.EscapeString(sc.Params),
		)

	case model.ShortcodeResource:
		// [zone:resource:资源ID] ※预留
		return fmt.Sprintf(
			`<div class="zone-card" data-zone-type="resource" data-zone-id="%s"><span class="zone-card-loading">资源卡片加载中...</span></div>`,
			html.EscapeString(sc.Params),
		)

	default:
		return ""
	}
}
