package order

import (
	"testing"
	"time"

	"hfb_sys/backend/internal/model"

	"gorm.io/datatypes"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

// setupTestDB 创建测试数据库
func setupOrderTestDB(t *testing.T) *gorm.DB {
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
		Logger: logger.Default.LogMode(logger.Silent),
	})
	if err != nil {
		t.Fatalf("无法创建测试数据库: %v", err)
	}

	if err := db.AutoMigrate(
		&model.User{},
		&model.GameAccount{},
		&model.RentalListing{},
		&model.RentalOrder{},
		&model.PaymentOrder{},
		&model.Notification{},
		&model.HandoffRecord{},
		&model.OrderCheckout{},
		&model.AuditLog{},
	); err != nil {
		t.Fatalf("数据库迁移失败: %v", err)
	}

	return db
}

// TestRepositoryCreateOrderValidatesListing 测试订单创建时的商品验证
func TestRepositoryCreateOrderValidatesListing(t *testing.T) {
	db := setupOrderTestDB(t)
	repo := NewRepository(db)

	// 创建号主
	owner := model.User{Phone: "13800000001"}
	db.Create(&owner)

	// 创建租客
	renter := model.User{Phone: "13800000002"}
	db.Create(&renter)

	// 创建游戏账号
	account := model.GameAccount{
		OwnerID:       owner.ID,
		ServerRegion:  "国服",
		LoginPlatform: "steam",
		Title:         "测试账号",
	}
	db.Create(&account)

	// 测试场景：商品状态不可租
	testCases := []struct {
		name          string
		status        string
		reviewStatus  string
		inTransaction bool
		wantErr       error
	}{
		{
			name:         "商品未发布",
			status:       "draft",
			reviewStatus: "none",
			wantErr:      ErrListingUnavailable,
		},
		{
			name:         "商品已下架",
			status:       "offline",
			reviewStatus: "approved",
			wantErr:      ErrListingUnavailable,
		},
		{
			name:         "商品待审核",
			status:       "published",
			reviewStatus: "pending",
			wantErr:      ErrListingUnavailable,
		},
		{
			name:          "商品交易中",
			status:        "published",
			reviewStatus:  "approved",
			inTransaction: true,
			wantErr:       ErrListingUnavailable,
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			listing := model.RentalListing{
				AccountID:     account.ID,
				OwnerID:       owner.ID,
				PriceCent:     1000,
				Status:        tc.status,
				ReviewStatus:  tc.reviewStatus,
				InTransaction: tc.inTransaction,
			}
			db.Create(&listing)

			_, err := repo.Create(t.Context(), renter.ID, CreateRequest{ListingID: listing.ID})

			if err != tc.wantErr {
				t.Fatalf("error = %v, want %v", err, tc.wantErr)
			}

			// 清理
			db.Delete(&listing)
		})
	}
}

// TestRepositoryCreateOrderRejectsOwnListing 测试不能租自己的商品
func TestRepositoryCreateOrderRejectsOwnListing(t *testing.T) {
	db := setupOrderTestDB(t)
	repo := NewRepository(db)

	// 创建号主
	owner := model.User{Phone: "13800000001"}
	db.Create(&owner)

	// 创建游戏账号
	account := model.GameAccount{
		OwnerID:       owner.ID,
		ServerRegion:  "国服",
		LoginPlatform: "steam",
		Title:         "测试账号",
	}
	db.Create(&account)

	// 创建可租商品
	listing := model.RentalListing{
		AccountID:     account.ID,
		OwnerID:       owner.ID,
		PriceCent:     1000,
		Status:        "published",
		ReviewStatus:  "approved",
		InTransaction: false,
	}
	db.Create(&listing)

	// 号主尝试租自己的商品
	_, err := repo.Create(t.Context(), owner.ID, CreateRequest{ListingID: listing.ID})

	if err != ErrCannotRentOwnListing {
		t.Fatalf("error = %v, want ErrCannotRentOwnListing", err)
	}
}

