package auth

import (
	"context"

	"github.com/tech/sendico/pkg/db/policy"
	"github.com/tech/sendico/pkg/db/repository/builder"
	"github.com/tech/sendico/pkg/db/template"
	"github.com/tech/sendico/pkg/mlogger"
	"github.com/tech/sendico/pkg/model"
	"github.com/tech/sendico/pkg/mservice"
	"go.mongodb.org/mongo-driver/v2/bson"
	"go.mongodb.org/mongo-driver/v2/mongo"
	"go.uber.org/zap"
)

type AccountBoundDB[T model.AccountBoundStorable] interface {
	Create(ctx context.Context, accountRef bson.ObjectID, object T) error
	Get(ctx context.Context, accountRef, objectRef bson.ObjectID, result T) error
	Update(ctx context.Context, accountRef bson.ObjectID, object T) error
	Patch(ctx context.Context, accountRef, objectRef bson.ObjectID, patch builder.Patch) error
	Delete(ctx context.Context, accountRef, objectRef bson.ObjectID) error
	DeleteMany(ctx context.Context, accountRef bson.ObjectID, query builder.Query) error
	FindOne(ctx context.Context, accountRef bson.ObjectID, query builder.Query, result T) error
	ListIDs(ctx context.Context, accountRef bson.ObjectID, query builder.Query) ([]bson.ObjectID, error)
	ListAccountBound(ctx context.Context, accountRef, organizationRef bson.ObjectID, query builder.Query) ([]model.AccountBoundStorable, error)
}

func CreateAccountBound[T model.AccountBoundStorable](
	ctx context.Context,
	logger mlogger.Logger,
	pdb policy.DB,
	enforcer Enforcer,
	collection mservice.Type,
	db *mongo.Database,
) (AccountBoundDB[T], error) {
	logger = logger.Named("account_bound")
	var policy model.PolicyDescription
	if err := pdb.GetBuiltInPolicy(ctx, mservice.Organizations, &policy); err != nil {
		logger.Warn("Failed to fetch organization policy description", zap.Error(err))
		return nil, err
	}
	res := &AccountBoundDBImp[T]{
		Logger:        logger,
		DBImp:         template.Create[T](logger, collection, db),
		Enforcer:      enforcer,
		PermissionRef: policy.ID,
		Collection:    collection,
	}
	return res, nil
}
