package usecase

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

	"apps/backend/internal/module/studio/domain"
)

// 8D keys — 與前端 DIM_ORDER 對齊
var dimKeys = []string{
	"d1Tone", "d2Structure", "d3Interaction", "d4Topics",
	"d5Rhythm", "d6Visual", "d7Conversion", "d8Risk",
}

func splitSamples(raw string, max int) []string {
	parts := regexp.MustCompile(`\n\s*---\s*\n|\n{3,}`).Split(raw, -1)
	out := make([]string, 0, max)
	for _, p := range parts {
		p = strings.TrimSpace(p)
		if utf8.RuneCountInString(p) < 8 {
			continue
		}
		out = append(out, p)
		if max > 0 && len(out) >= max {
			break
		}
	}
	if len(out) == 0 && strings.TrimSpace(raw) != "" {
		out = append(out, strings.TrimSpace(raw))
	}
	return out
}

func pickTone(blob string) string {
	switch {
	case regexp.MustCompile(`哈哈|笑死|哭|靠|真的假的`).MatchString(blob):
		return "輕鬆吐槽、情緒外露"
	case regexp.MustCompile(`建議|成分|數據|對比|規格`).MatchString(blob):
		return "務實、有依據、少廢話"
	case regexp.MustCompile(`懂|抱抱|辛苦|没关系|沒關係|一起`).MatchString(blob):
		return "共感、溫柔、像朋友"
	default:
		return "口語親近、不端著"
	}
}

func pickHooks(blob string) string {
	switch {
	case strings.ContainsAny(blob, "？?"):
		return "用真心疑問開場，邀請別人補經驗"
	case regexp.MustCompile(`有人|大家|求`).MatchString(blob):
		return "先丟痛點再求推坑／經驗"
	default:
		return "先講自己卡關的情境，再拋問題"
	}
}

func pickEvidence(samples []string, max int) []string {
	out := make([]string, 0, max)
	for _, s := range samples {
		r := []rune(s)
		if len(r) > 36 {
			s = string(r[:36]) + "…"
		}
		out = append(out, s)
		if len(out) >= max {
			break
		}
	}
	return out
}

// serializeDraftText — 與前端 fingerprint 格式一致（【標籤】）
func serializeDraftText(d domain.PersonaDraft) string {
	var lines []string
	push := func(label, value string) {
		v := strings.TrimSpace(value)
		if v == "" {
			return
		}
		lines = append(lines, "【"+label+"】\n"+v)
	}
	push("我是誰", d.Identity)
	push("語氣", d.Tone)
	push("對誰說", d.Audience)
	push("開場鉤子", d.Hooks)
	push("語言指紋", d.LanguageFingerprint)
	push("節奏", d.Rhythm)
	push("標點", d.Punctuation)
	push("內容套路", d.ContentPatterns)
	push("知識轉譯", d.KnowledgeTranslation)
	push("CTA", d.CtaStyle)
	push("像他會說的話", d.Examples)
	push("絕不怎麼說", d.Avoid)
	return strings.Join(lines, "\n\n")
}

func buildDimensions(draft domain.PersonaDraft, samples []string, dims map[string]domain.StyleDimension) map[string]domain.StyleDimension {
	ev := pickEvidence(samples, 2)
	out := make(map[string]domain.StyleDimension, 8)
	// 規則底稿
	defaults := map[string]string{
		"d1Tone":        draft.Tone,
		"d2Structure":   draft.ContentPatterns,
		"d3Interaction": joinNonEmpty("；", draft.Hooks, draft.CtaStyle),
		"d4Topics":      joinNonEmpty("；", draft.Audience, "常談生活／實用經驗"),
		"d5Rhythm":      draft.Rhythm,
		"d6Visual":      draft.Punctuation,
		"d7Conversion":  draft.CtaStyle,
		"d8Risk":        draft.Avoid,
	}
	for _, k := range dimKeys {
		sum := ""
		var evidence []string
		if dims != nil {
			if d, ok := dims[k]; ok {
				sum = strings.TrimSpace(d.Summary)
				evidence = d.Evidence
			}
		}
		if sum == "" {
			sum = strings.TrimSpace(defaults[k])
		}
		if sum == "" {
			sum = "（待補）"
		}
		if len(evidence) == 0 {
			evidence = append([]string(nil), ev...)
		}
		out[k] = domain.StyleDimension{Summary: sum, Evidence: evidence}
	}
	return out
}

