package store

import (
	"context"
	"strings"

	storage "github.com/tech/sendico/gateway/mntx/storage"
	"github.com/tech/sendico/gateway/mntx/storage/model"
	"github.com/tech/sendico/pkg/db/repository"
	ri "github.com/tech/sendico/pkg/db/repository/index"
	"github.com/tech/sendico/pkg/merrors"
	"github.com/tech/sendico/pkg/mlogger"
	"go.mongodb.org/mongo-driver/v2/bson"
	"go.mongodb.org/mongo-driver/v2/mongo"
	"go.uber.org/zap"
)

const (
	payoutsCollection = "card_payouts"
	payoutIdemField   = "idempotencyKey"
	payoutIdField     = "payoutId"
)

type Payouts struct {
	logger     mlogger.Logger
	repository repository.Repository
}

func NewPayouts(logger mlogger.Logger, db *mongo.Database) (*Payouts, error) {
	if db == nil {
		return nil, merrors.InvalidArgument("mongo database is nil")
	}
	if logger == nil {
		logger = zap.NewNop()
	}
	logger = logger.Named("payouts").With(zap.String("collection", payoutsCollection))

	repo := repository.CreateMongoRepository(db, payoutsCollection)
	if err := repo.CreateIndex(&ri.Definition{
		Keys:   []ri.Key{{Field: payoutIdemField, Sort: ri.Asc}},
		Unique: true,
	}); err != nil {
		logger.Error("Failed to create payouts idempotency index",
			zap.Error(err),
			zap.String("index_field", payoutIdemField))
		return nil, err
	}

	p := &Payouts{
		logger:     logger,
		repository: repo,
	}
	p.logger.Debug("Payouts store initialised")
	return p, nil
}

func (p *Payouts) findOneByField(ctx context.Context, field, value string) (*model.CardPayout, error) {
	var res model.CardPayout
	return &res, p.repository.FindOneByFilter(ctx, repository.Filter(field, value), &res)
}

func (p *Payouts) FindByIdempotencyKey(ctx context.Context, key string) (*model.CardPayout, error) {
	return p.findOneByField(ctx, payoutIdemField, key)
}

func (p *Payouts) FindByPaymentID(ctx context.Context, paymentID string) (*model.CardPayout, error) {
	return p.findOneByField(ctx, payoutIdField, paymentID)
}

func (p *Payouts) Upsert(ctx context.Context, record *model.CardPayout) error {
	if record == nil {
		return merrors.InvalidArgument("payout record is nil", "record")
	}

	record.OperationRef = strings.TrimSpace(record.OperationRef)
	record.PayoutID = strings.TrimSpace(record.PayoutID)
	record.CustomerID = strings.TrimSpace(record.CustomerID)
	record.ProviderCode = strings.TrimSpace(record.ProviderCode)
	record.ProviderPaymentID = strings.TrimSpace(record.ProviderPaymentID)

	if record.OperationRef == "" {
		return merrors.InvalidArgument("operation ref is required", "operation_ref")
	}

	if record.ID == bson.NilObjectID {
		return p.repository.Insert(ctx, record, nil)
	}

	return p.repository.Update(ctx, record)
}

var _ storage.PayoutsStore = (*Payouts)(nil)