// TestBuildOrderPricing 测试订单定价计算
func TestBuildOrderPricingBasic(t *testing.T) {
	listing := model.RentalListing{
		PriceCent: 10000, // 100元/小时
	}
	account := model.GameAccount{}

	pricing := buildOrderPricing(listing, account)

	// 验证租金
	if pricing.RentAmountCent != listing.PriceCent {
		t.Fatalf("RentAmountCent = %d, want %d", pricing.RentAmountCent, listing.PriceCent)
	}

	// 验证号主实得 <= 租金
	if pricing.OwnerRentAmountCent > pricing.RentAmountCent {
		t.Fatal("OwnerRentAmountCent should not exceed RentAmountCent")
	}

	// 验证平台手续费 >= 0
	if pricing.PlatformFeeCent < 0 {
		t.Fatal("PlatformFeeCent should be non-negative")
	}

	// 验证总和：租金 = 号主实得 + 平台手续费
	if pricing.RentAmountCent != pricing.OwnerRentAmountCent+pricing.PlatformFeeCent {
		t.Fatalf("pricing sum mismatch: %d != %d + %d",
			pricing.RentAmountCent, pricing.OwnerRentAmountCent, pricing.PlatformFeeCent)
	}
}

// TestBuildCheckoutSettlement 测试结账结算计算
func TestBuildCheckoutSettlementFullRent(t *testing.T) {
	order := model.RentalOrder{
		RentAmountCent:      24000, // 240元
		OwnerRentAmountCent: 21600, // 号主应得216元
		PlatformFeeCent:     2400,  // 平台手续费24元
		DepositAmountCent:   5000,  // 押金50元
	}

	checkout := &model.OrderCheckout{
		ConsumableAmountCent:    0,
		DepositDeductAmountCent: 0,
	}

	settlement := buildCheckoutSettlement(order, checkout)

	// 验证号主租金收入
	if settlement.OwnerRentIncomeCent != order.OwnerRentAmountCent {
		t.Fatalf("OwnerRentIncomeCent = %d, want %d",
			settlement.OwnerRentIncomeCent, order.OwnerRentAmountCent)
	}

	// 验证平台手续费
	if settlement.PlatformFeeCent != order.PlatformFeeCent {
		t.Fatalf("PlatformFeeCent = %d, want %d",
			settlement.PlatformFeeCent, order.PlatformFeeCent)
	}

	// 验证租客退款（全额租用，应退还押金）
	expectedRefund := order.DepositAmountCent
	if settlement.RenterRefundCent != expectedRefund {
		t.Fatalf("RenterRefundCent = %d, want %d",
			settlement.RenterRefundCent, expectedRefund)
	}
}

// TestBuildCheckoutSettlementWithDepositDeduct 测试扣除押金
func TestBuildCheckoutSettlementWithDepositDeduct(t *testing.T) {
	order := model.RentalOrder{
		RentAmountCent:      24000,
		OwnerRentAmountCent: 21600,
		PlatformFeeCent:     2400,
		DepositAmountCent:   5000,
	}

	checkout := &model.OrderCheckout{
		ConsumableAmountCent:    0,
		DepositDeductAmountCent: 2000, // 扣押金20元
	}

	settlement := buildCheckoutSettlement(order, checkout)

	// 验证押金赔付给号主
	if settlement.DepositCompensationCent != checkout.DepositDeductAmountCent {
		t.Fatalf("DepositCompensationCent = %d, want %d",
			settlement.DepositCompensationCent, checkout.DepositDeductAmountCent)
	}

	// 验证号主总收入 = 租金 + 押金赔付
	expectedOwnerIncome := order.OwnerRentAmountCent + checkout.DepositDeductAmountCent
	if settlement.OwnerIncomeCent != expectedOwnerIncome {
		t.Fatalf("OwnerIncomeCent = %d, want %d",
			settlement.OwnerIncomeCent, expectedOwnerIncome)
	}

	// 验证租客退款 = 押金 - 扣除金额
	expectedRefund := order.DepositAmountCent - checkout.DepositDeductAmountCent
	if settlement.RenterRefundCent != expectedRefund {
		t.Fatalf("RenterRefundCent = %d, want %d",
			settlement.RenterRefundCent, expectedRefund)
	}
}

