package orchestrator

import (
	"context"
	"testing"

	ledgerclient "github.com/tech/sendico/ledger/client"
	"github.com/tech/sendico/payments/orchestrator/storage/model"
	mo "github.com/tech/sendico/pkg/model"
	paymenttypes "github.com/tech/sendico/pkg/payments/types"
	ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.uber.org/zap"
)

func TestReleasePaymentHold_TransfersFromHoldAccount(t *testing.T) {
	ctx := context.Background()

	store := newStubPaymentsStore()
	repo := &stubRepository{store: store}

	var releaseReq *ledgerv1.TransferRequest
	ledgerFake := &ledgerclient.Fake{
		TransferInternalFn: func(ctx context.Context, req *ledgerv1.TransferRequest) (*ledgerv1.PostResponse, error) {
			releaseReq = req
			return &ledgerv1.PostResponse{JournalEntryRef: "release-1"}, nil
		},
	}

	svc := &Service{
		logger:  zap.NewNop(),
		storage: repo,
		deps: serviceDependencies{
			ledger: ledgerDependency{
				client:   ledgerFake,
				internal: ledgerFake,
			},
		},
	}

	executor := newPaymentExecutor(&svc.deps, svc.logger, svc)

	payment := &model.Payment{
		PaymentRef:     "pay-release-1",
		IdempotencyKey: "pay-release-1",
		OrganizationBoundBase: mo.OrganizationBoundBase{
			OrganizationRef: primitive.NewObjectID(),
		},
		Intent: model.PaymentIntent{
			Kind: model.PaymentKindPayout,
			Source: model.PaymentEndpoint{
				Type: model.EndpointTypeManagedWallet,
				ManagedWallet: &model.ManagedWalletEndpoint{
					ManagedWalletRef: "wallet-src",
				},
			},
			Attributes: map[string]string{
				"ledger_debit_account_ref": "ledger:debit",
				"ledger_block_account_ref": "ledger:block",
			},
		},
		PaymentPlan: &model.PaymentPlan{
			ID:             "pay-release-1",
			IdempotencyKey: "pay-release-1",
			Steps: []*model.PaymentStep{
				{StepID: "ledger_block", Rail: model.RailLedger, Action: model.RailOperationBlock, Amount: &paymenttypes.Money{Currency: "USD", Amount: "100"}},
				{StepID: "ledger_release", Rail: model.RailLedger, Action: model.RailOperationRelease, DependsOn: []string{"ledger_block"}, Amount: &paymenttypes.Money{Currency: "USD", Amount: "100"}},
			},
		},
	}

	store.payments[payment.PaymentRef] = payment

	execPlan := ensureExecutionPlanForPlan(payment, payment.PaymentPlan)
	steps := executionStepsByCode(execPlan)
	blockStep := steps["ledger_block"]
	if blockStep == nil {
		t.Fatalf("expected block step in execution plan")
	}
	setExecutionStepStatus(blockStep, executionStepStatusConfirmed)

	if err := executor.releasePaymentHold(ctx, store, payment); err != nil {
		t.Fatalf("releasePaymentHold error: %v", err)
	}

	if releaseReq == nil {
		t.Fatalf("expected ledger release transfer")
	}
	if releaseReq.GetFromLedgerAccountRef() != "ledger:block" {
		t.Fatalf("unexpected release from account: %s", releaseReq.GetFromLedgerAccountRef())
	}
	if releaseReq.GetToLedgerAccountRef() != "ledger:debit" {
		t.Fatalf("unexpected release to account: %s", releaseReq.GetToLedgerAccountRef())
	}

	steps = executionStepsByCode(payment.ExecutionPlan)
	releaseStep := steps["ledger_release"]
	if releaseStep == nil {
		t.Fatalf("expected release step in execution plan")
	}
	if executionStepStatus(releaseStep) != executionStepStatusConfirmed {
		t.Fatalf("expected release step confirmed, got %s", executionStepStatus(releaseStep))
	}
	if releaseStep.TransferRef != "release-1" {
		t.Fatalf("expected release transfer ref set, got %s", releaseStep.TransferRef)
	}
}
