package job

import (
	"context"
	"fmt"

	app "haixun-backend/internal/library/errors"
	"haixun-backend/internal/library/errors/code"
	libmatrix "haixun-backend/internal/library/matrix"
	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"
	copydraftentity "haixun-backend/internal/model/copy_draft/domain/entity"
	copydraftdomain "haixun-backend/internal/model/copy_draft/domain/usecase"
	missionentity "haixun-backend/internal/model/copy_mission/domain/entity"
	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 GenerateCopyMatrixDeps struct {
	Jobs           jobdom.UseCase
	CopyMission    missiondomain.UseCase
	Persona        personadomain.UseCase
	ScanPost       scanpostdomain.UseCase
	CopyDraft      copydraftdomain.UseCase
	ThreadsAccount threadsaccountdomain.UseCase
	AI             aiusecase.UseCase
}

func RegisterGenerateCopyMatrixHandler(runner *Runner, deps GenerateCopyMatrixDeps) {
	if runner == nil {
		return
	}
	runner.RegisterStepHandler("copy_matrix_generate", func(ctx context.Context, step StepContext) error {
		return runGenerateCopyMatrix(ctx, step, deps)
	})
}

func runGenerateCopyMatrix(ctx context.Context, step StepContext, deps GenerateCopyMatrixDeps) error {
	payload := step.Run.Payload
	tenantID, ownerUID := runActorFromPayload(payload, step.Run)
	personaID := stringField(payload, "persona_id")
	missionID := copyMissionIDFromPayload(payload)
	if tenantID == "" || ownerUID == "" || personaID == "" || missionID == "" {
		return fmt.Errorf("generate-copy-matrix payload missing tenant_id, owner_uid, persona_id, or copy_mission_id")
	}

	mission, err := deps.CopyMission.Get(ctx, tenantID, ownerUID, personaID, missionID)
	if err != nil {
		return err
	}
	if mission.Status != string(missionentity.StatusScanned) &&
		mission.Status != string(missionentity.StatusDrafted) {
		return app.For(code.Persona).ResInvalidState("請先完成海巡再產出內容矩陣")
	}
	if len(mission.SelectedTags) == 0 {
		return app.For(code.Persona).InputMissingRequired("請先選擇海巡標籤")
	}

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

	count := intField(payload, "count")
	if count <= 0 {
		count = 5
	}
	if count > 12 {
		count = 12
	}

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

	updateProgress("讀取爆款樣本…", 15)

	posts, err := deps.ScanPost.ListForPersona(ctx, scanpostdomain.PersonaListRequest{
		TenantID:      tenantID,
		OwnerUID:      ownerUID,
		PersonaID:     personaID,
		CopyMissionID: missionID,
		Limit:         12,
	})
	if err != nil {
		return err
	}
	samples := matrixSamplesFromPosts(posts)
	researchBlock := libmatrix.FormatCopyResearchMapBlock(
		mission.ResearchMap.AudienceSummary,
		mission.ResearchMap.ContentGoal,
		mission.ResearchMap.Questions,
		mission.ResearchMap.Pillars,
		mission.ResearchMap.Exclusions,
	)
	userPrompt, err := libmatrix.BuildCopyUserPrompt(libmatrix.CopyGenerateInput{
		Count:        count,
		TopicLabel:   mission.Label,
		TopicBrief:   mission.Brief,
		ResearchMap:  researchBlock,
		SelectedTags: mission.SelectedTags,
		ViralSamples: samples,
		PersonaBlock: personaBlock,
	})
	if err != nil {
		return app.For(code.AI).SysInternal("matrix user prompt load failed")
	}
	systemPrompt, err := libprompt.MatrixCopySystem()
	if err != nil {
		return app.For(code.AI).SysInternal("matrix system prompt load failed")
	}

	updateProgress("產出內容矩陣草稿…", 45)

	credential, err := deps.ThreadsAccount.ResolveMemberAiCredential(ctx, tenantID, ownerUID)
	if err != nil {
		return err
	}
	providerID, err := aiusecase.MapWorkerProvider(credential.Provider)
	if err != nil {
		return err
	}
	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},
		},
	})
	if err != nil {
		return err
	}
	parsed, err := libmatrix.ParseGenerateOutput(result.Text)
	if err != nil {
		return app.For(code.AI).SvcThirdParty("內容矩陣 LLM 回傳無法解析：" + err.Error())
	}

	updateProgress("儲存矩陣草稿…", 88)

	createReqs := make([]copydraftdomain.CreateRequest, 0, len(parsed.Rows))
	for _, row := range parsed.Rows {
		createReqs = append(createReqs, copydraftdomain.CreateRequest{
			CopyMissionID:  missionID,
			DraftType:      copydraftentity.DraftTypeMatrix,
			SortOrder:      row.SortOrder,
			Text:           row.Text,
			Angle:          row.Angle,
			Hook:           row.Hook,
			Rationale:      row.Rationale,
			ReferenceNotes: row.ReferenceNotes,
			Sources:        row.SourcePermalinks,
		})
	}
	saved, err := deps.CopyDraft.ReplaceMissionMatrix(ctx, tenantID, ownerUID, personaID, missionID, createReqs)
	if err != nil {
		return err
	}

	drafted := missionentity.StatusDrafted
	_, _ = deps.CopyMission.Update(ctx, missiondomain.UpdateRequest{
		TenantID:  tenantID,
		OwnerUID:  ownerUID,
		PersonaID: personaID,
		MissionID: missionID,
		Patch: missiondomain.MissionPatch{
			Status: &drafted,
		},
	})

	handoff := map[string]any{
		"flow":            "copy",
		"persona_id":      personaID,
		"copy_mission_id": missionID,
		"summary":         fmt.Sprintf("已產出 %d 篇矩陣草稿", len(saved)),
		"next_route":      fmt.Sprintf("/matrix/missions/%s#copy-output", missionID),
	}

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

func matrixSamplesFromPosts(posts []scanpostdomain.ScanPostSummary) string {
	samples := make([]libmatrix.ViralPostSample, 0, len(posts))
	for _, post := range posts {
		replies := make([]libmatrix.ViralReplySample, 0, len(post.Replies))
		for _, reply := range post.Replies {
			replies = append(replies, libmatrix.ViralReplySample{
				Author: reply.Author,
				Text:   reply.Text,
			})
		}
		samples = append(samples, libmatrix.ViralPostSample{
			Author:    post.Author,
			LikeCount: post.LikeCount,
			SearchTag: post.SearchTag,
			Text:      post.Text,
			Replies:   replies,
		})
	}
	return libmatrix.FormatViralSamples(samples)
}

func intField(payload map[string]any, key string) int {
	if payload == nil {
		return 0
	}
	switch v := payload[key].(type) {
	case int:
		return v
	case int32:
		return int(v)
	case int64:
		return int(v)
	case float64:
		return int(v)
	default:
		return 0
	}
}