// TestBuildCheckoutSettlementWithConsumable 测试额外消耗
func TestBuildCheckoutSettlementWithConsumable(t *testing.T) {
	order := model.RentalOrder{
		RentAmountCent:      24000,
		OwnerRentAmountCent: 21600,
		PlatformFeeCent:     2400,
		DepositAmountCent:   5000,
	}

	checkout := &model.OrderCheckout{
		ConsumableAmountCent:    1500, // 消耗15元
		DepositDeductAmountCent: 1500, // 设置押金扣除（通常与消耗品一致）
	}

	settlement := buildCheckoutSettlement(order, checkout)

	// 验证押金赔付
	if settlement.DepositCompensationCent != checkout.DepositDeductAmountCent {
		t.Fatalf("DepositCompensationCent = %d, want %d",
			settlement.DepositCompensationCent, checkout.DepositDeductAmountCent)
	}

	// 验证号主总收入 = 租金 + 押金赔付
	expectedOwnerIncome := order.OwnerRentAmountCent + checkout.DepositDeductAmountCent
	if settlement.OwnerIncomeCent != expectedOwnerIncome {
		t.Fatalf("OwnerIncomeCent = %d, want %d",
			settlement.OwnerIncomeCent, expectedOwnerIncome)
	}
}

// TestOrderStatusConstants 测试订单状态常量
func TestOrderStatusConstantsAreDefined(t *testing.T) {
	statuses := []string{
		orderStatusPendingPayment,
		orderStatusPendingHandoff,
		orderStatusRenting,
		orderStatusOverdue,
		orderStatusPendingCheckoutConfirm,
		orderStatusCompleted,
		orderStatusCancelled,
		orderStatusClosed,
	}

	for _, status := range statuses {
		if status == "" {
			t.Fatal("order status should not be empty")
		}
	}

	// 验证状态唯一性
	seen := make(map[string]bool)
	for _, status := range statuses {
		if seen[status] {
			t.Fatalf("duplicate order status: %s", status)
		}
		seen[status] = true
	}
}

// TestHandoffStatusConstants 测试交接状态常量
func TestHandoffStatusConstantsAreDefined(t *testing.T) {
	statuses := []string{
		handoffStatusNone,
		handoffStatusPendingOwner,
		handoffStatusPendingRenterConfirm,
		handoffStatusReceived,
		handoffStatusReturnOverdue,
		handoffStatusPendingOwnerCheckout,
		handoffStatusPendingRenterCheckout,
		handoffStatusReturned,
		handoffStatusCancelled,
		handoffStatusAdminClosed,
		handoffStatusAdminAbnormal,
	}

	for _, status := range statuses {
		if status == "" {
			t.Fatal("handoff status should not be empty")
		}
	}
}

// TestCheckoutStatusConstants 测试结账状态常量
func TestCheckoutStatusConstantsAreDefined(t *testing.T) {
	statuses := []string{
		checkoutStatusSubmitted,
		checkoutStatusCountered,
		checkoutStatusAccepted,
		checkoutStatusDisputed,
	}

	for _, status := range statuses {
		if status == "" {
			t.Fatal("checkout status should not be empty")
		}
	}
}

// TestOrderDurationHours 测试订单时长计算
func TestOrderDurationHoursUsesEstimated(t *testing.T) {
	order := model.RentalOrder{
		EstimatedDurationHours: 48,
	}

	hours := orderDurationHours(order)
	if hours != 48 {
		t.Fatalf("hours = %d, want 48", hours)
	}
}

func TestOrderDurationHoursUsesDefault(t *testing.T) {
	order := model.RentalOrder{
		EstimatedDurationHours: 0,
	}

	hours := orderDurationHours(order)
	if hours != internalOrderHours {
		t.Fatalf("hours = %d, want %d", hours, internalOrderHours)
	}
}

func TestEstimateOrderDurationHoursFromSnapshot(t *testing.T) {
	snapshot := datatypes.JSON([]byte(`{
		"haf_coin_amount": 165000000,
		"asset_summary": {
			"daily_loss_m": 10
		}
	}`))

	hours := estimateOrderDurationHours(snapshot)
	if hours != 396 {
		t.Fatalf("hours = %d, want 396", hours)
	}
}

