package order

import (
	"testing"
	"time"

	"hfb_sys/backend/internal/model"
	"hfb_sys/backend/internal/timeutil"

	"gorm.io/datatypes"
)

func TestCalculateCheckoutSettlementRefundsUnusedRent(t *testing.T) {
	order := model.RentalOrder{
		RentAmountCent:      38300,
		OwnerRentAmountCent: 35300,
		DepositAmountCent:   15000,
		AccountSnapshot: datatypes.JSON([]byte(`{
			"haf_coin_amount": 100000000,
			"asset_summary": {
				"price_breakdown": {
					"buyer_coin_base_price": 263,
					"seller_coin_base_price": 233,
					"consumable_price": 120
				}
			}
		}`)),
	}

	settlement := calculateCheckoutSettlement(order, 700, 90, 0)

	// 角精度：243.7 = roundMoney(236.7 + 7)
	if settlement.ActualRentAmountCent != 24370 {
		t.Fatalf("ActualRentAmountCent = %d, want 24370", settlement.ActualRentAmountCent)
	}
	// 角精度：216.7 = roundMoney(209.7 + 7) 卖家币价+消耗品
	if settlement.OwnerRentIncomeCent != 21670 {
		t.Fatalf("OwnerRentIncomeCent = %d, want 21670", settlement.OwnerRentIncomeCent)
	}
	// 角精度：27.0 = roundMoney(27) 平台费
	if settlement.PlatformFeeCent != 2700 {
		t.Fatalf("PlatformFeeCent = %d, want 2700", settlement.PlatformFeeCent)
	}
	// 角精度：139.3 = roundMoney(383 - 243.7)
	if settlement.RentRefundCent != 13930 {
		t.Fatalf("RentRefundCent = %d, want 13930", settlement.RentRefundCent)
	}
	if settlement.DepositRefundCent != 15000 {
		t.Fatalf("DepositRefundCent = %d, want 15000", settlement.DepositRefundCent)
	}
	// 角精度：289.3 = roundMoney(139.3 + 150)
	if settlement.RenterRefundCent != 28930 {
		t.Fatalf("RenterRefundCent = %d, want 28930", settlement.RenterRefundCent)
	}
}

func TestCalculateCheckoutSettlementUsesBuyerAndSellerRatiosSeparately(t *testing.T) {
	order := model.RentalOrder{
		RentAmountCent:      38300,
		OwnerRentAmountCent: 35300,
		DepositAmountCent:   15000,
		AccountSnapshot: datatypes.JSON([]byte(`{
			"haf_coin_amount": 100000000,
			"asset_summary": {
				"price_breakdown": {
					"buyer_coin_base_price": 263,
					"seller_coin_base_price": 233,
					"consumable_price": 120
				}
			}
		}`)),
	}

	settlement := calculateCheckoutSettlement(order, 0, 50, 0)

	// 角精度：131.5 = roundMoney(131.5)
	if settlement.ActualRentAmountCent != 13150 {
		t.Fatalf("租客侧实际租金 = %d, want 13150", settlement.ActualRentAmountCent)
	}
	// 角精度：116.5 = roundMoney(116.5)
	if settlement.OwnerRentIncomeCent != 11650 {
		t.Fatalf("卖家侧租金收入 = %d, want 11650", settlement.OwnerRentIncomeCent)
	}
	// 角精度：15.0 = roundMoney(15)
	if settlement.PlatformFeeCent != 1500 {
		t.Fatalf("平台差价 = %d, want 1500", settlement.PlatformFeeCent)
	}
}

