package threads_account

import (
	"context"
	"fmt"
	"strings"

	app "haixun-backend/internal/library/errors"
	"haixun-backend/internal/library/errors/code"
	libformula "haixun-backend/internal/library/formula"
	liboutreach "haixun-backend/internal/library/outreach"
	libownpost "haixun-backend/internal/library/ownpost"
	libviral "haixun-backend/internal/library/viral"
	domai "haixun-backend/internal/model/ai/domain/usecase"
	aiusecase "haixun-backend/internal/model/ai/usecase"
	cfentity "haixun-backend/internal/model/content_formula/domain/entity"
	cfdom "haixun-backend/internal/model/content_formula/domain/usecase"
	"haixun-backend/internal/svc"
	"haixun-backend/internal/types"
)

type AnalyzeContentFormulaLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewAnalyzeContentFormulaLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AnalyzeContentFormulaLogic {
	return &AnalyzeContentFormulaLogic{ctx: ctx, svcCtx: svcCtx}
}

func (l *AnalyzeContentFormulaLogic) AnalyzeContentFormula(req *types.AnalyzeContentFormulaHandlerReq) (*types.AnalyzeContentFormulaData, error) {
	tenantID, uid, err := actorFrom(l.ctx)
	if err != nil {
		return nil, err
	}
	sourceType := strings.TrimSpace(req.SourceType)
	if sourceType == "" {
		return nil, app.For(code.ThreadsAccount).InputMissingRequired("source_type is required")
	}
	if _, err := l.svcCtx.ThreadsAccount.Get(l.ctx, tenantID, uid, req.ID); err != nil {
		return nil, err
	}

	credential, err := l.svcCtx.ThreadsAccount.ResolveMemberAiCredential(l.ctx, tenantID, uid)
	if err != nil {
		return nil, err
	}
	providerID, err := aiusecase.MapWorkerProvider(credential.Provider)
	if err != nil {
		return nil, err
	}
	aiReq := domai.GenerateRequest{
		Provider:   providerID,
		Model:      credential.Model,
		Credential: domai.Credential{APIKey: credential.APIKey},
	}
	personaBlock := l.resolvePersonaBlock(tenantID, uid, req.PersonaID)

	var analysis *libformula.PastedAnalysis
	var sourceRef, postText, author, permalink string
	var metrics *cfentity.SourceMetrics

	switch sourceType {
	case cfentity.SourcePaste:
		postText = strings.TrimSpace(req.PostText)
		if postText == "" {
			return nil, app.For(code.ThreadsAccount).InputMissingRequired("post_text is required for paste")
		}
		author = strings.TrimSpace(req.AuthorName)
		analysis, err = libformula.AnalyzePasted(l.ctx, l.svcCtx.AI, aiReq, libformula.AnalyzePastedInput{
			PostText: postText, AuthorName: author, PersonaBlock: personaBlock,
			LikeCount: req.LikeCount, ReplyCount: req.ReplyCount,
		})
		sourceRef = "paste"
	case cfentity.SourceOwnPost:
		mediaID := strings.TrimSpace(req.MediaID)
		if mediaID == "" {
			return nil, app.For(code.ThreadsAccount).InputMissingRequired("media_id is required for own_post")
		}
		review, rerr := l.svcCtx.OwnPostFormula.Get(l.ctx, tenantID, uid, req.ID, mediaID)
		if rerr != nil || review == nil || req.ForceRefresh {
			gen := NewGenerateOwnPostFormulaLogic(l.ctx, l.svcCtx)
			force := req.ForceRefresh
			genData, genErr := gen.GenerateOwnPostFormula(&types.GenerateOwnPostFormulaHandlerReq{
				ThreadsAccountPath: types.ThreadsAccountPath{ID: req.ID},
				GenerateOwnPostFormulaReq: types.GenerateOwnPostFormulaReq{
					MediaID: mediaID, PersonaID: req.PersonaID, PostText: req.PostText,
					LikeCount: req.LikeCount, ReplyCount: req.ReplyCount, Views: req.Views,
					Force: force,
				},
			})
			if genErr != nil {
				return nil, genErr
			}
			analysis = &libformula.PastedAnalysis{
				Summary: genData.Summary, Wins: genData.Wins, Improvements: genData.Improvements,
				Formula: genData.Formula, PostTemplate: genData.PostTemplate,
				HookPattern: genData.HookPattern, Structure: genData.Structure,
				ReplicationTips: genData.ReplicationTips, Avoid: genData.Avoid,
			}
		} else {
			analysis = libformula.FromOwnPostReview(&libownpost.PostFormulaReview{
				Summary: review.Summary, Wins: review.Wins, Improvements: review.Improvements,
				Formula: review.Formula, PostTemplate: review.PostTemplate,
				HookPattern: review.HookPattern, Structure: review.Structure,
				ReplicationTips: review.ReplicationTips, Avoid: review.Avoid,
			})
		}
		postText = strings.TrimSpace(req.PostText)
		if postText == "" && review != nil {
			postText = review.PostText
		}
		sourceRef = mediaID
	case cfentity.SourceScanPost:
		scanPostID := strings.TrimSpace(req.ScanPostID)
		personaID := strings.TrimSpace(req.PersonaID)
		if scanPostID == "" || personaID == "" {
			return nil, app.For(code.ThreadsAccount).InputMissingRequired("scan_post_id and persona_id are required for scan_post")
		}
		post, perr := l.svcCtx.ScanPost.GetForPersona(l.ctx, tenantID, uid, personaID, scanPostID)
		if perr != nil {
			return nil, perr
		}
		postText = post.Text
		author = post.Author
		permalink = post.Permalink
		analysis, err = l.analyzeViralPost(aiReq, post.Text, post.Author, post.LikeCount, post.ReplyCount, post.SearchTag, personaBlock)
		sourceRef = scanPostID
		metrics = &cfentity.SourceMetrics{LikeCount: post.LikeCount, ReplyCount: post.ReplyCount}
	case cfentity.SourceKeywordSearch:
		postText = strings.TrimSpace(req.PostText)
		if postText == "" {
			return nil, app.For(code.ThreadsAccount).InputMissingRequired("post_text is required after selecting a search result")
		}
		author = strings.TrimSpace(req.AuthorName)
		permalink = strings.TrimSpace(req.Permalink)
		analysis, err = l.analyzeViralPost(aiReq, postText, author, req.LikeCount, req.ReplyCount, req.Keyword, personaBlock)
		sourceRef = strings.TrimSpace(req.Keyword)
		metrics = &cfentity.SourceMetrics{
			LikeCount: req.LikeCount, ReplyCount: req.ReplyCount,
			Views: req.Views, RepostCount: req.RepostCount,
			QuoteCount: req.QuoteCount, Shares: req.Shares,
		}
	default:
		return nil, app.For(code.ThreadsAccount).InputMissingRequired("unsupported source_type")
	}
	if err != nil {
		return nil, app.For(code.AI).SvcThirdParty("分析失敗：" + err.Error())
	}
	if analysis == nil {
		return nil, app.For(code.AI).SvcThirdParty("分析未產出有效公式")
	}

	label := strings.TrimSpace(req.SaveLabel)
	if label == "" {
		label = defaultFormulaLabel(sourceType, postText, author)
	}
	saved, err := l.svcCtx.ContentFormula.Create(l.ctx, cfdom.CreateRequest{
		TenantID: tenantID, OwnerUID: uid, AccountID: req.ID,
		Label: label, SourceType: sourceType, SourceRef: sourceRef,
		SourcePostText: postText, SourceAuthor: author, SourcePermalink: permalink,
		SourceMetrics: metrics,
		Summary:       analysis.Summary, Wins: analysis.Wins, Improvements: analysis.Improvements,
		Formula: analysis.Formula, PostTemplate: analysis.PostTemplate,
		HookPattern: analysis.HookPattern, Structure: analysis.Structure,
		ReplicationTips: analysis.ReplicationTips, Avoid: analysis.Avoid,
	})
	if err != nil {
		return nil, err
	}
	data := toContentFormulaData(*saved)
	return &types.AnalyzeContentFormulaData{
		Formula: data,
		Message: fmt.Sprintf("已分析並存入公式庫：%s", data.Label),
	}, nil
}

