package payment

import (
	"context"
	"testing"
	"time"

	"go.mongodb.org/mongo-driver/v2/bson"
	"go.mongodb.org/mongo-driver/v2/mongo"
)

func TestAslanPaidCoinSellerFilterUsesPaymentFacts(t *testing.T) {
	source := &MongoRechargeBillSource{
		collection: &mongo.Collection{},
		config:     aslanBillSourceConfig(),
		regionResolvers: []legacyRegionCountryResolver{staticLegacyRegionResolver{
			codes: []string{"eg"}, handled: true,
		}},
	}
	filter, regionCodes, matchable, err := source.financePaidCoinSellerFilter(context.Background(), financeCoinSellerRechargeQuery{
		Keyword:      "x5ctfib7800289mrkm5fco0A10001910",
		RegionID:     1001,
		StartAtMS:    1_784_000_000_000,
		EndAtMS:      1_785_000_000_000,
		TargetUserID: 2_070_458_148_788_715_521,
	})
	if err != nil || !matchable {
		t.Fatalf("financePaidCoinSellerFilter err=%v matchable=%v", err, matchable)
	}
	if filter["sysOrigin"] != "ATYOU" || filter["status"] != "SUCCESS" {
		t.Fatalf("payment status boundary missing: %#v", filter)
	}
	if filter["acceptUserId"] != int64(2_070_458_148_788_715_521) {
		t.Fatalf("seller filter missing: %#v", filter)
	}
	if len(regionCodes) != 1 || regionCodes[0] != "EG" {
		t.Fatalf("region code mismatch: %#v", regionCodes)
	}
	conditions, ok := filter["$and"].(bson.A)
	if !ok || len(conditions) != 3 {
		t.Fatalf("freight/keyword/time conditions missing: %#v", filter)
	}
	if got := len(legacyFreightCommodityMatchConditions()); got != len(legacyFreightCommodityFields) {
		t.Fatalf("freight compatibility fields=%d want=%d", got, len(legacyFreightCommodityFields))
	}
	if _, exists := filter["recharge_type"]; exists {
		t.Fatalf("wallet running-water recharge_type must not enter Mongo bill filter: %#v", filter)
	}
}

func TestLegacyPaidCoinSellerRechargeBillDTOUsesOriginalBillAmounts(t *testing.T) {
	providerAmount, err := bson.ParseDecimal128("4726")
	if err != nil {
		t.Fatalf("parse provider amount: %v", err)
	}
	usdAmount, err := bson.ParseDecimal128("100.00")
	if err != nil {
		t.Fatalf("parse usd amount: %v", err)
	}
	createdAt := time.Date(2026, 7, 14, 12, 14, 26, 499_000_000, time.UTC)
	paidAt := time.Date(2026, 7, 14, 12, 15, 14, 777_000_000, time.UTC)
	document := legacyPurchaseDocument{
		ID:                 int64(2_077_003_764_199_862_273),
		OrderID:            "x5ctfib7800289mrkm5fco0A10001910",
		AcceptUserID:       2_070_458_148_788_715_521,
		CountryCode:        "EG",
		Currency:           "EGP",
		Amount:             providerAmount,
		AmountUSD:          usdAmount,
		Status:             "SUCCESS",
		TrackCommodityType: "FREIGHT_GOLD",
		Factory:            legacyPurchaseFactory{Platform: "H5", FactoryCode: "MIFA_PAY"},
		Products:           []legacyPurchaseProduct{{Code: "FREIGHT_GOLD", Name: "FREIGHT_GOLD", Content: "8800000"}},
		CreateTime:         createdAt,
		UpdateTime:         paidAt,
		LegacyBillCountry:  "EG",
	}
	dto := legacyPaidCoinSellerRechargeBillDTO(aslanBillSourceConfig(), document)
	if dto.RechargeType != financeCoinSellerMiFaPayType || dto.Status != "succeeded" {
		t.Fatalf("coin seller bill identity mismatch: %+v", dto)
	}
	if dto.ExternalRef != document.OrderID || dto.SellerUserID != document.AcceptUserID {
		t.Fatalf("provider/seller identity mismatch: %+v", dto)
	}
	if dto.USDMinorAmount != 10_000 || dto.CoinAmount != 8_800_000 {
		t.Fatalf("must use original bill USD/product amount: %+v", dto)
	}
	if dto.ProviderAmountMinor != 472_600 || dto.UserPaidCurrencyCode != "EGP" || dto.UserPaidAmountMicro != 4_726_000_000 {
		t.Fatalf("provider paid amount mismatch: %+v", dto)
	}
	if dto.CreatedAtMS != paidAt.UnixMilli() || dto.PaidSyncedAtMS != paidAt.UnixMilli() {
		t.Fatalf("successful payment time mismatch: %+v", dto)
	}
	if dto.PolicyVersion != "FREIGHT_GOLD" {
		t.Fatalf("commodity type mismatch: %+v", dto)
	}
}