func TestCalculateCheckoutSettlementAddsDepositCompensation(t *testing.T) {
	order := model.RentalOrder{
		RentAmountCent:      38300,
		OwnerRentAmountCent: 35300,
		DepositAmountCent:   15000,
		AccountSnapshot: datatypes.JSON([]byte(`{
			"haf_coin_amount": 100000000,
			"asset_summary": {
				"price_breakdown": {
					"buyer_coin_base_price": 263,
					"seller_coin_base_price": 233,
					"consumable_price": 120
				}
			}
		}`)),
	}

	settlement := calculateCheckoutSettlement(order, 12000, 100, 3000)

	if settlement.ActualRentAmountCent != 38300 {
		t.Fatalf("ActualRentAmountCent = %d, want 38300", settlement.ActualRentAmountCent)
	}
	if settlement.OwnerRentIncomeCent != 35300 {
		t.Fatalf("OwnerRentIncomeCent = %d, want 35300", settlement.OwnerRentIncomeCent)
	}
	if settlement.DepositCompensationCent != 3000 {
		t.Fatalf("DepositCompensationCent = %d, want 3000", settlement.DepositCompensationCent)
	}
	if settlement.OwnerIncomeCent != 38300 {
		t.Fatalf("OwnerIncomeCent = %d, want 38300", settlement.OwnerIncomeCent)
	}
	if settlement.RenterRefundCent != 12000 {
		t.Fatalf("RenterRefundCent = %d, want 12000", settlement.RenterRefundCent)
	}
}

func TestCalculateDepositWaiverUsesSharedRemainingQuota(t *testing.T) {
	paid, waived := calculateDepositWaiver(500, 300, 0)
	if paid != 200 || waived != 300 {
		t.Fatalf("paid = %d, waived = %d, want 200, 300", paid, waived)
	}

	paid, waived = calculateDepositWaiver(500, 300, 200)
	if paid != 400 || waived != 100 {
		t.Fatalf("paid = %d, waived = %d, want 400, 100", paid, waived)
	}

	paid, waived = calculateDepositWaiver(500, 300, 300)
	if paid != 500 || waived != 0 {
		t.Fatalf("paid = %d, waived = %d, want 500, 0", paid, waived)
	}
}

func TestArchiveAssetsMovesListingOffline(t *testing.T) {
	now := time.Now()
	listing := model.RentalListing{
		Status:        listingStatusRented,
		InTransaction: true,
		PublishedAt:   &now,
	}
	account := model.GameAccount{Status: accountStatusRented}

	archiveAssets(&listing, &account)

	if listing.Status != listingStatusOffline {
		t.Fatalf("listing.Status = %q, want %q", listing.Status, listingStatusOffline)
	}
	if listing.InTransaction {
		t.Fatal("listing.InTransaction = true, want false")
	}
	if listing.PublishedAt != nil {
		t.Fatal("listing.PublishedAt is not nil")
	}
	if account.Status != accountStatusOffline {
		t.Fatalf("account.Status = %q, want %q", account.Status, accountStatusOffline)
	}
}

func TestCompleteAssetsMovesListingCompleted(t *testing.T) {
	now := time.Now()
	listing := model.RentalListing{
		Status:        listingStatusRented,
		InTransaction: true,
		PublishedAt:   &now,
	}
	account := model.GameAccount{Status: accountStatusRented}

	completeAssets(&listing, &account)

	if listing.Status != listingStatusCompleted {
		t.Fatalf("listing.Status = %q, want %q", listing.Status, listingStatusCompleted)
	}
	if listing.InTransaction {
		t.Fatal("listing.InTransaction = true, want false")
	}
	if listing.PublishedAt != nil {
		t.Fatal("listing.PublishedAt is not nil")
	}
	if account.Status != accountStatusOffline {
		t.Fatalf("account.Status = %q, want %q", account.Status, accountStatusOffline)
	}
}

func TestNewOrderNoUsesShanghaiTimeWhenLocalIsUTC(t *testing.T) {
	oldLocal := time.Local
	time.Local = time.UTC
	defer func() {
		time.Local = oldLocal
	}()

	loc := timeutil.ShanghaiLocation()
	before := time.Now().In(loc)
	orderNo, err := newOrderNo()
	after := time.Now().In(loc)
	if err != nil {
		t.Fatalf("newOrderNo() error = %v", err)
	}
	if len(orderNo) != len("RO20260603123456789") {
		t.Fatalf("orderNo length = %d, want %d: %s", len(orderNo), len("RO20260603123456789"), orderNo)
	}
	parsed, err := time.ParseInLocation("20060102150405", orderNo[2:16], loc)
	if err != nil {
		t.Fatalf("parse order no time: %v", err)
	}
	if parsed.Before(before.Add(-2*time.Second)) || parsed.After(after.Add(2*time.Second)) {
		t.Fatalf("order no time = %s, want between %s and %s", parsed, before, after)
	}
}
