package orchestrator

import (
	"context"
	"testing"

	ledgerclient "github.com/tech/sendico/ledger/client"
	"github.com/tech/sendico/payments/orchestrator/storage/model"
	"github.com/tech/sendico/pkg/model/account_role"
	paymenttypes "github.com/tech/sendico/pkg/payments/types"
	connectorv1 "github.com/tech/sendico/pkg/proto/connector/v1"
	ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1"
	orchestratorv1 "github.com/tech/sendico/pkg/proto/payments/orchestrator/v1"
	"go.mongodb.org/mongo-driver/v2/bson"
	"go.uber.org/zap"
	"google.golang.org/protobuf/types/known/structpb"
)

func TestLedgerAccountResolution_UsesRoleAccounts(t *testing.T) {
	ctx := context.Background()
	fromAccountID := "ledger:operating:usd"
	toAccountID := "ledger:transit:usd"

	operatingDetails, err := structpb.NewStruct(map[string]interface{}{
		"role": "ACCOUNT_ROLE_OPERATING",
	})
	if err != nil {
		t.Fatalf("provider details build error: %v", err)
	}
	transitDetails, err := structpb.NewStruct(map[string]interface{}{
		"role": "ACCOUNT_ROLE_TRANSIT",
	})
	if err != nil {
		t.Fatalf("provider details build error: %v", err)
	}

	listCalls := 0
	var transferReq *ledgerv1.TransferRequest
	ledgerFake := &ledgerclient.Fake{
		ListConnectorAccountsFn: func(ctx context.Context, req *connectorv1.ListAccountsRequest) (*connectorv1.ListAccountsResponse, error) {
			listCalls++
			return &connectorv1.ListAccountsResponse{
				Accounts: []*connectorv1.Account{
					{
						Ref:             &connectorv1.AccountRef{ConnectorId: "ledger", AccountId: fromAccountID},
						Kind:            connectorv1.AccountKind_LEDGER_ACCOUNT,
						Asset:           "USD",
						OwnerRef:        "",
						ProviderDetails: operatingDetails,
					},
					{
						Ref:             &connectorv1.AccountRef{ConnectorId: "ledger", AccountId: toAccountID},
						Kind:            connectorv1.AccountKind_LEDGER_ACCOUNT,
						Asset:           "USD",
						OwnerRef:        "",
						ProviderDetails: transitDetails,
					},
				},
			}, nil
		},
		TransferInternalFn: func(ctx context.Context, req *ledgerv1.TransferRequest) (*ledgerv1.PostResponse, error) {
			transferReq = req
			return &ledgerv1.PostResponse{JournalEntryRef: "entry-1"}, nil
		},
	}

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

	amount := &paymenttypes.Money{Currency: "USD", Amount: "10"}
	payment := &model.Payment{
		PaymentRef:     "pay-1",
		IdempotencyKey: "pay-1",
		Intent: model.PaymentIntent{
			Ref:  "ref-1",
			Kind: model.PaymentKindPayout,
		},
		PaymentPlan: &model.PaymentPlan{
			ID:             "pay-1",
			IdempotencyKey: "pay-1",
			Steps: []*model.PaymentStep{
				{StepID: "ledger_move", Rail: model.RailLedger, Action: model.RailOperationMove, Amount: cloneMoney(amount), FromRole: rolePtr(account_role.AccountRoleOperating), ToRole: rolePtr(account_role.AccountRoleTransit)},
			},
		},
	}
	payment.OrganizationRef = bson.NewObjectID()

	store := newStubPaymentsStore()
	store.payments[payment.PaymentRef] = payment

	if err := executor.executePaymentPlan(ctx, store, payment, &orchestratorv1.PaymentQuote{}); err != nil {
		t.Fatalf("executePaymentPlan error: %v", err)
	}
	if listCalls == 0 {
		t.Fatalf("expected ledger accounts lookup")
	}
	if transferReq == nil {
		t.Fatalf("expected ledger transfer")
	}
	if transferReq.GetFromLedgerAccountRef() != fromAccountID {
		t.Fatalf("expected from account %s, got %s", fromAccountID, transferReq.GetFromLedgerAccountRef())
	}
	if transferReq.GetToLedgerAccountRef() != toAccountID {
		t.Fatalf("expected to account %s, got %s", toAccountID, transferReq.GetToLedgerAccountRef())
	}
}
