package usecase

import (
	"context"
	"strings"

	app "haixun-backend/internal/library/errors"
	"haixun-backend/internal/library/errors/code"
	libkg "haixun-backend/internal/library/knowledge"
	libmongo "haixun-backend/internal/library/mongo"
	"haixun-backend/internal/model/knowledge_graph/domain/entity"
	domrepo "haixun-backend/internal/model/knowledge_graph/domain/repository"
	domusecase "haixun-backend/internal/model/knowledge_graph/domain/usecase"
)

type knowledgeGraphUseCase struct {
	repo domrepo.Repository
}

func NewUseCase(repo domrepo.Repository) domusecase.UseCase {
	return &knowledgeGraphUseCase{repo: repo}
}

func (u *knowledgeGraphUseCase) Get(ctx context.Context, tenantID, ownerUID, brandID string) (*domusecase.GraphSummary, error) {
	if err := requireScope(tenantID, ownerUID, brandID, ""); err != nil {
		return nil, err
	}
	item, err := u.repo.FindByBrand(ctx, tenantID, ownerUID, brandID)
	if err != nil {
		return nil, err
	}
	summary := toSummary(item)
	return &summary, nil
}

func (u *knowledgeGraphUseCase) GetByTopic(ctx context.Context, tenantID, ownerUID, topicID, brandID string) (*domusecase.GraphSummary, error) {
	if err := requireScope(tenantID, ownerUID, "", topicID); err != nil {
		return nil, err
	}
	item, err := u.repo.FindByTopic(ctx, tenantID, ownerUID, topicID)
	if err != nil {
		return nil, err
	}
	if item == nil {
		brandID = strings.TrimSpace(brandID)
		if brandID != "" {
			legacy, legacyErr := u.repo.FindByBrand(ctx, tenantID, ownerUID, brandID)
			if legacyErr != nil {
				return nil, legacyErr
			}
			if legacy != nil {
				legacyTopicID := strings.TrimSpace(legacy.TopicID)
				if legacyTopicID == "" || legacyTopicID == topicID {
					if legacyTopicID == "" {
						if err := u.repo.AttachTopicID(ctx, tenantID, ownerUID, brandID, topicID); err != nil {
							return nil, err
						}
						legacy.TopicID = topicID
					}
					item = legacy
				}
			}
		}
	}
	summary := toSummary(item)
	return &summary, nil
}

func (u *knowledgeGraphUseCase) Upsert(ctx context.Context, req domusecase.UpsertRequest) (*domusecase.GraphSummary, error) {
	topicID := strings.TrimSpace(req.TopicID)
	brandID := strings.TrimSpace(req.BrandID)
	if err := requireScope(req.TenantID, req.OwnerUID, brandID, topicID); err != nil {
		return nil, err
	}
	seed := strings.TrimSpace(req.Seed)
	if seed == "" {
		return nil, app.For(code.Brand).InputMissingRequired("seed is required")
	}
	graph := &entity.Graph{
		TenantID:       req.TenantID,
		OwnerUID:       req.OwnerUID,
		BrandID:        brandID,
		TopicID:        topicID,
		Seed:           seed,
		Nodes:          req.Nodes,
		Edges:          req.Edges,
		BraveSources:   req.BraveSources,
		ExpandStrategy: req.ExpandStrategy,
		PainTagCount:   req.PainTagCount,
		GeneratedAt:    req.GeneratedAt,
	}
	var (
		item *entity.Graph
		err  error
	)
	if topicID != "" {
		item, err = u.repo.UpsertByTopic(ctx, graph)
	} else {
		item, err = u.repo.UpsertByBrand(ctx, graph)
	}
	if err != nil {
		return nil, err
	}
	summary := toSummary(item)
	return &summary, nil
}

