package viral

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

	"haixun-backend/internal/library/copyvoice"
	"haixun-backend/internal/library/threadspost"
)

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

type ReplicateInput struct {
	TopicLabel        string
	TopicBrief        string
	Persona           string
	StyleProfile      string
	OriginalText      string
	AuthorName        string
	StructureAnalysis string
	NarrativeSeed     string
}

type ReplicateResult struct {
	Angle          string `json:"angle"`
	Hook           string `json:"hook"`
	Text           string `json:"text"`
	Rationale      string `json:"rationale"`
	StructureNotes string `json:"structureNotes"`
}

func BuildSystemPrompt() string {
	return strings.TrimSpace(`你是 Threads / Instagram 創作者。讀完參考爆文後，用指定人設的語言指紋寫一篇全新貼文。

` + copyvoice.SystemRules() + `

輸出：
- text 主文 ≤ 500 字（Threads API 硬上限，含 #話題標籤）
- 參考原文只學情緒力度、資訊密度與節奏；最後必須換成人設會講出口的話
- 篇幅、行數、標點與換行優先遵守人設語言指紋；沒有指紋時才貼近參考原文
- text 排版要像真人手機發文：可使用自然標點、空行、列點與少量 emoji；不要完全無標點，也不要每句硬換行，除非人設樣本就是這樣
- 只回傳一個 JSON 物件，欄位：angle, hook, text, rationale, structureNotes`)
}

func BuildUserPrompt(input ReplicateInput) string {
	var b strings.Builder
	b.WriteString("主題：")
	b.WriteString(strings.TrimSpace(input.TopicLabel))
	b.WriteString("\n")
	if brief := strings.TrimSpace(input.TopicBrief); brief != "" {
		b.WriteString("Brief：")
		b.WriteString(brief)
		b.WriteString("\n")
	}
	if persona := strings.TrimSpace(input.Persona); persona != "" {
		b.WriteString("\n人設定位、語言指紋與禁忌（最高優先）：\n")
		b.WriteString(persona)
		b.WriteString("\n")
		b.WriteString(copyvoice.PersonaCalibrationNote())
		b.WriteString("\n")
	}
	if style := strings.TrimSpace(input.StyleProfile); style != "" {
		b.WriteString("\n8D 風格策略：\n")
		b.WriteString(style)
		b.WriteString("\n")
	}
	if analysis := strings.TrimSpace(input.StructureAnalysis); analysis != "" {
		b.WriteString("\n敘事分析（只學故事感，不要套句型或段落公式）：\n")
		b.WriteString(analysis)
		b.WriteString("\n")
	}
	author := strings.TrimSpace(input.AuthorName)
	if author == "" {
		author = "匿名"
	}
	original := strings.TrimSpace(input.OriginalText)
	if note := threadspost.MimicLengthGuidance(original); note != "" {
		b.WriteString("\n")
		b.WriteString(note)
		b.WriteString("\n")
	}
	b.WriteString("\n原文參考（@")
	b.WriteString(author)
	b.WriteString("，只感受情緒、敘事與篇幅，禁止抄句型）：\n")
	b.WriteString(original)
	b.WriteString("\n\n本次敘事切入：")
	b.WriteString(copyvoice.NarrativeLens(input.NarrativeSeed))
	b.WriteString("\n\n請產出一篇可發布的全新貼文 JSON。hook 欄位只放內部分析，不要把模板句型寫進 text。")
	return b.String()
}

func ParseReplicateOutput(raw string) (ReplicateResult, error) {
	payload, err := extractJSONObject(raw)
	if err != nil {
		return ReplicateResult{}, err
	}
	var out ReplicateResult
	if err := json.Unmarshal(payload, &out); err != nil {
		return ReplicateResult{}, fmt.Errorf("parse viral replica json: %w", err)
	}
	out.Text = threadspost.FormatDraftText(out.Text)
	if out.Text == "" {
		return ReplicateResult{}, fmt.Errorf("replica text missing")
	}
	out.Angle = strings.TrimSpace(out.Angle)
	out.Hook = strings.TrimSpace(out.Hook)
	out.Rationale = strings.TrimSpace(out.Rationale)
	out.StructureNotes = strings.TrimSpace(out.StructureNotes)
	return out, nil
}

func extractJSONObject(raw string) ([]byte, error) {
	raw = strings.TrimSpace(raw)
	if m := codeFenceRE.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("replica output missing json object")
	}
	return []byte(raw[start : end+1]), nil
}
