package gateway_funding_profile

import (
	"context"
	"testing"

	"github.com/tech/sendico/payments/storage/model"
	"github.com/tech/sendico/pkg/model/account_role"
	paymenttypes "github.com/tech/sendico/pkg/payments/types"
	moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
)

func TestStaticFundingProfileResolver_ExplicitCardRoute(t *testing.T) {
	resolver := NewStaticFundingProfileResolver(StaticFundingProfileResolverInput{
		DefaultMode: model.FundingModeNone,
		CardRoutes: map[string]CardGatewayFundingRoute{
			paymenttypes.DefaultCardsGatewayID: {
				FundingAddress: "T-FUNDING",
				FeeWalletRef:   "wallet-fee",
			},
		},
		FeeLedgerAccounts: map[string]string{
			paymenttypes.DefaultCardsGatewayID: "ledger:fees",
		},
	})

	profile, err := resolver.ResolveGatewayFundingProfile(context.Background(), FundingProfileRequest{
		OrganizationRef: "org-1",
		Amount: &moneyv1.Money{
			Amount:   "100",
			Currency: "USDT",
		},
		Source: &model.PaymentEndpoint{
			Type: model.EndpointTypeManagedWallet,
			ManagedWallet: &model.ManagedWalletEndpoint{
				ManagedWalletRef: "wallet-src",
				Asset: &paymenttypes.Asset{
					Chain:       "TRON_MAINNET",
					TokenSymbol: "USDT",
				},
			},
		},
		Destination: &model.PaymentEndpoint{
			Type: model.EndpointTypeCard,
			Card: &model.CardEndpoint{
				MaskedPan: "****",
			},
		},
		Attributes: map[string]string{
			"gateway":       paymenttypes.DefaultCardsGatewayID,
			"initiator_ref": "usr-1",
		},
	})
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if profile == nil {
		t.Fatalf("expected profile")
	}
	if profile.GatewayID != paymenttypes.DefaultCardsGatewayID {
		t.Fatalf("expected gateway %s, got %q", paymenttypes.DefaultCardsGatewayID, profile.GatewayID)
	}
	if profile.Mode != model.FundingModeNone {
		t.Fatalf("expected mode none, got %q", profile.Mode)
	}
	if profile.FundingDestination == nil || profile.FundingDestination.ExternalChain == nil {
		t.Fatalf("expected funding destination")
	}
	if got := profile.FundingDestination.ExternalChain.Address; got != "T-FUNDING" {
		t.Fatalf("expected funding address T-FUNDING, got %q", got)
	}
	if profile.FeeDestination == nil || profile.FeeDestination.ManagedWallet == nil {
		t.Fatalf("expected managed wallet fee destination")
	}
	if got := profile.FeeDestination.ManagedWallet.ManagedWalletRef; got != "wallet-fee" {
		t.Fatalf("expected fee wallet wallet-fee, got %q", got)
	}
	if got := profile.Defaults["fee_ledger_account_ref"]; got != "ledger:fees" {
		t.Fatalf("expected fee ledger account mapping, got %q", got)
	}
}

func TestStaticFundingProfileResolver_ReserveFromSourceAndAttrs(t *testing.T) {
	resolver := NewStaticFundingProfileResolver(StaticFundingProfileResolverInput{
		GatewayModes: map[string]model.FundingMode{
			"mntx": model.FundingModeBalanceReserve,
		},
	})

	profile, err := resolver.ResolveGatewayFundingProfile(context.Background(), FundingProfileRequest{
		GatewayID: "mntx",
		Source: &model.PaymentEndpoint{
			Type: model.EndpointTypeLedger,
			Ledger: &model.LedgerEndpoint{
				LedgerAccountRef:       "ledger:operating",
				ContraLedgerAccountRef: "ledger:hold",
			},
		},
		Attributes: map[string]string{
			"ledger_block_account_ref": "ledger:block",
			"from_role":                string(account_role.AccountRoleOperating),
			"to_role":                  string(account_role.AccountRoleHold),
		},
	})
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if profile == nil {
		t.Fatalf("expected profile")
	}
	if profile.Mode != model.FundingModeBalanceReserve {
		t.Fatalf("expected balance_reserve mode, got %q", profile.Mode)
	}
	if profile.Reserve == nil {
		t.Fatalf("expected reserve policy")
	}
	if got := profile.Reserve.DebitAccountRef; got != "ledger:operating" {
		t.Fatalf("expected debit account ledger:operating, got %q", got)
	}
	if got := profile.Reserve.BlockAccountRef; got != "ledger:block" {
		t.Fatalf("expected block account ledger:block, got %q", got)
	}
	if profile.Reserve.FromRole == nil || *profile.Reserve.FromRole != account_role.AccountRoleOperating {
		t.Fatalf("expected from role operating, got %#v", profile.Reserve.FromRole)
	}
	if profile.Reserve.ToRole == nil || *profile.Reserve.ToRole != account_role.AccountRoleHold {
		t.Fatalf("expected to role hold, got %#v", profile.Reserve.ToRole)
	}
}

func TestStaticFundingProfileResolver_EmptyInputReturnsNil(t *testing.T) {
	resolver := NewStaticFundingProfileResolver(StaticFundingProfileResolverInput{})

	profile, err := resolver.ResolveGatewayFundingProfile(context.Background(), FundingProfileRequest{
		OrganizationRef: "org-1",
		Amount: &moneyv1.Money{
			Amount:   "10",
			Currency: "USD",
		},
	})
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if profile != nil {
		t.Fatalf("expected nil profile")
	}
}

func TestStaticFundingProfileResolver_ConfiguredProfileCloned(t *testing.T) {
	resolver := NewStaticFundingProfileResolver(StaticFundingProfileResolverInput{
		Profiles: map[string]*GatewayFundingProfile{
			paymenttypes.DefaultCardsGatewayID: {
				Mode: model.FundingModeDepositObserved,
				DepositCheck: &model.DepositCheckPolicy{
					WalletRef: "wallet-deposit",
					ExpectedAmount: &moneyv1.Money{
						Amount:   "15",
						Currency: "USDT",
					},
					MinConfirmations: 2,
				},
			},
		},
	})

	first, err := resolver.ResolveGatewayFundingProfile(context.Background(), FundingProfileRequest{
		GatewayID: paymenttypes.DefaultCardsGatewayID,
	})
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if first == nil || first.DepositCheck == nil {
		t.Fatalf("expected configured profile")
	}
	first.DepositCheck.WalletRef = "changed"

	second, err := resolver.ResolveGatewayFundingProfile(context.Background(), FundingProfileRequest{
		GatewayID: paymenttypes.DefaultCardsGatewayID,
	})
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if second == nil || second.DepositCheck == nil {
		t.Fatalf("expected configured profile")
	}
	if second.DepositCheck.WalletRef != "wallet-deposit" {
		t.Fatalf("expected cloned profile, got %q", second.DepositCheck.WalletRef)
	}
}
