package fx

import (
	"errors"
	"testing"
	"time"

	"github.com/niels/banking-app/backend/internal/domain"
)

func TestApplySpread(t *testing.T) {
	got, err := applySpread(1_080_000, 25)
	if err != nil {
		t.Fatalf("applySpread returned error: %v", err)
	}
	if got != 1_077_300 {
		t.Fatalf("expected 1077300, got %d", got)
	}
}

func TestConvertAmount(t *testing.T) {
	got, err := convertAmount(10_000, 1_077_300)
	if err != nil {
		t.Fatalf("convertAmount returned error: %v", err)
	}
	if got != 10_773 {
		t.Fatalf("expected 10773, got %d", got)
	}
}

func TestConvertAmountWithMinorUnitDifference(t *testing.T) {
	got, err := convertAmountWithPolicies(
		10_000,
		163_500_000,
		domain.DefaultRoundingPolicy("EUR"),
		domain.DefaultRoundingPolicy("JPY"),
	)
	if err != nil {
		t.Fatalf("convertAmountWithPolicies returned error: %v", err)
	}
	if got != 16_350 {
		t.Fatalf("expected 16350 JPY minor units, got %d", got)
	}
}

func TestInverseRateMicros(t *testing.T) {
	got, err := inverseRateMicros(1_080_000)
	if err != nil {
		t.Fatalf("inverseRateMicros returned error: %v", err)
	}
	if got != 925_925 {
		t.Fatalf("expected 925925, got %d", got)
	}
}

func TestQuoteExpiryBoundary(t *testing.T) {
	now := time.Date(2026, 6, 9, 12, 0, 0, 0, time.UTC)
	if isQuoteExpired(now, now.Add(time.Nanosecond)) {
		t.Fatal("quote should be usable before expires_at")
	}
	if !isQuoteExpired(now, now) {
		t.Fatal("quote should expire exactly at expires_at")
	}
	if !isQuoteExpired(now, now.Add(-time.Nanosecond)) {
		t.Fatal("quote should expire after expires_at")
	}
}

func TestQuoteDisclosureIncludesFeeRateAndSpread(t *testing.T) {
	expiresAt := time.Date(2026, 6, 9, 12, 1, 0, 0, time.UTC)
	quote := Quote{
		FromCurrency:     "EUR",
		FromAmountCents:  10_000,
		ToAmountCents:    10_773,
		RateMicros:       1_077_300,
		MarketRateMicros: 1_080_000,
		SpreadBps:        25,
		FeeCents:         99,
		RateSource:       "provider",
		RateProvider:     "market-data-sandbox",
		ExpiresAt:        expiresAt,
	}

	disclosure := buildQuoteDisclosure(quote)
	if disclosure.CustomerRateMicros != 1_077_300 || disclosure.MarketRateMicros != 1_080_000 {
		t.Fatalf("unexpected disclosure rates: %+v", disclosure)
	}
	if disclosure.FeeCents != 99 || disclosure.FeeCurrency != "EUR" {
		t.Fatalf("unexpected fee disclosure: %+v", disclosure)
	}
	if disclosure.EstimatedSpreadRevenue != 27 {
		t.Fatalf("expected 27 quote-currency cents spread revenue, got %d", disclosure.EstimatedSpreadRevenue)
	}
	if !disclosure.RateExpiresAt.Equal(expiresAt) {
		t.Fatalf("expected expiry %s, got %s", expiresAt, disclosure.RateExpiresAt)
	}
}

func TestValidateRateChangeDecision(t *testing.T) {
	request := RateChangeRequest{
		RequesterAdminID: "maker",
		Status:           "pending",
	}
	if _, err := validateRateChangeDecision(request, "maker", "approve", ""); !errors.Is(err, domain.ErrForbidden) {
		t.Fatalf("expected maker approval to be forbidden, got %v", err)
	}
	if _, err := validateRateChangeDecision(request, "checker", "approve", "Looks fine"); err != nil {
		t.Fatalf("checker approval rejected: %v", err)
	}
	if _, err := validateRateChangeDecision(request, "checker", "reject", "short"); !errors.Is(err, domain.ErrValidation) {
		t.Fatalf("expected short reject note to fail, got %v", err)
	}
	if _, err := validateRateChangeDecision(request, "maker", "cancel", "No longer needed"); err != nil {
		t.Fatalf("maker cancellation rejected: %v", err)
	}
}
