package outreach

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

	libprompt "haixun-backend/internal/library/prompt"
)

const maxChars = 500

type Draft struct {
	Text      string `json:"text"`
	Angle     string `json:"angle"`
	Rationale string `json:"rationale"`
}

type GenerateResult struct {
	Relevance float64 `json:"relevance"`
	Reason    string  `json:"reason"`
	Drafts    []Draft `json:"drafts"`
}

type GenerateInput struct {
	Persona         string
	TopicLabel      string
	AudienceBrief   string
	ProductBrief    string
	PlacementReason string
	TargetText      string
	AuthorName      string
	Count           int
}

var codeFenceRE = regexp.MustCompile(`(?s)^` + "```(?:json)?\\s*(.*?)\\s*" + "```$")

func BuildUserPrompt(in GenerateInput) (string, error) {
	count := in.Count
	if count <= 0 {
		count = 2
	}
	personaBlock := ""
	if strings.TrimSpace(in.Persona) != "" {
		personaBlock = "人設與語氣：\n" + strings.TrimSpace(in.Persona) + "\n"
	}
	audienceLine := ""
	if strings.TrimSpace(in.AudienceBrief) != "" {
		audienceLine = "受眾與情境：" + strings.TrimSpace(in.AudienceBrief)
	}
	productBrief := strings.TrimSpace(in.ProductBrief)
	if productBrief == "" {
		productBrief = "（尚未填寫品牌與產品，請先給實用建議，不要捏造品牌）"
	}
	reasonLine := ""
	if strings.TrimSpace(in.PlacementReason) != "" {
		reasonLine = "為何適合留言：" + strings.TrimSpace(in.PlacementReason)
	}
	topic := strings.TrimSpace(in.TopicLabel)
	if topic == "" {
		topic = "未指定"
	}
	author := strings.TrimSpace(in.AuthorName)
	if author == "" {
		author = "匿名"
	}
	return libprompt.OutreachPlacementUser(map[string]string{
		"persona_block":         personaBlock,
		"topic_label":           topic,
		"audience_line":         audienceLine,
		"product_brief":         productBrief,
		"placement_reason_line": reasonLine,
		"author_name":           author,
		"target_text":           strings.TrimSpace(in.TargetText),
		"count":                 fmt.Sprintf("%d", count),
	})
}

func ParseGenerateOutput(raw string) (GenerateResult, error) {
	payload, err := extractJSONObject(raw)
	if err != nil {
		return GenerateResult{}, err
	}
	var out GenerateResult
	if err := json.Unmarshal(payload, &out); err != nil {
		return GenerateResult{}, fmt.Errorf("parse outreach json: %w", err)
	}
	if len(out.Drafts) == 0 {
		return GenerateResult{}, fmt.Errorf("outreach drafts missing")
	}
	for i := range out.Drafts {
		out.Drafts[i].Text = trimDraftText(out.Drafts[i].Text)
		if out.Drafts[i].Text == "" {
			return GenerateResult{}, fmt.Errorf("outreach draft %d empty", i+1)
		}
	}
	if out.Relevance < 0 {
		out.Relevance = 0
	}
	if out.Relevance > 1 {
		out.Relevance = 1
	}
	out.Reason = strings.TrimSpace(out.Reason)
	return out, nil
}

func trimDraftText(text string) string {
	text = strings.TrimSpace(text)
	runes := []rune(text)
	if len(runes) > maxChars {
		return string(runes[:maxChars])
	}
	return text
}

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("outreach output missing json object")
	}
	return []byte(raw[start : end+1]), nil
}