func TestEstimateOrderDurationHoursFromServerSnapshot(t *testing.T) {
	snapshot := datatypes.JSON([]byte(`{
		"haf_coin_amount": 105000000,
		"asset_summary": {
			"daily_loss_m": 10
		}
	}`))

	hours := estimateOrderDurationHours(snapshot)
	if hours != 252 {
		t.Fatalf("hours = %d, want 252", hours)
	}
}

func TestEstimateOrderDurationHoursUsesDefaultWithoutDailyLoss(t *testing.T) {
	snapshot := datatypes.JSON([]byte(`{
		"haf_coin_amount": 165000000,
		"asset_summary": {}
	}`))

	hours := estimateOrderDurationHours(snapshot)
	if hours != internalOrderHours {
		t.Fatalf("hours = %d, want %d", hours, internalOrderHours)
	}
}

func TestConfirmReceiveRecalculatesEstimatedDuration(t *testing.T) {
	db := setupOrderTestDB(t)
	repo := NewRepository(db)

	owner := model.User{Phone: "13800002001"}
	renter := model.User{Phone: "13900002001"}
	if err := db.Create(&owner).Error; err != nil {
		t.Fatalf("create owner failed: %v", err)
	}
	if err := db.Create(&renter).Error; err != nil {
		t.Fatalf("create renter failed: %v", err)
	}

	account := model.GameAccount{
		OwnerID:       owner.ID,
		Status:        "rented",
		ServerRegion:  "国服",
		LoginPlatform: "steam",
		Title:         "测试账号",
		HafCoinAmount: 165000000,
		AssetSummary:  datatypes.JSON([]byte(`{"daily_loss_m":10}`)),
	}
	if err := db.Create(&account).Error; err != nil {
		t.Fatalf("create account failed: %v", err)
	}

	listing := model.RentalListing{
		ListingNo:     "LST-DUR-001",
		OwnerID:       owner.ID,
		AccountID:     account.ID,
		Status:        "published",
		ReviewStatus:  "approved",
		InTransaction: true,
		PriceCent:     1000,
	}
	if err := db.Create(&listing).Error; err != nil {
		t.Fatalf("create listing failed: %v", err)
	}

	snapshot, err := makeAccountSnapshot(account, listing)
	if err != nil {
		t.Fatalf("make snapshot failed: %v", err)
	}

	order := model.RentalOrder{
		OrderNo:                "ORD-DUR-001",
		ListingID:              listing.ID,
		AccountID:              account.ID,
		OwnerID:                owner.ID,
		RenterID:               renter.ID,
		EstimatedDurationHours: internalOrderHours,
		AccountSnapshot:        snapshot,
		Status:                 orderStatusPendingHandoff,
		HandoffStatus:          handoffStatusPendingRenterConfirm,
	}
	if err := db.Create(&order).Error; err != nil {
		t.Fatalf("create order failed: %v", err)
	}

	if err := repo.ConfirmReceive(t.Context(), renter.ID, order.ID); err != nil {
		t.Fatalf("ConfirmReceive() error = %v", err)
	}

	var saved model.RentalOrder
	if err := db.First(&saved, order.ID).Error; err != nil {
		t.Fatalf("load order failed: %v", err)
	}
	if saved.EstimatedDurationHours != 396 {
		t.Fatalf("EstimatedDurationHours = %d, want 396", saved.EstimatedDurationHours)
	}
	if saved.RentedAt == nil || saved.RentedAt.After(time.Now()) {
		t.Fatalf("RentedAt not set correctly: %#v", saved.RentedAt)
	}
}

