package copy_mission

import (
	"context"
	"strings"

	app "haixun-backend/internal/library/errors"
	"haixun-backend/internal/library/errors/code"
	"haixun-backend/internal/library/placement"
	"haixun-backend/internal/library/style8d"
	libviral "haixun-backend/internal/library/viral"
	"haixun-backend/internal/library/websearch"
	domai "haixun-backend/internal/model/ai/domain/usecase"
	aiusecase "haixun-backend/internal/model/ai/usecase"
	"haixun-backend/internal/svc"
	"haixun-backend/internal/types"
)

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

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

func (l *InspireCopyMissionLogic) InspireCopyMission(req *types.CopyMissionInspirationHandlerReq) (*types.CopyMissionInspirationData, error) {
	tenantID, uid, err := actorFrom(l.ctx)
	if err != nil {
		return nil, err
	}
	personaID := strings.TrimSpace(req.PersonaID)
	userKeyword := strings.TrimSpace(req.Keyword)
	persona, err := l.svcCtx.Persona.Get(l.ctx, tenantID, uid, personaID)
	if err != nil {
		return nil, err
	}

	missionList, err := l.svcCtx.CopyMission.List(l.ctx, tenantID, uid, personaID)
	if err != nil {
		return nil, err
	}
	recentLabels := make([]string, 0, 8)
	recentSeeds := make([]string, 0, 8)
	for _, mission := range missionList.List {
		if mission.Status == "archived" {
			continue
		}
		if label := strings.TrimSpace(mission.Label); label != "" {
			recentLabels = append(recentLabels, label)
		}
		if seed := strings.TrimSpace(mission.SeedQuery); seed != "" {
			recentSeeds = append(recentSeeds, seed)
		}
		if len(recentLabels) >= 8 && len(recentSeeds) >= 8 {
			break
		}
	}

	trendSnippets := []libviral.MissionInspireTrendSnippet{}
	webSearchProvider := ""
	trendSourceLabel := ""
	llmOnly := true

	research, researchErr := l.svcCtx.Placement.ResearchSettings(l.ctx, tenantID, uid)
	if researchErr == nil {
		memberCtx, memberErr := l.svcCtx.ThreadsAccount.ResolveMemberPlacementContext(l.ctx, tenantID, uid, research)
		if memberErr == nil {
			trendSnippets = libviral.CollectMissionInspireThreadsTrends(l.ctx, libviral.MissionInspireThreadsInput{
				Keyword:        userKeyword,
				PersonaBrief:   persona.Brief,
				StyleBenchmark: persona.StyleBenchmark,
				Pillars:        append([]string(nil), persona.CopyResearchMap.Pillars...),
				Questions:      append([]string(nil), persona.CopyResearchMap.Questions...),
				Member:         memberCtx,
				Crawler:        l.makeCrawlerSearchFn(tenantID, uid),
			})
			if len(trendSnippets) > 0 {
				trendSourceLabel = memberCtx.DiscoverPathLabel()
				llmOnly = false
			}
			if len(trendSnippets) == 0 && placement.WebSearchAvailable(research) {
				webClient := websearch.New(memberCtx.WebSearchConfig())
				trendSnippets = libviral.CollectMissionInspireTrends(
					l.ctx,
					webClient,
					memberCtx,
					persona.Brief+" "+userKeyword,
					persona.StyleBenchmark,
				)
				webSearchProvider = memberCtx.WebSearchProviderLabel()
				trendSourceLabel = webSearchProvider
				llmOnly = len(trendSnippets) == 0
			}
		}
	}
	webSearchUsed := len(trendSnippets) > 0 && webSearchProvider != ""

	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
	}

	personaBlock := style8d.ResolvePersonaBlock(persona.Persona, persona.StyleProfile, persona.Brief)
	result, err := l.svcCtx.AI.GenerateText(l.ctx, domai.GenerateRequest{
		Provider: providerID,
		Model:    credential.Model,
		Credential: domai.Credential{
			APIKey: credential.APIKey,
		},
		System: libviral.BuildMissionInspireSystemPrompt(),
		Messages: []domai.Message{
			{
				Role: "user",
				Content: libviral.BuildMissionInspireUserPrompt(libviral.MissionInspireInput{
					PersonaDisplayName:  persona.DisplayName,
					PersonaBrief:        persona.Brief,
					PersonaBlock:        personaBlock,
					StyleBenchmark:      persona.StyleBenchmark,
					PersonaAudience:     persona.CopyResearchMap.AudienceSummary,
					PersonaContentGoal:  persona.CopyResearchMap.ContentGoal,
					PersonaQuestions:    append([]string(nil), persona.CopyResearchMap.Questions...),
					PersonaPillars:      append([]string(nil), persona.CopyResearchMap.Pillars...),
					RecentMissionLabels: recentLabels,
					RecentSeedQueries:   recentSeeds,
					UserKeyword:         userKeyword,
					TrendSnippets:       trendSnippets,
					WebSearchProvider:   trendSourceLabel,
					LLMOnly:             llmOnly,
				}),
			},
		},
	})
	if err != nil {
		return nil, err
	}

	parsed, err := libviral.ParseMissionInspireOutput(result.Text)
	if err != nil {
		return nil, app.For(code.AI).SvcThirdParty("靈感骰子 LLM 回傳無法解析：" + err.Error())
	}

	sources := make([]types.CopyMissionInspirationSourceData, 0, len(trendSnippets))
	for _, item := range trendSnippets {
		sources = append(sources, types.CopyMissionInspirationSourceData{
			Query:   item.Query,
			Title:   item.Title,
			Snippet: item.Snippet,
			URL:     item.URL,
		})
	}

	message := "已依近期 Threads 熱門素材產出任務靈感，可微調後建立任務"
	if webSearchUsed {
		message = "已依近期網路趨勢產出任務靈感，可微調後建立任務"
	}
	if len(trendSnippets) == 0 {
		message = "未取得外部趨勢素材，已改由 LLM 依人設與關鍵字推測靈感；同步 Chrome Session、完成 Threads API 或補上 Brave／Exa key 可更貼近熱搜"
	}

	return &types.CopyMissionInspirationData{
		Label:         parsed.Label,
		SeedQuery:     parsed.SeedQuery,
		Brief:         parsed.Brief,
		TrendReason:   parsed.TrendReason,
		TrendKeywords: parsed.TrendKeywords,
		Sources:       sources,
		WebSearchUsed: webSearchUsed,
		Message:       message,
	}, nil
}

func (l *InspireCopyMissionLogic) makeCrawlerSearchFn(tenantID, ownerUID string) placement.CrawlerSearchFn {
	return func(ctx context.Context, member placement.MemberContext, keyword string, limit int) ([]placement.DiscoverPost, error) {
		accountID := strings.TrimSpace(member.ActiveAccountID)
		if accountID == "" {
			return nil, app.For(code.Setting).InputMissingRequired("請先選定經營帳號並同步 Chrome Session")
		}
		session, err := l.svcCtx.ThreadsAccount.GetBrowserSession(ctx, tenantID, ownerUID, accountID)
		if err != nil {
			return nil, err
		}
		return placement.RunExecCrawlerSearch(ctx, session.StorageState, keyword, limit)
	}
}
