package methods

import (
	"github.com/tech/sendico/payments/storage"
	"github.com/tech/sendico/pkg/api/routers"
	"github.com/tech/sendico/pkg/merrors"
	msg "github.com/tech/sendico/pkg/messaging"
	mb "github.com/tech/sendico/pkg/messaging/broker"
	"github.com/tech/sendico/pkg/mlogger"
	methodsv1 "github.com/tech/sendico/pkg/proto/payments/methods/v1"
	"google.golang.org/grpc"
)

var errStoreUnavailable = merrors.Internal("payment-methods: storage is not initialised")

// Option configures service dependencies.
type Option func(*Service)

// WithRecipientEventsBroker wires the broker used to consume recipient events.
func WithRecipientEventsBroker(broker mb.Broker) Option {
	return func(s *Service) {
		if broker != nil {
			s.recipientBroker = broker
		}
	}
}

// Service implements payments.methods.v1.PaymentMethodsService.
type Service struct {
	logger  mlogger.Logger
	storage storage.Repository
	pmstore storage.PaymentMethodsStore

	recipientBroker    mb.Broker
	recipientConsumers []msg.Consumer

	methodsv1.UnimplementedPaymentMethodsServiceServer
}

// NewService creates a payment methods gRPC service.
func NewService(logger mlogger.Logger, repo storage.Repository, opts ...Option) (*Service, error) {
	if logger == nil {
		return nil, merrors.InvalidArgument("payment-methods: logger is required")
	}
	if repo == nil {
		return nil, merrors.InvalidArgument("payment-methods: storage repository is required")
	}

	pmstore := repo.PaymentMethods()
	if pmstore == nil {
		return nil, errStoreUnavailable
	}

	svc := &Service{
		logger:  logger.Named("payment_methods"),
		storage: repo,
		pmstore: pmstore,
	}

	for _, opt := range opts {
		if opt != nil {
			opt(svc)
		}
	}

	svc.startRecipientConsumers()
	return svc, nil
}

// Register attaches the service to the supplied gRPC router.
func (s *Service) Register(router routers.GRPC) error {
	return router.Register(func(reg grpc.ServiceRegistrar) {
		methodsv1.RegisterPaymentMethodsServiceServer(reg, s)
	})
}

// Shutdown releases underlying resources.
func (s *Service) Shutdown() {
	if s == nil {
		return
	}
	for _, consumer := range s.recipientConsumers {
		if consumer != nil {
			consumer.Close()
		}
	}
	s.recipientConsumers = nil
	s.pmstore = nil
	s.storage = nil
}