func TestLegacyFreightCoinAmountFallsBackToTopLevelType(t *testing.T) {
	document := legacyPurchaseDocument{
		TrackCommodityType: "FREIGHT_GOLD",
		Products: []legacyPurchaseProduct{
			{Name: "LEGACY_PRODUCT", Content: "8800000"},
			{Name: "PROPS", Content: "not-a-number"},
		},
	}
	if got := legacyFreightCoinAmount(document); got != 8_800_000 {
		t.Fatalf("legacy freight coin amount=%d", got)
	}
}

type fakeAslanPaidCoinSellerSource struct {
	stats      financeCoinSellerRechargeStats
	items      []rechargeBillDTO
	total      int64
	statsCalls int
	listCalls  int
}

func (s *fakeAslanPaidCoinSellerSource) AppCode() string { return "aslan" }
func (s *fakeAslanPaidCoinSellerSource) AppName() string { return "Aslan" }
func (s *fakeAslanPaidCoinSellerSource) ListRechargeBills(context.Context, legacyRechargeBillQuery) ([]rechargeBillDTO, int64, error) {
	return []rechargeBillDTO{}, 0, nil
}
func (s *fakeAslanPaidCoinSellerSource) SummarizeRechargeBills(context.Context, legacyRechargeBillQuery) (rechargeBillSummaryDTO, error) {
	return rechargeBillSummaryDTO{}, nil
}
func (s *fakeAslanPaidCoinSellerSource) SupportsGooglePaidSync() bool { return false }
func (s *fakeAslanPaidCoinSellerSource) ListPendingGooglePaidTransactionIDs(context.Context, int) ([]string, error) {
	return []string{}, nil
}
func (s *fakeAslanPaidCoinSellerSource) RefreshGooglePaidDetails(context.Context, []string) ([]googleRechargePaidDTO, error) {
	return []googleRechargePaidDTO{}, nil
}
func (s *fakeAslanPaidCoinSellerSource) Overview(context.Context, legacyRechargeBillQuery, int32) (rechargeBillOverviewDTO, error) {
	return rechargeBillOverviewDTO{Daily: []rechargeBillDailyBucketDTO{}, Regions: []rechargeBillRegionBucketDTO{}}, nil
}
func (s *fakeAslanPaidCoinSellerSource) financePaidCoinSellerRechargeStats(context.Context, financeCoinSellerRechargeQuery) (financeCoinSellerRechargeStats, error) {
	s.statsCalls++
	return s.stats, nil
}
func (s *fakeAslanPaidCoinSellerSource) listFinancePaidCoinSellerRechargeBills(context.Context, financeCoinSellerRechargeQuery, int) ([]rechargeBillDTO, int64, error) {
	s.listCalls++
	return append([]rechargeBillDTO(nil), s.items...), s.total, nil
}

