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 := requireActor(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) Upsert(ctx context.Context, req domusecase.UpsertRequest) (*domusecase.GraphSummary, error) {
	if err := requireActor(req.TenantID, req.OwnerUID, req.BrandID); err != nil {
		return nil, err
	}
	seed := strings.TrimSpace(req.Seed)
	if seed == "" {
		return nil, app.For(code.Brand).InputMissingRequired("seed is required")
	}
	item, err := u.repo.UpsertByBrand(ctx, &entity.Graph{
		TenantID:     req.TenantID,
		OwnerUID:     req.OwnerUID,
		BrandID:      req.BrandID,
		Seed:         seed,
		Nodes:        req.Nodes,
		Edges:        req.Edges,
		BraveSources: req.BraveSources,
		PainTagCount: req.PainTagCount,
		GeneratedAt:  req.GeneratedAt,
	})
	if err != nil {
		return nil, err
	}
	summary := toSummary(item)
	return &summary, nil
}

func (u *knowledgeGraphUseCase) UpdateNodeSelections(ctx context.Context, req domusecase.UpdateNodesRequest) (*domusecase.GraphSummary, error) {
	if err := requireActor(req.TenantID, req.OwnerUID, req.BrandID); err != nil {
		return nil, err
	}
	if len(req.Updates) == 0 {
		return nil, app.For(code.Brand).InputMissingRequired("updates is required")
	}
	current, err := u.repo.FindByBrand(ctx, req.TenantID, req.OwnerUID, req.BrandID)
	if err != nil {
		return nil, err
	}
	selections := map[string]bool{}
	for _, update := range req.Updates {
		id := strings.TrimSpace(update.NodeID)
		if id == "" {
			continue
		}
		selections[id] = update.SelectedForScan
	}
	nodes := make([]libkg.Node, len(current.Nodes))
	copy(nodes, current.Nodes)
	for i := range nodes {
		if selected, ok := selections[nodes[i].ID]; ok {
			nodes[i].SelectedForScan = selected
		}
	}
	painCount := libkg.CountPainTagCandidates(nodes)
	item, err := u.repo.UpdateNodes(ctx, req.TenantID, req.OwnerUID, req.BrandID, nodes, painCount)
	if err != nil {
		return nil, err
	}
	summary := toSummary(item)
	return &summary, nil
}

func requireActor(tenantID, ownerUID, brandID string) error {
	if strings.TrimSpace(tenantID) == "" || strings.TrimSpace(ownerUID) == "" {
		return app.For(code.Brand).InputMissingRequired("tenant_id and uid are required")
	}
	if strings.TrimSpace(brandID) == "" {
		return app.For(code.Brand).InputMissingRequired("brand 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),
		Seed:         item.Seed,
		Nodes:        item.Nodes,
		Edges:        item.Edges,
		BraveSources: item.BraveSources,
		PainTagCount: item.PainTagCount,
		GeneratedAt:  item.GeneratedAt,
		UpdateAt:     item.UpdateAt,
		CreateAt:     item.CreateAt,
	}
}