func joinNonEmpty(sep string, parts ...string) string {
	var ok []string
	for _, p := range parts {
		p = strings.TrimSpace(p)
		if p != "" {
			ok = append(ok, p)
		}
	}
	return strings.Join(ok, sep)
}

func splitAvoidList(avoid string) []string {
	var out []string
	for _, a := range regexp.MustCompile(`[、,，;；/｜|]`).Split(avoid, -1) {
		a = strings.TrimSpace(a)
		if a != "" {
			out = append(out, a)
		}
	}
	if len(out) == 0 {
		return []string{"硬銷", "客服腔", "條列說教", "AI 腔"}
	}
	return out
}

func isPlaceholderName(name string) bool {
	n := strings.TrimSpace(name)
	if n == "" {
		return true
	}
	// 前端新建預設／測試名
	switch n {
	case "新人設", "新身份", "未命名人設", "未命名", "分析", "對標", "新", "New persona", "Untitled":
		return true
	}
	return strings.HasPrefix(n, "人設 ") || strings.HasPrefix(n, "persona")
}

func shortNameFromIdentity(identity, fallback string) string {
	id := strings.TrimSpace(identity)
	if id == "" {
		return fallback
	}
	// 取第一句或前 16 字
	if i := strings.IndexAny(id, "，,。；;"); i > 0 && i <= 24 {
		id = id[:i]
	}
	r := []rune(id)
	if len(r) > 16 {
		id = string(r[:16])
	}
	id = strings.TrimSpace(id)
	if id == "" {
		return fallback
	}
	return id
}

// finalizePersonaAnalysis 寫入概要 + 指紋 + 8D（分析完成後一律覆寫可分析欄位）
func finalizePersonaAnalysis(
	p *domain.Persona,
	draft domain.PersonaDraft,
	samples []string,
	source, username, sourceLabel, brief, nameHint string,
	dims map[string]domain.StyleDimension,
) {
	now := domain.NowNano()
	p.Status = domain.PersonaReady
	p.Style.Source = source
	p.Style.BenchmarkUsername = username
	p.Style.SourceLabel = sourceLabel
	p.Style.SampleCount = len(samples)
	p.Style.AnalyzedAt = now
	p.Style.SamplePreviews = make([]string, 0, min(5, len(samples)))
	for i, s := range samples {
		if i >= 5 {
			break
		}
		r := []rune(s)
		if len(r) > 100 {
			s = string(r[:100]) + "…"
		}
		p.Style.SamplePreviews = append(p.Style.SamplePreviews, s)
	}
	p.Style.Draft = draft
	p.Style.DraftText = serializeDraftText(draft)
	p.Style.Dimensions = buildDimensions(draft, samples, dims)
	p.Voice = draft.Tone

	// 概要 brief：分析後一律填（LLM brief 優先）
	brief = strings.TrimSpace(brief)
	if brief == "" {
		brief = joinNonEmpty("。", draft.Identity, draft.Tone+"；對「"+draft.Audience+"」說話")
	}
	p.Brief = brief

	// 名稱：仍是佔位名時用 identity / hint 補上
	if isPlaceholderName(p.Name) {
		hint := strings.TrimSpace(nameHint)
		if hint == "" {
			hint = shortNameFromIdentity(draft.Identity, p.Name)
		}
		if hint != "" && !isPlaceholderName(hint) {
			p.Name = hint
		} else if draft.Identity != "" {
			p.Name = shortNameFromIdentity(draft.Identity, "寫作人設")
		}
	}

	// 護欄：分析後覆寫 avoid / 預設字數 / 禁 AI 腔
	p.Guard.BanAiTone = true
	if p.Guard.MaxChars == 0 {
		p.Guard.MaxChars = 280
	}
	if draft.Avoid != "" {
		p.Guard.Avoid = splitAvoidList(draft.Avoid)
	} else if len(p.Guard.Avoid) == 0 {
		p.Guard.Avoid = []string{"硬銷", "客服腔", "條列說教", "AI 腔"}
	}

	// notes 放分析來源摘要（不覆寫使用者手動長註？分析後可寫來源）
	if source == "benchmark" && username != "" {
		p.Notes = fmt.Sprintf("對標 @%s · 樣本 %d 則 · 已寫入概要／8D／指紋", username, len(samples))
	} else if sourceLabel != "" {
		p.Notes = fmt.Sprintf("來源：%s · 樣本 %d 則 · 已寫入概要／8D／指紋", sourceLabel, len(samples))
	}

	p.UpdatedAt = now
}