func TestFinanceCoinSellerIncludesAslanPaidBills(t *testing.T) {
	source := &fakeAslanPaidCoinSellerSource{
		stats: financeCoinSellerRechargeStats{
			Total: rechargeBillSummaryBucketDTO{BillCount: 1, CoinAmount: 8_800_000, USDMinorAmount: 10_000},
			Daily: map[string]rechargeBillSummaryBucketDTO{
				"2026-07-14": {BillCount: 1, CoinAmount: 8_800_000, USDMinorAmount: 10_000},
			},
			Regions: []rechargeBillRegionBucketDTO{{RegionID: 9, BillCount: 1, UsdMinorAmount: 10_000}},
		},
		items: []rechargeBillDTO{{
			AppCode: "aslan", TransactionID: "2077003764199862273", ExternalRef: "x5ctfib7800289mrkm5fco0A10001910",
			RechargeType: financeCoinSellerThirdPartyType, ProviderCode: "mifa_pay", USDMinorAmount: 10_000, CoinAmount: 8_800_000,
		}},
		total: 1,
	}
	handler := New(nil, nil, nil, nil, nil, WithRechargeBillSources(source))
	stats, err := handler.financeCoinSellerRechargeStats(context.Background(), "aslan", financeCoinSellerRechargeQuery{})
	if err != nil {
		t.Fatalf("financeCoinSellerRechargeStats: %v", err)
	}
	if stats.Total.USDMinorAmount != 10_000 || stats.Total.CoinAmount != 8_800_000 || stats.Total.BillCount != 1 {
		t.Fatalf("paid bill stats missing: %+v", stats.Total)
	}
	if stats.Daily["2026-07-14"].USDMinorAmount != 10_000 || len(stats.Regions) != 1 || stats.Regions[0].RegionID != 9 {
		t.Fatalf("paid bill dimensions missing: %+v", stats)
	}
	items, total, err := handler.listFinanceCoinSellerRechargeBills(context.Background(), "aslan", financeCoinSellerRechargeQuery{Page: 1, PageSize: 20})
	if err != nil {
		t.Fatalf("listFinanceCoinSellerRechargeBills: %v", err)
	}
	if total != 1 || len(items) != 1 || items[0].USDMinorAmount != 10_000 || items[0].ExternalRef != "x5ctfib7800289mrkm5fco0A10001910" {
		t.Fatalf("paid bill list mismatch: total=%d items=%+v", total, items)
	}
	if source.statsCalls != 1 || source.listCalls != 1 {
		t.Fatalf("paid source calls mismatch: stats=%d list=%d", source.statsCalls, source.listCalls)
	}
}

func TestFinanceCoinSellerBillDedupKeepsOriginalPaymentBill(t *testing.T) {
	original := rechargeBillDTO{TransactionID: "mongo", ExternalRef: "ORDER-1", ProviderCode: "MIFA_PAY", USDMinorAmount: 10_000}
	operatorBound := rechargeBillDTO{TransactionID: "admin", ExternalRef: "order-1", ProviderCode: "mifapay", USDMinorAmount: 11_000}
	items, duplicateCount := deduplicateFinanceCoinSellerRechargeBills([]rechargeBillDTO{original, operatorBound})
	if duplicateCount != 1 || len(items) != 1 {
		t.Fatalf("dedup mismatch: duplicates=%d items=%+v", duplicateCount, items)
	}
	if items[0].TransactionID != "mongo" || items[0].USDMinorAmount != 10_000 {
		t.Fatalf("original payment bill must win: %+v", items[0])
	}
}

func TestFinancePaidCoinSellerSourceIsAslanOnly(t *testing.T) {
	source := &fakeAslanPaidCoinSellerSource{}
	handler := New(nil, nil, nil, nil, nil, WithRechargeBillSources(source))
	if handler.financePaidCoinSellerRechargeSource("aslan") == nil {
		t.Fatalf("Aslan paid source should be registered")
	}
	if handler.financePaidCoinSellerRechargeSource("yumi") != nil {
		t.Fatalf("Aslan paid source must not expand to Yumi")
	}
}

var _ RechargeBillSource = (*fakeAslanPaidCoinSellerSource)(nil)
var _ financePaidCoinSellerRechargeSource = (*fakeAslanPaidCoinSellerSource)(nil)
