package gateway

import (
	"context"
	"fmt"

	"github.com/tech/sendico/gateway/tron/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/payments/rail"
	"go.uber.org/zap"
)

func isFinalStatus(t *model.Transfer) bool {
	switch t.Status {
	case model.TransferStatusFailed, model.TransferStatusSuccess, model.TransferStatusCancelled:
		return true
	default:
		return false
	}
}

func toOpStatus(t *model.Transfer) rail.OperationResult {
	switch t.Status {
	case model.TransferStatusFailed:
		return rail.OperationResultFailed
	case model.TransferStatusSuccess:
		return rail.OperationResultSuccess
	case model.TransferStatusCancelled:
		return rail.OperationResultCancelled
	default:
		panic(fmt.Sprintf("toOpStatus: unexpected transfer status: %s", t.Status))
	}
}

func toError(t *model.Transfer) string {
	if t == nil {
		return ""
	}
	if t.Status == model.TransferStatusSuccess {
		return ""
	}
	return t.FailureReason
}

func (s *Service) updateTransferStatus(ctx context.Context, transferRef string, status model.TransferStatus, failureReason, txHash string) (*model.Transfer, error) {
	transfer, err := s.storage.Transfers().UpdateStatus(ctx, transferRef, status, failureReason, txHash)
	if err != nil {
		s.logger.Warn("Failed to update transfer status", zap.String("transfer_ref", transferRef), zap.String("status", string(status)), zap.Error(err))
	}
	if isFinalStatus(transfer) {
		s.emitTransferStatusEvent(transfer)
	}
	return transfer, err
}

func (s *Service) emitTransferStatusEvent(transfer *model.Transfer) {
	if s == nil || s.producer == nil || transfer == nil {
		return
	}

	exec := pmodel.PaymentGatewayExecution{
		PaymentIntentID: transfer.IntentRef,
		IdempotencyKey:  transfer.IdempotencyKey,
		ExecutedMoney:   transfer.NetAmount,
		PaymentRef:      transfer.PaymentRef,
		Status:          toOpStatus(transfer),
		OperationRef:    transfer.OperationRef,
		Error:           toError(transfer),
		TransferRef:     transfer.TransferRef,
	}
	env := paymentgateway.PaymentGatewayExecution(mservice.ChainGateway, &exec)
	if err := s.producer.SendMessage(env); err != nil {
		s.logger.Warn("Failed to publish transfer status event", zap.Error(err), zap.String("transfer_ref", transfer.TransferRef))
	}
}