// TestCancelClosesPendingPaymentOrder 验证租客取消待支付订单时，未完成支付流水会同步关闭。
func TestCancelClosesPendingPaymentOrder(t *testing.T) {
	db := setupOrderTestDB(t)
	repo := NewRepository(db)
	owner := model.User{Phone: "13800001001"}
	renter := model.User{Phone: "13900001001"}
	if err := db.Create(&owner).Error; err != nil {
		t.Fatalf("create owner failed: %v", err)
	}
	if err := db.Create(&renter).Error; err != nil {
		t.Fatalf("create renter failed: %v", err)
	}
	account := model.GameAccount{
		OwnerID:       owner.ID,
		Status:        "rented",
		ServerRegion:  "国服",
		LoginPlatform: "steam",
		Title:         "测试账号",
	}
	if err := db.Create(&account).Error; err != nil {
		t.Fatalf("create account failed: %v", err)
	}
	listing := model.RentalListing{
		ListingNo:     "LST-CANCEL-001",
		OwnerID:       owner.ID,
		AccountID:     account.ID,
		Status:        "published",
		ReviewStatus:  "approved",
		InTransaction: true,
		PriceCent:     1000,
	}
	if err := db.Create(&listing).Error; err != nil {
		t.Fatalf("create listing failed: %v", err)
	}
	order := model.RentalOrder{
		OrderNo:        "ORD-CANCEL-001",
		ListingID:      listing.ID,
		AccountID:      account.ID,
		OwnerID:        owner.ID,
		RenterID:       renter.ID,
		RentAmountCent: 1000,
		Status:         "pending_payment",
		HandoffStatus:  "none",
	}
	if err := db.Create(&order).Error; err != nil {
		t.Fatalf("create order failed: %v", err)
	}
	payment := model.PaymentOrder{
		PaymentNo:    "PAY-CANCEL-001",
		OrderID:      order.ID,
		OrderNo:      order.OrderNo,
		UserID:       renter.ID,
		Provider:     "lakala",
		ThirdOrderID: "PAY-CANCEL-001",
		AmountCent:   1000,
		BizType:      "order_pay",
		Status:       "paying",
	}
	if err := db.Create(&payment).Error; err != nil {
		t.Fatalf("create payment failed: %v", err)
	}

	if err := repo.Cancel(t.Context(), renter.ID, order.ID); err != nil {
		t.Fatalf("Cancel() error = %v", err)
	}

	var latestPayment model.PaymentOrder
	if err := db.First(&latestPayment, payment.ID).Error; err != nil {
		t.Fatalf("find payment failed: %v", err)
	}
	if latestPayment.Status != "closed" {
		t.Fatalf("payment status = %q, want closed", latestPayment.Status)
	}
}

func TestAdminResetCheckoutConfirmTimeout(t *testing.T) {
	db := setupOrderTestDB(t)
	repo := NewRepository(db)

	owner := model.User{Phone: "13800003001"}
	renter := model.User{Phone: "13900003001"}
	if err := db.Create(&owner).Error; err != nil {
		t.Fatalf("create owner failed: %v", err)
	}
	if err := db.Create(&renter).Error; err != nil {
		t.Fatalf("create renter failed: %v", err)
	}

	order := model.RentalOrder{
		OrderNo:          "ORD-RESET-CHECKOUT-001",
		ListingID:        1,
		AccountID:        1,
		OwnerID:          owner.ID,
		RenterID:         renter.ID,
		Status:           orderStatusAbnormal,
		HandoffStatus:    handoffStatusOwnerCheckoutConfirmTimeout,
		SettlementStatus: settlementStatusPending,
	}
	if err := db.Create(&order).Error; err != nil {
		t.Fatalf("create order failed: %v", err)
	}

	err := repo.AdminResetHandoff(t.Context(), 1, order.ID, AdminActionRequest{Reason: "号主已联系上"}, AuditMeta{})
	if err != nil {
		t.Fatalf("AdminResetHandoff() error = %v", err)
	}

	var saved model.RentalOrder
	if err := db.First(&saved, order.ID).Error; err != nil {
		t.Fatalf("load order failed: %v", err)
	}
	if saved.Status != orderStatusPendingCheckoutConfirm {
		t.Fatalf("status = %q, want %q", saved.Status, orderStatusPendingCheckoutConfirm)
	}
	if saved.HandoffStatus != handoffStatusPendingOwnerCheckout {
		t.Fatalf("handoff status = %q, want %q", saved.HandoffStatus, handoffStatusPendingOwnerCheckout)
	}
	if saved.HandoffStartedAt == nil {
		t.Fatal("handoff started at should be refreshed")
	}
}

