// Code scaffolded by goctl. Safe to edit.
// goctl 1.10.1

package persona

import (
	"context"
	"strings"

	app "haixun-backend/internal/library/errors"
	"haixun-backend/internal/library/errors/code"
	libprompt "haixun-backend/internal/library/prompt"
	"haixun-backend/internal/library/style8d"
	domai "haixun-backend/internal/model/ai/domain/usecase"
	aiusecase "haixun-backend/internal/model/ai/usecase"
	personausecase "haixun-backend/internal/model/persona/domain/usecase"
	tausecase "haixun-backend/internal/model/threads_account/domain/usecase"
	"haixun-backend/internal/svc"
	"haixun-backend/internal/types"

	"github.com/zeromicro/go-zero/core/logx"
)

type StartPersonaStyleAnalysisFromTextLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewStartPersonaStyleAnalysisFromTextLogic(ctx context.Context, svcCtx *svc.ServiceContext) *StartPersonaStyleAnalysisFromTextLogic {
	return &StartPersonaStyleAnalysisFromTextLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *StartPersonaStyleAnalysisFromTextLogic) StartPersonaStyleAnalysisFromText(
	req *types.StartPersonaStyleAnalysisFromTextHandlerReq,
) (resp *types.StartPersonaStyleAnalysisFromTextData, err error) {
	tenantID, uid, err := actorFrom(l.ctx)
	if err != nil {
		return nil, err
	}
	if req == nil {
		return nil, app.For(code.Persona).InputMissingRequired("request is required")
	}

	texts, err := style8d.NormalizeReferenceTexts(req.ReferenceTexts, req.RawText)
	if err != nil {
		return nil, app.For(code.Persona).InputInvalidFormat(err.Error())
	}

	persona, err := l.svcCtx.Persona.Get(l.ctx, tenantID, uid, req.ID)
	if err != nil {
		return nil, err
	}

	activeAccountID := ""
	if member, memberErr := l.svcCtx.Member.GetByUID(l.ctx, tenantID, uid); memberErr == nil {
		activeAccountID = member.ActiveThreadsAccountID
	}

	credential, err := l.resolveResearchCredential(tenantID, uid, activeAccountID)
	if err != nil {
		return nil, err
	}

	providerID, err := aiusecase.MapWorkerProvider(credential.Provider)
	if err != nil {
		return nil, err
	}

	posts := style8d.TextsToPosts(texts)
	input := style8d.AnalyzeInput{
		Mode:        style8d.AnalyzeModeManual,
		SourceLabel: strings.TrimSpace(req.SourceLabel),
		Brief:       strings.TrimSpace(persona.Brief),
		Posts:       posts,
	}

	systemPrompt, err := libprompt.Style8DSystem()
	if err != nil {
		return nil, app.For(code.AI).SysInternal("prompt config load failed")
	}
	maxTokens := 8192
	result, err := l.svcCtx.AI.GenerateText(l.ctx, domai.GenerateRequest{
		Provider: providerID,
		Model:    credential.Model,
		Credential: domai.Credential{
			APIKey: credential.APIKey,
		},
		System: systemPrompt,
		Messages: []domai.Message{
			{Role: "user", Content: input.BuildUserPrompt()},
		},
		MaxTokens: &maxTokens,
	})
	if err != nil {
		if strings.Contains(err.Error(), "HTTP 401") {
			err = app.For(code.AI).SvcThirdParty(
				"8D AI 分析授權失敗：請到「設定 > 帳號 AI 設定」確認研究用 provider=" +
					credential.Provider + "、model=" + credential.Model + "，並重新貼上對應 provider 的 API key",
			)
		}
		return nil, err
	}

	parsed, err := style8d.ParseLLMOutput(result.Text)
	if err != nil {
		return nil, app.For(code.AI).SvcThirdParty("8D LLM 回傳無法解析：" + err.Error())
	}
	profile := input.BuildStoredProfile(parsed)
	profileJSON, err := profile.JSON()
	if err != nil {
		return nil, err
	}

	benchmark := input.StyleBenchmark()
	personaDraft := strings.TrimSpace(profile.PersonaDraft)
	patch := personausecase.PersonaPatch{
		StyleProfile:   &profileJSON,
		StyleBenchmark: &benchmark,
	}
	if personaDraft != "" {
		patch.Persona = &personaDraft
	}
	updated, err := l.svcCtx.Persona.Update(l.ctx, personausecase.UpdateRequest{
		TenantID:  tenantID,
		OwnerUID:  uid,
		PersonaID: req.ID,
		Patch:     patch,
	})
	if err != nil {
		return nil, err
	}

	out := toPersonaData(*updated)
	return &types.StartPersonaStyleAnalysisFromTextData{
		Persona:   out,
		PostCount: len(posts),
		Message:   "8D 分析完成，風格檔案已寫入人設",
	}, nil
}

func (l *StartPersonaStyleAnalysisFromTextLogic) resolveResearchCredential(
	tenantID, uid, activeAccountID string,
) (*tausecase.WorkerAiCredential, error) {
	accountID := strings.TrimSpace(activeAccountID)
	if accountID != "" {
		return l.svcCtx.ThreadsAccount.ResolveWorkerAiCredential(l.ctx, tenantID, uid, accountID)
	}
	return l.svcCtx.ThreadsAccount.ResolveMemberResearchAiCredential(l.ctx, tenantID, uid)
}
