package store

import (
	"context"
	"errors"
	"strings"
	"time"

	"github.com/tech/sendico/gateway/tgsettle/storage"
	"github.com/tech/sendico/gateway/tgsettle/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/mongo"
	"go.uber.org/zap"
)

const (
	telegramCollection = "telegram_confirmations"
	fieldRequestID     = "requestId"
)

type TelegramConfirmations struct {
	logger mlogger.Logger
	repo   repository.Repository
}

func NewTelegramConfirmations(logger mlogger.Logger, db *mongo.Database) (*TelegramConfirmations, error) {
	if db == nil {
		return nil, merrors.InvalidArgument("mongo database is nil")
	}
	if logger == nil {
		logger = zap.NewNop()
	}
	logger = logger.Named("telegram_confirmations").With(zap.String("collection", telegramCollection))

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

	t := &TelegramConfirmations{
		logger: logger,
		repo:   repo,
	}
	t.logger.Debug("Telegram confirmations store initialised")
	return t, nil
}

func (t *TelegramConfirmations) Upsert(ctx context.Context, record *model.TelegramConfirmation) error {
	if record == nil {
		return merrors.InvalidArgument("telegram confirmation is nil", "record")
	}
	record.RequestID = strings.TrimSpace(record.RequestID)
	record.PaymentIntentID = strings.TrimSpace(record.PaymentIntentID)
	record.QuoteRef = strings.TrimSpace(record.QuoteRef)
	if record.RequestID == "" {
		return merrors.InvalidArgument("request_id is required", "request_id")
	}
	if record.ReceivedAt.IsZero() {
		record.ReceivedAt = time.Now()
	}
	filter := repository.Filter(fieldRequestID, record.RequestID)
	err := t.repo.Insert(ctx, record, filter)
	if errors.Is(err, merrors.ErrDataConflict) {
		patch := repository.Patch().
			Set(repository.Field("paymentIntentId"), record.PaymentIntentID).
			Set(repository.Field("quoteRef"), record.QuoteRef).
			Set(repository.Field("rawReply"), record.RawReply).
			Set(repository.Field("receivedAt"), record.ReceivedAt)
		_, err = t.repo.PatchMany(ctx, filter, patch)
	}
	if err != nil && !errors.Is(err, context.Canceled) && !errors.Is(err, context.DeadlineExceeded) {
		fields := []zap.Field{zap.String("request_id", record.RequestID)}
		if record.PaymentIntentID != "" {
			fields = append(fields, zap.String("payment_intent_id", record.PaymentIntentID))
		}
		if record.QuoteRef != "" {
			fields = append(fields, zap.String("quote_ref", record.QuoteRef))
		}
		t.logger.Warn("Failed to upsert telegram confirmation", append(fields, zap.Error(err))...)
	}
	return err
}

var _ storage.TelegramConfirmationsStore = (*TelegramConfirmations)(nil)
