package placement_topic

import (
	"context"
	"strings"

	app "haixun-backend/internal/library/errors"
	"haixun-backend/internal/library/errors/code"
	libkg "haixun-backend/internal/library/knowledge"
	"haixun-backend/internal/library/placement"
	brandlogic "haixun-backend/internal/logic/brand"
	jobdom "haixun-backend/internal/model/job/domain/usecase"
	kgdom "haixun-backend/internal/model/knowledge_graph/domain/usecase"
	"haixun-backend/internal/svc"
	"haixun-backend/internal/types"

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

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

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

func (l *StartPlacementTopicScanJobLogic) StartPlacementTopicScanJob(req *types.StartPlacementTopicScanJobHandlerReq) (*types.StartBrandScanJobData, error) {
	tenantID, uid, err := actorFrom(l.ctx)
	if err != nil {
		return nil, err
	}
	scope, err := resolveScope(l.ctx, l.svcCtx, tenantID, uid, req.ID)
	if err != nil {
		return nil, err
	}
	var graph *kgdom.GraphSummary
	if loaded, err := l.svcCtx.KnowledgeGraph.GetByTopic(l.ctx, tenantID, uid, scope.TopicID, scope.BrandID); err != nil {
		if !brandlogic.IsKnowledgeGraphNotFound(err) {
			return nil, err
		}
	} else {
		graph = loaded
	}
	selected := 0
	if graph != nil {
		for _, node := range graph.Nodes {
			if node.SelectedForScan {
				selected++
			}
		}
	}
	nodeIDs := []string{}
	for _, id := range req.NodeIDs {
		id = strings.TrimSpace(id)
		if id != "" {
			nodeIDs = append(nodeIDs, id)
		}
	}
	graphID := scope.TopicID
	if graph != nil && strings.TrimSpace(graph.ID) != "" {
		graphID = graph.ID
	}
	if strings.TrimSpace(req.GraphID) != "" {
		graphID = strings.TrimSpace(req.GraphID)
	}
	dualTrack := true
	patrolMode := req.PatrolMode
	brandForPatrol := scope.Brand
	brandForPatrol.ProductID = scope.Topic.ProductID
	brandForPatrol.ResearchMap = scope.Topic.ResearchMap
	productBrief := strings.TrimSpace(brandForPatrol.ProductBrief)
	if formatted := placement.ProductBriefFromContext(brandForPatrol.ProductContext); formatted != "" {
		productBrief = formatted
	}
	patrolInput := libkg.PatrolTagInputFromBrand(&brandForPatrol, productBrief)
	patrolNodes := []libkg.Node{}
	if graph != nil {
		patrolNodes = graph.Nodes
	}
	patrolKeywords := libkg.ResolveScanPatrolKeywords(
		req.PatrolKeywords,
		scope.Topic.ResearchMap.PatrolKeywords,
		patrolInput,
		patrolNodes,
	)
	if patrolMode || (len(nodeIDs) == 0 && selected == 0) {
		if len(patrolKeywords) == 0 {
			return nil, app.For(code.Brand).InputMissingRequired("請先產生研究地圖，系統會依研究地圖自動整理海巡關鍵字")
		}
		patrolMode = true
	} else if graph == nil {
		return nil, app.For(code.Brand).ResNotFound("請先產生延伸知識圖譜，或改用手動海巡關鍵字")
	}
	research, err := l.svcCtx.Placement.ResearchSettings(l.ctx, tenantID, uid)
	if err != nil {
		return nil, err
	}
	memberCtx, err := l.svcCtx.ThreadsAccount.ResolveMemberPlacementContext(l.ctx, tenantID, uid, research)
	if err != nil {
		return nil, err
	}
	if !memberCtx.AllowsBrave && !memberCtx.AllowsThreadsAPI && !memberCtx.AllowsCrawler {
		return nil, app.For(code.Setting).InputMissingRequired("目前連線模式無法海巡，請確認 Threads API、Brave 或 Chrome Session")
	}
	if placement.MemberNeedsWebSearchKey(memberCtx) && placement.MissingWebSearchKey(research) {
		return nil, app.For(code.Setting).InputMissingRequired(placement.WebSearchKeyRequiredMessage(research))
	}
	if memberCtx.DevMode && !memberCtx.BrowserConnected {
		return nil, app.For(code.Setting).InputMissingRequired("開發模式需先同步 Chrome Session")
	}
	if !memberCtx.DevMode && memberCtx.AllowsThreadsAPI && !memberCtx.ApiConnected {
		return nil, app.For(code.Setting).InputMissingRequired("正式模式需先完成 Threads API 連線")
	}
	payload := map[string]any{
		"topic_id":   scope.TopicID,
		"brand_id":   scope.BrandID,
		"graph_id":   graphID,
		"dual_track": dualTrack,
		"node_ids":   nodeIDs,
	}
	if patrolMode {
		payload["patrol_mode"] = true
		payload["patrol_keywords"] = patrolKeywords
	}
	for key, value := range memberCtx.PayloadFields() {
		payload[key] = value
	}
	run, err := l.svcCtx.Job.CreateRun(l.ctx, jobdom.CreateRunRequest{
		TemplateType: "placement-scan",
		Scope:        "placement_topic",
		ScopeID:      scope.TopicID,
		TenantID:     tenantID,
		OwnerUID:     uid,
		Payload:      payload,
	})
	if err != nil {
		return nil, err
	}
	return &types.StartBrandScanJobData{
		JobID:   run.ID.Hex(),
		Status:  string(run.Status),
		Message: "雙軌海巡已在背景執行，完成後可到獲客台查看貼文",
	}, nil
}
