package orchestrator

import (
	"context"
	"testing"
	"time"

	oracleclient "github.com/tech/sendico/fx/oracle/client"
	feesv1 "github.com/tech/sendico/pkg/proto/billing/fees/v1"
	moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
	orchestratorv1 "github.com/tech/sendico/pkg/proto/payments/orchestrator/v1"
	"go.uber.org/zap"
	"google.golang.org/grpc"
)

type feeEngineFake struct {
	precomputeCalls int
}

func (f *feeEngineFake) QuoteFees(ctx context.Context, in *feesv1.QuoteFeesRequest, opts ...grpc.CallOption) (*feesv1.QuoteFeesResponse, error) {
	return &feesv1.QuoteFeesResponse{}, nil
}

func (f *feeEngineFake) PrecomputeFees(ctx context.Context, in *feesv1.PrecomputeFeesRequest, opts ...grpc.CallOption) (*feesv1.PrecomputeFeesResponse, error) {
	f.precomputeCalls++
	return &feesv1.PrecomputeFeesResponse{}, nil
}

func (f *feeEngineFake) ValidateFeeToken(ctx context.Context, in *feesv1.ValidateFeeTokenRequest, opts ...grpc.CallOption) (*feesv1.ValidateFeeTokenResponse, error) {
	return &feesv1.ValidateFeeTokenResponse{}, nil
}

func TestBuildPaymentQuote_RequestsFXWhenSettlementDiffers(t *testing.T) {
	ctx := context.Background()
	calls := 0

	svc := &Service{
		logger: zap.NewNop(),
		clock:  testClock{now: time.Now()},
		deps: serviceDependencies{
			oracle: oracleDependency{
				client: &oracleclient.Fake{
					GetQuoteFn: func(ctx context.Context, params oracleclient.GetQuoteParams) (*oracleclient.Quote, error) {
						calls++
						return &oracleclient.Quote{
							QuoteRef:    "q1",
							Pair:        params.Pair,
							Side:        params.Side,
							Price:       "1.1",
							BaseAmount:  params.BaseAmount,
							QuoteAmount: params.QuoteAmount,
							ExpiresAt:   time.Now().Add(time.Minute),
						}, nil
					},
				},
			},
		},
	}

	req := &orchestratorv1.QuotePaymentRequest{
		Meta: &orchestratorv1.RequestMeta{OrganizationRef: "org"},
		Intent: &orchestratorv1.PaymentIntent{
			Source: &orchestratorv1.PaymentEndpoint{
				Endpoint: &orchestratorv1.PaymentEndpoint_Ledger{Ledger: &orchestratorv1.LedgerEndpoint{LedgerAccountRef: "ledger:source"}},
			},
			Destination: &orchestratorv1.PaymentEndpoint{
				Endpoint: &orchestratorv1.PaymentEndpoint_Ledger{Ledger: &orchestratorv1.LedgerEndpoint{LedgerAccountRef: "ledger:dest"}},
			},
			Amount:             &moneyv1.Money{Currency: "USD", Amount: "100"},
			SettlementCurrency: "EUR",
		},
	}

	if _, _, err := svc.buildPaymentQuote(ctx, "org", req); err != nil {
		t.Fatalf("buildPaymentQuote returned error: %v", err)
	}
	if calls != 1 {
		t.Fatalf("expected 1 fx quote call, got %d", calls)
	}
}

func TestBuildPaymentQuote_FeesRequestedForExternalRails(t *testing.T) {
	ctx := context.Background()
	feeFake := &feeEngineFake{}

	svc := &Service{
		logger: zap.NewNop(),
		clock:  testClock{now: time.Now()},
		deps: serviceDependencies{
			fees: feesDependency{client: feeFake},
		},
	}

	req := &orchestratorv1.QuotePaymentRequest{
		Meta: &orchestratorv1.RequestMeta{OrganizationRef: "org"},
		Intent: &orchestratorv1.PaymentIntent{
			Source: &orchestratorv1.PaymentEndpoint{
				Endpoint: &orchestratorv1.PaymentEndpoint_Ledger{Ledger: &orchestratorv1.LedgerEndpoint{LedgerAccountRef: "ledger:source"}},
			},
			Destination: &orchestratorv1.PaymentEndpoint{
				Endpoint: &orchestratorv1.PaymentEndpoint_Card{Card: &orchestratorv1.CardEndpoint{}},
			},
			Amount:             &moneyv1.Money{Currency: "USD", Amount: "100"},
			SettlementCurrency: "USD",
		},
	}

	if _, _, err := svc.buildPaymentQuote(ctx, "org", req); err != nil {
		t.Fatalf("buildPaymentQuote returned error: %v", err)
	}
	if feeFake.precomputeCalls != 1 {
		t.Fatalf("expected 1 fee precompute call, got %d", feeFake.precomputeCalls)
	}
}

func TestBuildPaymentQuote_FeesSkippedForLedgerTransfer(t *testing.T) {
	ctx := context.Background()
	feeFake := &feeEngineFake{}

	svc := &Service{
		logger: zap.NewNop(),
		clock:  testClock{now: time.Now()},
		deps: serviceDependencies{
			fees: feesDependency{client: feeFake},
		},
	}

	req := &orchestratorv1.QuotePaymentRequest{
		Meta: &orchestratorv1.RequestMeta{OrganizationRef: "org"},
		Intent: &orchestratorv1.PaymentIntent{
			Source: &orchestratorv1.PaymentEndpoint{
				Endpoint: &orchestratorv1.PaymentEndpoint_Ledger{Ledger: &orchestratorv1.LedgerEndpoint{LedgerAccountRef: "ledger:source"}},
			},
			Destination: &orchestratorv1.PaymentEndpoint{
				Endpoint: &orchestratorv1.PaymentEndpoint_Ledger{Ledger: &orchestratorv1.LedgerEndpoint{LedgerAccountRef: "ledger:dest"}},
			},
			Amount:             &moneyv1.Money{Currency: "USD", Amount: "100"},
			SettlementCurrency: "USD",
		},
	}

	if _, _, err := svc.buildPaymentQuote(ctx, "org", req); err != nil {
		t.Fatalf("buildPaymentQuote returned error: %v", err)
	}
	if feeFake.precomputeCalls != 0 {
		t.Fatalf("expected fee precompute to be skipped, got %d", feeFake.precomputeCalls)
	}
}
