package outreach

import (
	"context"
	"strings"

	app "haixun-backend/internal/library/errors"
	"haixun-backend/internal/library/errors/code"
	libprompt "haixun-backend/internal/library/prompt"
	domai "haixun-backend/internal/model/ai/domain/usecase"
	aiusecase "haixun-backend/internal/model/ai/usecase"
	branddomain "haixun-backend/internal/model/brand/domain/usecase"
	kgdomain "haixun-backend/internal/model/knowledge_graph/domain/usecase"
	outreachusecase "haixun-backend/internal/model/outreach_draft/domain/usecase"
	personadomain "haixun-backend/internal/model/persona/domain/usecase"
	scanpostentity "haixun-backend/internal/model/scan_post/domain/entity"
	scanpostusecase "haixun-backend/internal/model/scan_post/domain/usecase"
	threadsaccountdomain "haixun-backend/internal/model/threads_account/domain/usecase"
)

type GenerateDraftRequest struct {
	TenantID       string
	OwnerUID       string
	BrandID        string
	TopicID        string
	ScanPostID     string
	Count          int
	VoicePersonaID string
	ProductID      string
	Regenerate     bool
}

type GenerateDraftDeps struct {
	Brand          branddomain.UseCase
	Persona        personadomain.UseCase
	ScanPost       scanpostusecase.UseCase
	KnowledgeGraph kgdomain.UseCase
	ThreadsAccount threadsaccountdomain.UseCase
	AI             aiusecase.UseCase
	OutreachDraft  outreachusecase.UseCase
}

func GenerateDraft(ctx context.Context, deps GenerateDraftDeps, req GenerateDraftRequest) (*outreachusecase.DraftSummary, error) {
	scanPostID := strings.TrimSpace(req.ScanPostID)
	count := req.Count
	if count <= 0 {
		count = 2
	}
	if count > 4 {
		count = 4
	}
	if scanPostID == "" {
		return nil, app.For(code.Brand).InputMissingRequired("scan_post_id is required")
	}

	brand, err := deps.Brand.Get(ctx, req.TenantID, req.OwnerUID, req.BrandID)
	if err != nil {
		return nil, err
	}
	voiceID := strings.TrimSpace(req.VoicePersonaID)
	if voiceID == "" {
		return nil, app.For(code.Brand).InputMissingRequired("voice_persona_id is required")
	}
	vp, err := deps.Persona.Get(ctx, req.TenantID, req.OwnerUID, voiceID)
	if err != nil {
		return nil, err
	}
	voicePersona := FormatVoicePersonaBlock(vp)
	if voicePersona == "" {
		return nil, app.For(code.Persona).InputMissingRequired("請先在人設頁填寫人設描述或完成 8D 風格分析")
	}
	preferredProductID := strings.TrimSpace(req.ProductID)
	if preferredProductID == "" {
		preferredProductID = strings.TrimSpace(brand.ProductID)
	}
	post, err := deps.ScanPost.Get(ctx, req.TenantID, req.OwnerUID, req.BrandID, scanPostID)
	if err != nil {
		return nil, err
	}
	pickedProduct := PickProductForOutreach(brand, preferredProductID, post.SearchTag, post.Text)
	productID := preferredProductID
	if pickedProduct != nil {
		productID = pickedProduct.ID
	}

	topicLabel := strings.TrimSpace(post.SearchTag)
	placementReason := BuildPlacementReason(post)
	if graph, graphErr := deps.KnowledgeGraph.Get(ctx, req.TenantID, req.OwnerUID, req.BrandID); graphErr == nil && graph != nil {
		if node := FindGraphNode(graph.Nodes, post.GraphNodeID); node != nil {
			if strings.TrimSpace(node.Label) != "" {
				topicLabel = strings.TrimSpace(node.Label)
			}
			placementReason = MergePlacementReason(placementReason, node, post)
		}
	}

	regenerate := req.Regenerate
	previousHint := ""
	if latest, latestErr := deps.OutreachDraft.GetLatestByScanPost(
		ctx, req.TenantID, req.OwnerUID, req.BrandID, strings.TrimSpace(req.TopicID), scanPostID,
	); latestErr == nil && latest != nil && len(latest.Drafts) > 0 {
		regenerate = true
		previousHint = FormatPreviousDraftsHint(latest)
	}

	userPrompt, err := BuildUserPrompt(GenerateInput{
		Persona:            voicePersona,
		TopicLabel:         topicLabel,
		AudienceBrief:      brand.TargetAudience,
		ProductBrief:       ProductBriefForBrand(brand, productID, post.SearchTag, post.Text),
		PlacementReason:    placementReason,
		TargetText:         post.Text,
		AuthorName:         post.Author,
		Count:              count,
		Regenerate:         regenerate,
		PreviousDraftsHint: previousHint,
	})
	if err != nil {
		return nil, app.For(code.AI).SysInternal("outreach user prompt load failed")
	}

	systemPrompt, err := libprompt.OutreachPlacementSystem()
	if err != nil {
		return nil, app.For(code.AI).SysInternal("outreach system prompt load failed")
	}
	systemPrompt = strings.TrimSpace(systemPrompt) + "\n\n【最高優先】以下人設與語氣是硬約束，每則留言都必須像這個角色親自回覆，不得變成通用網友口吻：\n" + strings.TrimSpace(voicePersona)

	credential, err := deps.ThreadsAccount.ResolveMemberAiCredential(ctx, req.TenantID, req.OwnerUID)
	if err != nil {
		return nil, err
	}
	providerID, err := aiusecase.MapWorkerProvider(credential.Provider)
	if err != nil {
		return nil, err
	}

	temp := 0.88
	result, err := deps.AI.GenerateText(ctx, domai.GenerateRequest{
		Provider: providerID,
		Model:    credential.Model,
		Credential: domai.Credential{
			APIKey: credential.APIKey,
		},
		System: systemPrompt,
		Messages: []domai.Message{
			{Role: "user", Content: userPrompt},
		},
		Temperature: &temp,
	})
	if err != nil {
		return nil, err
	}

	parsed, err := ParseGenerateOutput(result.Text)
	if err != nil {
		return nil, app.For(code.AI).SvcThirdParty("獲客留言 LLM 回傳無法解析：" + err.Error())
	}

	draftItems := make([]outreachusecase.DraftItem, 0, len(parsed.Drafts))
	for _, item := range parsed.Drafts {
		draftItems = append(draftItems, outreachusecase.DraftItem{
			Text:      item.Text,
			Angle:     item.Angle,
			Rationale: item.Rationale,
		})
	}
	saved, err := deps.OutreachDraft.Create(ctx, outreachusecase.CreateRequest{
		TenantID:   req.TenantID,
		OwnerUID:   req.OwnerUID,
		BrandID:    req.BrandID,
		TopicID:    strings.TrimSpace(req.TopicID),
		ScanPostID: scanPostID,
		Relevance:  parsed.Relevance,
		Reason:     parsed.Reason,
		Drafts:     draftItems,
	})
	if err != nil {
		return nil, err
	}
	_, _ = deps.ScanPost.UpdateOutreach(ctx, scanpostusecase.UpdateOutreachRequest{
		TenantID: req.TenantID,
		OwnerUID: req.OwnerUID,
		BrandID:  req.BrandID,
		PostID:   scanPostID,
		Status:   scanpostentity.OutreachStatusDrafted,
	})
	return saved, nil
}