func TestAdminResetReturnOverdueRecalculatesDuration(t *testing.T) {
	db := setupOrderTestDB(t)
	repo := NewRepository(db)

	owner := model.User{Phone: "13800003005"}
	renter := model.User{Phone: "13900003005"}
	if err := db.Create(&owner).Error; err != nil {
		t.Fatalf("create owner failed: %v", err)
	}
	if err := db.Create(&renter).Error; err != nil {
		t.Fatalf("create renter failed: %v", err)
	}

	rentedAt := time.Now().Add(-72 * time.Hour)
	order := model.RentalOrder{
		OrderNo:                "ORD-RESET-RETURN-001",
		ListingID:              1,
		AccountID:              1,
		OwnerID:                owner.ID,
		RenterID:               renter.ID,
		RentedAt:               &rentedAt,
		EstimatedDurationHours: 24,
		AccountSnapshot: datatypes.JSON([]byte(`{
			"haf_coin_amount": 105000000,
			"asset_summary": {"daily_loss_m": 10}
		}`)),
		Status:        orderStatusOverdue,
		HandoffStatus: handoffStatusReturnOverdue,
	}
	if err := db.Create(&order).Error; err != nil {
		t.Fatalf("create order failed: %v", err)
	}

	err := repo.AdminResetHandoff(t.Context(), 1, order.ID, AdminActionRequest{Reason: "预计时长计算有误"}, AuditMeta{})
	if err != nil {
		t.Fatalf("AdminResetHandoff() error = %v", err)
	}

	var saved model.RentalOrder
	if err := db.First(&saved, order.ID).Error; err != nil {
		t.Fatalf("load order failed: %v", err)
	}
	if saved.Status != orderStatusRenting {
		t.Fatalf("status = %q, want %q", saved.Status, orderStatusRenting)
	}
	if saved.HandoffStatus != handoffStatusReceived {
		t.Fatalf("handoff status = %q, want %q", saved.HandoffStatus, handoffStatusReceived)
	}
	if saved.EstimatedDurationHours != 252 {
		t.Fatalf("EstimatedDurationHours = %d, want 252", saved.EstimatedDurationHours)
	}
	if saved.RentedAt == nil || !saved.RentedAt.Equal(rentedAt) {
		t.Fatalf("RentedAt = %#v, want original %v", saved.RentedAt, rentedAt)
	}
}

func TestAdminOrderDTOIncludesResetAction(t *testing.T) {
	order := model.RentalOrder{
		Status:        orderStatusAbnormal,
		HandoffStatus: handoffStatusOwnerCheckoutConfirmTimeout,
	}
	dto := orderRow{RentalOrder: order}.toAdminDTO()
	if dto.AdminActions == nil || dto.AdminActions.ResetHandoff == nil {
		t.Fatal("reset handoff action should be present")
	}
	if !dto.AdminActions.ResetHandoff.Enabled {
		t.Fatal("reset handoff action should be enabled")
	}
	if dto.AdminActions.ResetHandoff.Label != "重置结账确认" {
		t.Fatalf("label = %q, want 重置结账确认", dto.AdminActions.ResetHandoff.Label)
	}
}

func TestAdminOrderDTOIncludesReturnOverdueResetAction(t *testing.T) {
	order := model.RentalOrder{
		Status:        orderStatusOverdue,
		HandoffStatus: handoffStatusReturnOverdue,
	}
	dto := orderRow{RentalOrder: order}.toAdminDTO()
	if dto.AdminActions == nil || dto.AdminActions.ResetHandoff == nil {
		t.Fatal("return overdue reset action should be present")
	}
	if !dto.AdminActions.ResetHandoff.Enabled {
		t.Fatal("return overdue reset action should be enabled")
	}
	if dto.AdminActions.ResetHandoff.Label != "重置归还逾期" {
		t.Fatalf("label = %q, want 重置归还逾期", dto.AdminActions.ResetHandoff.Label)
	}
}

func TestUserOrderDTODoesNotIncludeAdminActions(t *testing.T) {
	order := model.RentalOrder{
		OwnerID:       1,
		RenterID:      2,
		Status:        orderStatusAbnormal,
		HandoffStatus: handoffStatusOwnerCheckoutConfirmTimeout,
	}
	dto := orderRow{RentalOrder: order}.toDTOForUser(order.RenterID)
	if dto.AdminActions != nil {
		t.Fatal("user order dto should not include admin actions")
	}
}