func (l *AnalyzeContentFormulaLogic) resolvePersonaBlock(tenantID, uid, personaID string) string {
	personaID = strings.TrimSpace(personaID)
	if personaID == "" {
		return ""
	}
	persona, err := l.svcCtx.Persona.Get(l.ctx, tenantID, uid, personaID)
	if err != nil {
		return ""
	}
	return liboutreach.FormatVoicePersonaBlock(persona)
}

func (l *AnalyzeContentFormulaLogic) analyzeViralPost(
	aiReq domai.GenerateRequest,
	postText, author string, likes, replies int, tag, personaBlock string,
) (*libformula.PastedAnalysis, error) {
	out, err := l.svcCtx.AI.GenerateText(l.ctx, domai.GenerateRequest{
		Provider: aiReq.Provider, Model: aiReq.Model, Credential: aiReq.Credential,
		System: libviral.BuildAnalyzeViralSystemPrompt(),
		Messages: []domai.Message{{Role: "user", Content: libviral.BuildAnalyzeViralUserPrompt(libviral.AnalyzeViralInput{
			PostText: postText, AuthorName: author, LikeCount: likes, ReplyCount: replies,
			SearchTag: tag, TopicLabel: tag, Persona: personaBlock,
		})}},
	})
	if err != nil {
		return nil, err
	}
	parsed, err := libviral.ParseAnalyzeViralOutput(out.Text)
	if err != nil {
		return nil, err
	}
	return libformula.FromViralAnalysis(
		parsed.HookPattern, parsed.StructurePattern, parsed.EmotionalTrigger,
		parsed.ReplicationStrategy, parsed.KeyTakeaways,
	), nil
}

func defaultFormulaLabel(sourceType, postText, author string) string {
	preview := strings.TrimSpace(postText)
	if len([]rune(preview)) > 24 {
		preview = string([]rune(preview)[:24]) + "…"
	}
	if author != "" {
		return fmt.Sprintf("%s · @%s", sourceType, author)
	}
	if preview != "" {
		return preview
	}
	return sourceType + " 公式"
}