// applyStyleFromSamples fills persona style from real post samples (中文啟發式，可接 AI 再精修).
func applyStyleFromSamples(p *domain.Persona, samples []string, source string, username, sourceLabel string) {
	blob := strings.Join(samples, "\n")
	tone := pickTone(blob)
	hooks := pickHooks(blob)

	identity := p.Style.Draft.Identity
	if identity == "" {
		identity = p.Name
	}
	if isPlaceholderName(identity) {
		identity = "生活觀察者"
	}
	if source == "benchmark" && username != "" {
		identity = fmt.Sprintf("像 @%s 的發文者：%s", username, tone)
	}
	audience := p.Style.Draft.Audience
	if audience == "" {
		audience = "同溫層、會在 Threads 求經驗的人"
	}
	langFP := "短句、口語、少成語"
	if regexp.MustCompile(`有時候|後來|其實|講真的`).MatchString(blob) {
		langFP = "常用「有時候／後來／其實」當轉折"
	}
	rhythm := "一段講完再補一句問句"
	if strings.Contains(blob, "\n") {
		rhythm = "2～4 個短段落，段間空行"
	}
	punct := "逗號與問號為主，少驚嘆"
	if strings.Contains(blob, "…") || strings.Contains(blob, "...") {
		punct = "愛用省略號與問號"
	}
	avoid := "硬銷、條列教學、客服腔、假裝中立實則業配、AI 腔"
	ex := ""
	if len(samples) > 0 {
		r := []rune(samples[0])
		if len(r) > 80 {
			ex = string(r[:80])
		} else {
			ex = samples[0]
		}
	}

	draft := domain.PersonaDraft{
		Identity:             identity,
		Tone:                 tone,
		Audience:             audience,
		Hooks:                hooks,
		LanguageFingerprint:  langFP,
		Rhythm:               rhythm,
		Punctuation:          punct,
		ContentPatterns:      "情境 → 感受或觀察 → 輕問一句",
		KnowledgeTranslation: "先講使用情境，再講重點，不丟術語牆",
		CtaStyle:             "自然邀留言（「你們呢」「有人也…嗎」），不命令",
		Examples:             ex,
		Avoid:                avoid,
	}
	brief := fmt.Sprintf("%s。語氣%s，主要對「%s」說話。", identity, tone, audience)
	finalizePersonaAnalysis(p, draft, samples, source, username, sourceLabel, brief, "", nil)
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

type aiDimJSON struct {
	Summary  string   `json:"summary"`
	Evidence []string `json:"evidence"`
}

type aiStyleJSON struct {
	Identity             string               `json:"identity"`
	Tone                 string               `json:"tone"`
	Audience             string               `json:"audience"`
	Hooks                string               `json:"hooks"`
	LanguageFingerprint  string               `json:"language_fingerprint"`
	Rhythm               string               `json:"rhythm"`
	Punctuation          string               `json:"punctuation"`
	ContentPatterns      string               `json:"content_patterns"`
	KnowledgeTranslation string               `json:"knowledge_translation"`
	CtaStyle             string               `json:"cta_style"`
	Examples             string               `json:"examples"`
	Avoid                string               `json:"avoid"`
	Brief                string               `json:"brief"`
	Name                 string               `json:"name"`
	Dimensions           map[string]aiDimJSON `json:"dimensions"`
	// 扁平 8D 後備
	D1Tone        aiDimJSON `json:"d1Tone"`
	D2Structure   aiDimJSON `json:"d2Structure"`
	D3Interaction aiDimJSON `json:"d3Interaction"`
	D4Topics      aiDimJSON `json:"d4Topics"`
	D5Rhythm      aiDimJSON `json:"d5Rhythm"`
	D6Visual      aiDimJSON `json:"d6Visual"`
	D7Conversion  aiDimJSON `json:"d7Conversion"`
	D8Risk        aiDimJSON `json:"d8Risk"`
}

func buildStyleAnalysisPrompt(samples []string, username, sourceLabel string) string {
	var b strings.Builder
	b.WriteString("你是 Threads 寫作風格分析師。根據下列貼文樣本，用繁體中文（台灣用語）完整分析作者的寫作人設。\n")
	if username != "" {
		b.WriteString("對標帳號：@")
		b.WriteString(username)
		b.WriteString("\n")
	}
	if sourceLabel != "" {
		b.WriteString("來源標籤：")
		b.WriteString(sourceLabel)
		b.WriteString("\n")
	}
	b.WriteString("\n【貼文樣本】\n")
	for i, s := range samples {
		b.WriteString(fmt.Sprintf("--- 樣本 %d ---\n%s\n", i+1, s))
	}
	b.WriteString(`
【輸出要求】
只輸出一個 JSON 物件（不要 markdown 代碼塊、不要前言）。必須填滿所有欄位，不可留空字串。
{
  "name": "簡短人設名稱（4～12 字，不要帳號）",
  "brief": "兩到三句定位：是誰、對誰說、核心訊息",
  "identity": "發文身份／角色（一句完整描述，不要只寫帳號名）",
  "tone": "語氣（具體、可模仿）",
  "audience": "主要對誰說話",
  "hooks": "常見開場鉤子手法",
  "language_fingerprint": "用詞／口頭禪／轉折特徵",
  "rhythm": "段落節奏、長短句",
  "punctuation": "標點與 emoji 習慣",
  "content_patterns": "內容結構模式",
  "knowledge_translation": "如何把知識／經驗講成人話",
  "cta_style": "如何邀互動／收尾",
  "examples": "摘一句最像他的原句或改寫示範",
  "avoid": "模仿時要避開的寫法（可用頓號分隔多項）",
  "dimensions": {
    "d1Tone": { "summary": "D1 語氣人格一句話", "evidence": ["樣本短摘"] },
    "d2Structure": { "summary": "D2 結構模板", "evidence": [] },
    "d3Interaction": { "summary": "D3 互動方式", "evidence": [] },
    "d4Topics": { "summary": "D4 主題分布", "evidence": [] },
    "d5Rhythm": { "summary": "D5 發文節奏", "evidence": [] },
    "d6Visual": { "summary": "D6 視覺語法（換行／emoji）", "evidence": [] },
    "d7Conversion": { "summary": "D7 轉換／邀約方式", "evidence": [] },
    "d8Risk": { "summary": "D8 風險紅線", "evidence": [] }
  }
}
`)
	return b.String()
}

func collectDimsFromAI(parsed aiStyleJSON) map[string]domain.StyleDimension {
	out := make(map[string]domain.StyleDimension)
	put := func(k string, d aiDimJSON) {
		sum := strings.TrimSpace(d.Summary)
		if sum == "" {
			return
		}
		out[k] = domain.StyleDimension{Summary: sum, Evidence: d.Evidence}
	}
	if parsed.Dimensions != nil {
		for _, k := range dimKeys {
			if d, ok := parsed.Dimensions[k]; ok {
				put(k, d)
			}
		}
	}
	// 扁平後備
	put("d1Tone", parsed.D1Tone)
	put("d2Structure", parsed.D2Structure)
	put("d3Interaction", parsed.D3Interaction)
	put("d4Topics", parsed.D4Topics)
	put("d5Rhythm", parsed.D5Rhythm)
	put("d6Visual", parsed.D6Visual)
	put("d7Conversion", parsed.D7Conversion)
	put("d8Risk", parsed.D8Risk)
	return out
}

// applyAIStyleJSON parses LLM JSON into persona style. Returns false if unusable.
func applyAIStyleJSON(p *domain.Persona, raw string, samples []string, source, username, sourceLabel string) bool {
	raw = strings.TrimSpace(raw)
	if raw == "" {
		return false
	}
	// strip ```json fences if any
	if i := strings.Index(raw, "{"); i >= 0 {
		if j := strings.LastIndex(raw, "}"); j > i {
			raw = raw[i : j+1]
		}
	}
	var parsed aiStyleJSON
	if err := json.Unmarshal([]byte(raw), &parsed); err != nil {
		return false
	}
	// need at least tone or identity
	if strings.TrimSpace(parsed.Tone) == "" && strings.TrimSpace(parsed.Identity) == "" {
		return false
	}
	coalesce := func(v, fallback string) string {
		v = strings.TrimSpace(v)
		if v == "" {
			return fallback
		}
		return v
	}
	base := p.Style.Draft
	draft := domain.PersonaDraft{
		Identity:             coalesce(parsed.Identity, base.Identity),
		Tone:                 coalesce(parsed.Tone, base.Tone),
		Audience:             coalesce(parsed.Audience, base.Audience),
		Hooks:                coalesce(parsed.Hooks, base.Hooks),
		LanguageFingerprint:  coalesce(parsed.LanguageFingerprint, base.LanguageFingerprint),
		Rhythm:               coalesce(parsed.Rhythm, base.Rhythm),
		Punctuation:          coalesce(parsed.Punctuation, base.Punctuation),
		ContentPatterns:      coalesce(parsed.ContentPatterns, base.ContentPatterns),
		KnowledgeTranslation: coalesce(parsed.KnowledgeTranslation, base.KnowledgeTranslation),
		CtaStyle:             coalesce(parsed.CtaStyle, base.CtaStyle),
		Examples:             coalesce(parsed.Examples, base.Examples),
		Avoid:                coalesce(parsed.Avoid, base.Avoid),
	}
	// reject empty-shell identity like "有趣人設 · 對標 @x"
	if strings.Contains(draft.Identity, "· 對標 @") || strings.Contains(draft.Identity, "· 对标 @") {
		if parsed.Tone != "" {
			draft.Identity = "像 @" + username + " 的發文者：" + parsed.Tone
		}
	}
	// 補齊仍空的 draft 欄位（規則底稿）
	if draft.Audience == "" {
		draft.Audience = "同溫層、會在 Threads 求經驗的人"
	}
	if draft.Hooks == "" {
		draft.Hooks = pickHooks(strings.Join(samples, "\n"))
	}
	if draft.LanguageFingerprint == "" {
		draft.LanguageFingerprint = "短句、口語、少成語"
	}
	if draft.Rhythm == "" {
		draft.Rhythm = "2～4 個短段落，段間空行"
	}
	if draft.Punctuation == "" {
		draft.Punctuation = "逗號與問號為主"
	}
	if draft.ContentPatterns == "" {
		draft.ContentPatterns = "情境 → 感受或觀察 → 輕問一句"
	}
	if draft.KnowledgeTranslation == "" {
		draft.KnowledgeTranslation = "先講使用情境，再講重點"
	}
	if draft.CtaStyle == "" {
		draft.CtaStyle = "自然邀留言，不命令"
	}
	if draft.Avoid == "" {
		draft.Avoid = "硬銷、客服腔、AI 腔"
	}
	if draft.Examples == "" && len(samples) > 0 {
		r := []rune(samples[0])
		if len(r) > 80 {
			draft.Examples = string(r[:80])
		} else {
			draft.Examples = samples[0]
		}
	}

	dims := collectDimsFromAI(parsed)
	finalizePersonaAnalysis(p, draft, samples, source, username, sourceLabel, parsed.Brief, parsed.Name, dims)
	return true
}