func (u *knowledgeGraphUseCase) UpdateNodes(ctx context.Context, req domusecase.UpdateNodesRequest) (*domusecase.GraphSummary, error) {
	topicID := strings.TrimSpace(req.TopicID)
	brandID := strings.TrimSpace(req.BrandID)
	if err := requireScope(req.TenantID, req.OwnerUID, brandID, topicID); err != nil {
		return nil, err
	}
	if len(req.Updates) == 0 {
		return nil, app.For(code.Brand).InputMissingRequired("updates is required")
	}
	var (
		current *entity.Graph
		err     error
	)
	if topicID != "" {
		current, err = u.repo.FindByTopic(ctx, req.TenantID, req.OwnerUID, topicID)
	} else {
		current, err = u.repo.FindByBrand(ctx, req.TenantID, req.OwnerUID, brandID)
	}
	if err != nil {
		return nil, err
	}
	changes := map[string]domusecase.NodeUpdate{}
	for _, update := range req.Updates {
		id := strings.TrimSpace(update.NodeID)
		if id == "" {
			continue
		}
		changes[id] = update
	}
	nodes := make([]libkg.Node, len(current.Nodes))
	copy(nodes, current.Nodes)
	for i := range nodes {
		update, ok := changes[nodes[i].ID]
		if !ok {
			continue
		}
		if update.SelectedForScan != nil {
			nodes[i].SelectedForScan = *update.SelectedForScan
		}
		if update.RelevanceTagsSet {
			nodes[i].PatrolRelevance = libkg.SanitizePatrolKeywordList(update.RelevanceTags)
		}
		if update.RecencyTagsSet {
			nodes[i].PatrolRecency = libkg.SanitizePatrolKeywordList(update.RecencyTags)
		}
		if update.RelevanceTagsSet || update.RecencyTagsSet {
			nodes[i].DerivedTags = libkg.DerivePatrolTagsForNode(nodes[i], libkg.PatrolTagInput{})
		}
	}
	painCount := libkg.CountPainTagCandidates(nodes)
	var item *entity.Graph
	if topicID != "" {
		item, err = u.repo.UpdateNodesByTopic(ctx, req.TenantID, req.OwnerUID, topicID, nodes, painCount)
	} else {
		item, err = u.repo.UpdateNodes(ctx, req.TenantID, req.OwnerUID, brandID, nodes, painCount)
	}
	if err != nil {
		return nil, err
	}
	summary := toSummary(item)
	return &summary, nil
}

func (u *knowledgeGraphUseCase) AttachTopicID(ctx context.Context, tenantID, ownerUID, brandID, topicID string) error {
	if err := requireScope(tenantID, ownerUID, brandID, topicID); err != nil {
		return err
	}
	return u.repo.AttachTopicID(ctx, tenantID, ownerUID, brandID, topicID)
}

func requireScope(tenantID, ownerUID, brandID, topicID string) error {
	if strings.TrimSpace(tenantID) == "" || strings.TrimSpace(ownerUID) == "" {
		return app.For(code.Brand).InputMissingRequired("tenant_id and uid are required")
	}
	if strings.TrimSpace(topicID) == "" && strings.TrimSpace(brandID) == "" {
		return app.For(code.Brand).InputMissingRequired("brand id or topic id is required")
	}
	return nil
}

func toSummary(item *entity.Graph) domusecase.GraphSummary {
	if item == nil {
		return domusecase.GraphSummary{}
	}
	return domusecase.GraphSummary{
		ID:             item.ID,
		BrandID:        libmongo.ResolveBrandID(item.BrandID, item.LegacyPersonaID),
		TopicID:        strings.TrimSpace(item.TopicID),
		Seed:           item.Seed,
		Nodes:          item.Nodes,
		Edges:          item.Edges,
		BraveSources:   item.BraveSources,
		ExpandStrategy: item.ExpandStrategy,
		PainTagCount:   item.PainTagCount,
		GeneratedAt:    item.GeneratedAt,
		CreateAt:       item.CreateAt,
		UpdateAt:       item.UpdateAt,
	}
}
