package viral

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

type CopyResearchMap struct {
	AudienceSummary string   `json:"audienceSummary"`
	ContentGoal     string   `json:"contentGoal"`
	Questions       []string `json:"questions"`
	Pillars         []string `json:"pillars"`
	Exclusions      []string `json:"exclusions"`
	SuggestedTags   []string `json:"suggestedTags"`
	BenchmarkNotes  string   `json:"benchmarkNotes"`
}

type CopyResearchMapInput struct {
	Label                  string
	SeedQuery              string
	Brief                  string
	Persona                string
	StyleBenchmark         string
	PersonaAudienceSummary string
	PersonaContentGoal     string
	PersonaQuestions       []string
	PersonaPillars         []string
}

var copyMapFenceRE = regexp.MustCompile("(?s)```(?:json)?\\s*([\\s\\S]*?)```")

func BuildCopyResearchMapSystemPrompt() string {
	return strings.TrimSpace(`你是 Threads 爆款／對標研究顧問。目標是幫創作者找到「高互動、值得仿寫」的參考貼文與對標方向。

規則：
1. audienceSummary：必填，2～4 句描述「受眾是誰」（情境、痛點、會搜什麼）
2. contentGoal 要寫：找到近期互動佳、結構可模仿的爆款貼文，分析 hook／節奏／文案公式
3. pillars：可模仿的內容方向（語錄型、故事型、清單型等），至少 4 個
4. questions：受眾會搜尋／關心的短問題，5+ 個，適合當爆款掃描關鍵字
5. exclusions：不要模仿的內容（業配、純晒照、無結構閒聊等），至少 4 個
6. suggestedTags：2～4 字短詞，10 個左右，用於 Threads 搜尋爆款
7. benchmarkNotes：一句話說明怎樣算「值得仿的爆款」（互動、留言品質、hook 清楚）
8. 繁體中文；只回傳 JSON：audienceSummary, contentGoal, questions, pillars, exclusions, suggestedTags, benchmarkNotes`)
}

func BuildCopyResearchMapUserPrompt(in CopyResearchMapInput) string {
	var b strings.Builder
	b.WriteString("【主題】")
	b.WriteString(strings.TrimSpace(in.Label))
	b.WriteString("\n【種子關鍵字】")
	b.WriteString(strings.TrimSpace(in.SeedQuery))
	b.WriteString("\n【Brief】\n")
	b.WriteString(strings.TrimSpace(in.Brief))
	if p := strings.TrimSpace(in.Persona); p != "" {
		b.WriteString("\n【人設】\n")
		b.WriteString(p)
	}
	if bench := strings.TrimSpace(in.StyleBenchmark); bench != "" {
		b.WriteString("\n【對標帳號】@")
		b.WriteString(strings.TrimPrefix(bench, "@"))
		b.WriteString("\n")
	}
	b.WriteString("\n請產出拷貝忍者研究地圖 JSON。")
	return b.String()
}

func ParseCopyResearchMapOutput(raw string) (CopyResearchMap, error) {
	payload, err := extractCopyMapJSON(raw)
	if err != nil {
		return CopyResearchMap{}, err
	}
	var root map[string]json.RawMessage
	if err := json.Unmarshal(payload, &root); err != nil {
		return CopyResearchMap{}, fmt.Errorf("parse copy research map: %w", err)
	}
	tagObjs := parseFlexibleSuggestedTags(pickRawMessage(root, "suggestedTags", "suggested_tags"))
	tagStrs := make([]string, 0, len(tagObjs))
	for _, item := range tagObjs {
		if item.Tag != "" {
			tagStrs = append(tagStrs, item.Tag)
		}
	}
	out := CopyResearchMap{
		AudienceSummary: firstJSONString(root, "audienceSummary", "audience_summary"),
		ContentGoal:     firstJSONString(root, "contentGoal", "content_goal"),
		BenchmarkNotes:  firstJSONString(root, "benchmarkNotes", "benchmark_notes"),
		Questions:       parseFlexibleStringList(pickRawMessage(root, "questions")),
		Pillars:         parseFlexibleStringList(pickRawMessage(root, "pillars")),
		Exclusions:      parseFlexibleStringList(pickRawMessage(root, "exclusions")),
		SuggestedTags:   cleanLines(tagStrs),
	}
	if strings.TrimSpace(out.AudienceSummary) == "" {
		return CopyResearchMap{}, fmt.Errorf("copy research map missing audienceSummary")
	}
	return out, nil
}

func ToEntityResearchMap(m CopyResearchMap) map[string]any {
	return map[string]any{
		"audience_summary": m.AudienceSummary,
		"content_goal":     m.ContentGoal,
		"questions":        m.Questions,
		"pillars":          m.Pillars,
		"exclusions":       m.Exclusions,
		"suggested_tags":   m.SuggestedTags,
		"benchmark_notes":  m.BenchmarkNotes,
	}
}

func cleanLines(items []string) []string {
	out := make([]string, 0, len(items))
	seen := map[string]struct{}{}
	for _, item := range items {
		item = strings.TrimSpace(item)
		if item == "" {
			continue
		}
		if _, ok := seen[item]; ok {
			continue
		}
		seen[item] = struct{}{}
		out = append(out, item)
	}
	return out
}

func extractCopyMapJSON(raw string) ([]byte, error) {
	raw = strings.TrimSpace(raw)
	if m := copyMapFenceRE.FindStringSubmatch(raw); len(m) == 2 {
		raw = strings.TrimSpace(m[1])
	}
	start := strings.Index(raw, "{")
	end := strings.LastIndex(raw, "}")
	if start < 0 || end <= start {
		return nil, fmt.Errorf("copy research map missing json")
	}
	return []byte(raw[start : end+1]), nil
}
