package job

import (
	"context"
	"fmt"
	"strings"

	app "haixun-backend/internal/library/errors"
	"haixun-backend/internal/library/errors/code"
	"haixun-backend/internal/library/style8d"
	libviral "haixun-backend/internal/library/viral"
	domai "haixun-backend/internal/model/ai/domain/usecase"
	aiusecase "haixun-backend/internal/model/ai/usecase"
	copydraftdomain "haixun-backend/internal/model/copy_draft/domain/usecase"
	missiondomain "haixun-backend/internal/model/copy_mission/domain/usecase"
	jobdom "haixun-backend/internal/model/job/domain/usecase"
	personadomain "haixun-backend/internal/model/persona/domain/usecase"
	scanpostdomain "haixun-backend/internal/model/scan_post/domain/usecase"
	threadsaccountdomain "haixun-backend/internal/model/threads_account/domain/usecase"
)

type GenerateCopyDraftDeps struct {
	Jobs           jobdom.UseCase
	CopyMission    missiondomain.UseCase
	Persona        personadomain.UseCase
	ScanPost       scanpostdomain.UseCase
	CopyDraft      copydraftdomain.UseCase
	ThreadsAccount threadsaccountdomain.UseCase
	AI             aiusecase.UseCase
}

func RegisterGenerateCopyDraftHandler(runner *Runner, deps GenerateCopyDraftDeps) {
	if runner == nil {
		return
	}
	runner.RegisterStepHandler("copy_draft_generate", func(ctx context.Context, step StepContext) error {
		return runGenerateCopyDraft(ctx, step, deps)
	})
}

func runGenerateCopyDraft(ctx context.Context, step StepContext, deps GenerateCopyDraftDeps) error {
	payload := step.Run.Payload
	tenantID, ownerUID := runActorFromPayload(payload, step.Run)
	personaID := stringField(payload, "persona_id")
	missionID := copyMissionIDFromPayload(payload)
	scanPostID := strings.TrimSpace(stringField(payload, "scan_post_id"))
	if tenantID == "" || ownerUID == "" || personaID == "" || scanPostID == "" {
		return fmt.Errorf("generate-copy-draft payload missing tenant_id, owner_uid, persona_id, or scan_post_id")
	}

	updateProgress := func(summary string, percentage int) {
		_ = step.Heartbeat(ctx)
		_, _ = deps.Jobs.UpdateProgress(ctx, jobdom.UpdateProgressRequest{
			JobID:      step.JobID,
			WorkerID:   step.WorkerID,
			Phase:      "copy_draft_generate",
			Summary:    summary,
			Percentage: percentage,
		})
	}

	updateProgress("讀取爆款原文…", 12)

	persona, err := deps.Persona.Get(ctx, tenantID, ownerUID, personaID)
	if err != nil {
		return err
	}
	post, err := deps.ScanPost.GetForPersona(ctx, tenantID, ownerUID, personaID, scanPostID)
	if err != nil {
		return err
	}
	if missionID == "" {
		missionID = strings.TrimSpace(post.CopyMissionID)
	}

	topicLabel := strings.TrimSpace(post.SearchTag)
	topicBrief := strings.TrimSpace(persona.Brief)
	if missionID != "" {
		if mission, missionErr := deps.CopyMission.Get(ctx, tenantID, ownerUID, personaID, missionID); missionErr == nil {
			if label := strings.TrimSpace(mission.Label); label != "" {
				topicLabel = label
			}
			if brief := strings.TrimSpace(mission.Brief); brief != "" {
				topicBrief = brief
			}
		}
	}

	if !style8d.HasReady8D(persona.Persona, persona.StyleProfile) {
		return app.For(code.Persona).InputMissingRequired("請先完成人設 8D 對標分析")
	}
	personaBlock := style8d.ResolvePersonaBlock(persona.Persona, persona.StyleProfile, persona.Brief)

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

	updateProgress("分析爆款結構…", 35)

	analysisText := ""
	analyzeResult, analyzeErr := deps.AI.GenerateText(ctx, domai.GenerateRequest{
		Provider: providerID,
		Model:    credential.Model,
		Credential: domai.Credential{
			APIKey: credential.APIKey,
		},
		System: libviral.BuildAnalyzeViralSystemPrompt(),
		Messages: []domai.Message{
			{
				Role: "user",
				Content: libviral.BuildAnalyzeViralUserPrompt(libviral.AnalyzeViralInput{
					PostText:   post.Text,
					AuthorName: post.Author,
					LikeCount:  post.LikeCount,
					ReplyCount: post.ReplyCount,
					SearchTag:  post.SearchTag,
					TopicLabel: topicLabel,
					TopicBrief: topicBrief,
					Persona:    personaBlock,
				}),
			},
		},
	})
	if analyzeErr == nil {
		if parsed, parseErr := libviral.ParseAnalyzeViralOutput(analyzeResult.Text); parseErr == nil {
			analysisText = libviral.FormatAnalysisForReplicate(parsed)
		}
	}

	updateProgress("深仿寫產文中…", 65)

	result, err := deps.AI.GenerateText(ctx, domai.GenerateRequest{
		Provider: providerID,
		Model:    credential.Model,
		Credential: domai.Credential{
			APIKey: credential.APIKey,
		},
		System: libviral.BuildSystemPrompt(),
		Messages: []domai.Message{
			{
				Role: "user",
				Content: libviral.BuildUserPrompt(libviral.ReplicateInput{
					TopicLabel:        topicLabel,
					TopicBrief:        topicBrief,
					Persona:           personaBlock,
					StyleProfile:      "",
					OriginalText:      post.Text,
					AuthorName:        post.Author,
					StructureAnalysis: analysisText,
				}),
			},
		},
	})
	if err != nil {
		return err
	}

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

	updateProgress("儲存仿寫草稿…", 90)

	saved, err := deps.CopyDraft.Create(ctx, copydraftdomain.CreateRequest{
		TenantID:       tenantID,
		OwnerUID:       ownerUID,
		PersonaID:      personaID,
		CopyMissionID:  post.CopyMissionID,
		ScanPostID:     scanPostID,
		DraftType:      "replicate",
		Text:           parsed.Text,
		Angle:          parsed.Angle,
		Hook:           parsed.Hook,
		Rationale:      parsed.Rationale,
		ReferenceNotes: parsed.StructureNotes,
		Sources:        []string{post.Permalink},
	})
	if err != nil {
		return err
	}

	nextRoute := "/matrix"
	if missionID != "" {
		nextRoute = fmt.Sprintf("/matrix/missions/%s#copy-output", missionID)
	}
	handoff := map[string]any{
		"flow":            "copy",
		"persona_id":      personaID,
		"copy_mission_id": missionID,
		"scan_post_id":    scanPostID,
		"summary":         "已產出深仿寫草稿",
		"next_route":      nextRoute,
	}

	_, err = deps.Jobs.CompleteRun(ctx, jobdom.CompleteRunRequest{
		JobID:    step.JobID,
		WorkerID: step.WorkerID,
		Result: map[string]any{
			"draft_id": saved.ID,
			"handoff":  handoff,
		},
	})
	return err
}