func TestAdminResetRenterConfirmTimeoutRefreshesStageTime(t *testing.T) {
	db := setupOrderTestDB(t)
	repo := NewRepository(db)

	owner := model.User{Phone: "13800003002"}
	renter := model.User{Phone: "13900003002"}
	if err := db.Create(&owner).Error; err != nil {
		t.Fatalf("create owner failed: %v", err)
	}
	if err := db.Create(&renter).Error; err != nil {
		t.Fatalf("create renter failed: %v", err)
	}

	order := model.RentalOrder{
		OrderNo:       "ORD-RESET-RECEIVE-001",
		ListingID:     1,
		AccountID:     1,
		OwnerID:       owner.ID,
		RenterID:      renter.ID,
		Status:        orderStatusAbnormal,
		HandoffStatus: handoffStatusRenterConfirmTimeout,
	}
	if err := db.Create(&order).Error; err != nil {
		t.Fatalf("create order failed: %v", err)
	}

	err := repo.AdminResetHandoff(t.Context(), 1, order.ID, AdminActionRequest{Reason: "租客申请重新确认"}, AuditMeta{})
	if err != nil {
		t.Fatalf("AdminResetHandoff() error = %v", err)
	}

	var saved model.RentalOrder
	if err := db.First(&saved, order.ID).Error; err != nil {
		t.Fatalf("load order failed: %v", err)
	}
	if saved.Status != orderStatusPendingHandoff {
		t.Fatalf("status = %q, want %q", saved.Status, orderStatusPendingHandoff)
	}
	if saved.HandoffStatus != handoffStatusPendingRenterConfirm {
		t.Fatalf("handoff status = %q, want %q", saved.HandoffStatus, handoffStatusPendingRenterConfirm)
	}
	if saved.HandoffStartedAt == nil {
		t.Fatal("handoff started at should be refreshed")
	}
}

func TestSubmitCheckoutRefreshesStageTime(t *testing.T) {
	db := setupOrderTestDB(t)
	repo := NewRepository(db)

	owner := model.User{Phone: "13800003003"}
	renter := model.User{Phone: "13900003003"}
	if err := db.Create(&owner).Error; err != nil {
		t.Fatalf("create owner failed: %v", err)
	}
	if err := db.Create(&renter).Error; err != nil {
		t.Fatalf("create renter failed: %v", err)
	}

	oldStageTime := time.Now().Add(-2 * time.Hour)
	order := model.RentalOrder{
		OrderNo:                "ORD-CHECKOUT-STAGE-001",
		ListingID:              1,
		AccountID:              1,
		OwnerID:                owner.ID,
		RenterID:               renter.ID,
		Status:                 orderStatusRenting,
		HandoffStatus:          handoffStatusReceived,
		HandoffStartedAt:       &oldStageTime,
		RentAmountCent:         1000,
		OwnerRentAmountCent:    800,
		DepositAmountCent:      1000,
		PlatformFeeCent:        200,
		EstimatedDurationHours: 24,
		AccountSnapshot:        datatypes.JSON([]byte(`{"haf_coin_amount":1000000,"asset_summary":{"price_breakdown":{"buyer_coin_base_price":10,"seller_coin_base_price":8}}}`)),
	}
	if err := db.Create(&order).Error; err != nil {
		t.Fatalf("create order failed: %v", err)
	}

	if _, err := repo.SubmitCheckout(t.Context(), renter.ID, order.ID, SubmitCheckoutRequest{Content: "申请结账"}); err != nil {
		t.Fatalf("SubmitCheckout() error = %v", err)
	}

	var saved model.RentalOrder
	if err := db.First(&saved, order.ID).Error; err != nil {
		t.Fatalf("load order failed: %v", err)
	}
	if saved.Status != orderStatusPendingCheckoutConfirm || saved.HandoffStatus != handoffStatusPendingOwnerCheckout {
		t.Fatalf("status = %s/%s, want pending_checkout_confirm/pending_owner_checkout", saved.Status, saved.HandoffStatus)
	}
	if saved.HandoffStartedAt == nil || !saved.HandoffStartedAt.After(oldStageTime) {
		t.Fatalf("handoff started at = %#v, should be refreshed after old stage time", saved.HandoffStartedAt)
	}
}
