package mongo

import (
	"context"

	"github.com/tech/sendico/payments/orchestrator/storage"
	"github.com/tech/sendico/payments/orchestrator/storage/model"
	"github.com/tech/sendico/payments/orchestrator/storage/mongo/store"
	"github.com/tech/sendico/pkg/db"
	"github.com/tech/sendico/pkg/db/repository"
	"github.com/tech/sendico/pkg/merrors"
	"github.com/tech/sendico/pkg/mlogger"
)

// Store implements storage.Repository backed by MongoDB.
type Store struct {
	logger mlogger.Logger
	ping   func(context.Context) error

	payments storage.PaymentsStore
}

// New constructs a Mongo-backed payments repository from a Mongo connection.
func New(logger mlogger.Logger, conn *db.MongoConnection) (*Store, error) {
	if conn == nil {
		return nil, merrors.InvalidArgument("payments.storage.mongo: connection is nil")
	}
	repo := repository.CreateMongoRepository(conn.Database(), (&model.Payment{}).Collection())
	return NewWithRepository(logger, conn.Ping, repo)
}

// NewWithRepository constructs a payments repository using the provided primitives.
func NewWithRepository(logger mlogger.Logger, ping func(context.Context) error, paymentsRepo repository.Repository) (*Store, error) {
	if ping == nil {
		return nil, merrors.InvalidArgument("payments.storage.mongo: ping func is nil")
	}
	if paymentsRepo == nil {
		return nil, merrors.InvalidArgument("payments.storage.mongo: payments repository is nil")
	}

	childLogger := logger.Named("storage").Named("mongo")
	paymentsStore, err := store.NewPayments(childLogger, paymentsRepo)
	if err != nil {
		return nil, err
	}
	result := &Store{
		logger:   childLogger,
		ping:     ping,
		payments: paymentsStore,
	}

	return result, nil
}

// Ping verifies connectivity with the backing database.
func (s *Store) Ping(ctx context.Context) error {
	if s.ping == nil {
		return merrors.InvalidArgument("payments.storage.mongo: ping func is nil")
	}
	return s.ping(ctx)
}

// Payments returns the payments store.
func (s *Store) Payments() storage.PaymentsStore {
	return s.payments
}

var _ storage.Repository = (*Store)(nil)
