package mutil

import (
	"context"
	"errors"

	"github.com/tech/sendico/pkg/auth"
	"github.com/tech/sendico/pkg/db/repository"
	"github.com/tech/sendico/pkg/db/repository/builder"
	"github.com/tech/sendico/pkg/merrors"
	"github.com/tech/sendico/pkg/mlogger"
	"github.com/tech/sendico/pkg/model"
	mutil "github.com/tech/sendico/pkg/mutil/db"
	"github.com/tech/sendico/pkg/mutil/mzap"
	"go.mongodb.org/mongo-driver/v2/bson"
	"go.uber.org/zap"
)

func GetProtectedObjects[T any](
	ctx context.Context,
	logger mlogger.Logger,
	accountRef, organizationRef bson.ObjectID,
	action model.Action,
	filter builder.Query,
	cursor *model.ViewCursor,
	enforcer auth.Enforcer,
	repo repository.Repository,
) ([]T, error) {
	refs, err := repo.ListPermissionBound(ctx, repository.ApplyCursor(filter, cursor))
	if err != nil {
		if !errors.Is(err, merrors.ErrNoData) {
			logger.Warn("Failed to fetch object IDs", zap.Error(err), mzap.AccRef(accountRef),
				mzap.ObjRef("organization_ref", organizationRef), zap.String("action", string(action)))
		} else {
			logger.Debug("No matching IDs found", zap.Error(err), mzap.AccRef(accountRef),
				mzap.ObjRef("organization_ref", organizationRef), zap.String("action", string(action)))
		}
		return nil, err
	}
	res, err := enforcer.EnforceBatch(ctx, refs, accountRef, action)
	if err != nil {
		logger.Warn("Failed to enforce object IDs", zap.Error(err), mzap.AccRef(accountRef),
			mzap.ObjRef("organization_ref", organizationRef), zap.String("action", string(action)))
		return nil, err
	}

	allowed := make([]bson.ObjectID, 0, len(res))
	for _, ref := range refs {
		if ok := res[*ref.GetID()]; ok {
			allowed = append(allowed, *ref.GetID())
		}
	}
	if len(allowed) == 0 {
		return nil, merrors.NoData("no_data_found_or_allowed")
	}

	return mutil.GetObjects[T](ctx, logger, repository.Query().In(repository.IDField(), allowed), cursor, repo)
}
