package gateway

import (
	"context"
	"fmt"

	"github.com/tech/sendico/gateway/mntx/storage/model"
	paymentgateway "github.com/tech/sendico/pkg/messaging/notifications/paymentgateway"
	pmodel "github.com/tech/sendico/pkg/model"
	"github.com/tech/sendico/pkg/mservice"
	"github.com/tech/sendico/pkg/mutil/mzap"
	"github.com/tech/sendico/pkg/payments/rail"
	paytypes "github.com/tech/sendico/pkg/payments/types"
	"go.uber.org/zap"
)

func isFinalStatus(t *model.CardPayout) bool {
	switch t.Status {
	case model.PayoutStatusFailed, model.PayoutStatusSuccess, model.PayoutStatusCancelled:
		return true
	default:
		return false
	}
}

func toOpStatus(t *model.CardPayout) rail.OperationResult {
	switch t.Status {
	case model.PayoutStatusFailed:
		return rail.OperationResultFailed
	case model.PayoutStatusSuccess:
		return rail.OperationResultSuccess
	case model.PayoutStatusCancelled:
		return rail.OperationResultCancelled
	default:
		panic(fmt.Sprintf("unexpected transfer status, %s", t.Status))
	}
}

func (p *cardPayoutProcessor) updatePayoutStatus(ctx context.Context, state *model.CardPayout) error {
	if err := p.store.Payouts().Upsert(ctx, state); err != nil {
		p.logger.Warn("Failed to update transfer status", zap.String("transfer_ref", state.PayoutID), zap.String("status", string(state.Status)), zap.Error(err))
	}
	if isFinalStatus(state) {
		p.emitTransferStatusEvent(state)
	}
	return nil
}

func (p *cardPayoutProcessor) emitTransferStatusEvent(payout *model.CardPayout) {
	if p == nil || p.producer == nil || payout == nil {
		return
	}

	exec := pmodel.PaymentGatewayExecution{
		PaymentIntentID: payout.IntentRef,
		IdempotencyKey:  payout.IdempotencyKey,
		ExecutedMoney: &paytypes.Money{
			Amount:   fmt.Sprintf("%d", payout.AmountMinor),
			Currency: payout.Currency,
		},
		PaymentRef:   payout.PaymentRef,
		Status:       toOpStatus(payout),
		OperationRef: payout.OperationRef,
		Error:        payout.FailureReason,
		TransferRef:  payout.GetID().Hex(),
	}
	env := paymentgateway.PaymentGatewayExecution(mservice.MntxGateway, &exec)
	if err := p.producer.SendMessage(env); err != nil {
		p.logger.Warn("Failed to publish transfer status event", zap.Error(err), mzap.ObjRef("transfer_ref", payout.ID))
	}
}
