package dispute

import (
	"strings"
	"testing"
	"time"

	"hfb_sys/backend/internal/model"

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

func setupDisputeTestDB(t *testing.T) *gorm.DB {
	t.Helper()
	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.OrderCheckout{},
		&model.HandoffRecord{},
		&model.Dispute{},
		&model.Notification{},
	); err != nil {
		t.Fatalf("数据库迁移失败: %v", err)
	}
	return db
}

func createDisputeOrderFixture(t *testing.T, db *gorm.DB, order model.RentalOrder) (model.User, model.User, model.RentalOrder) {
	t.Helper()
	owner := model.User{Phone: "13800000001"}
	if err := db.Create(&owner).Error; err != nil {
		t.Fatalf("创建号主失败: %v", err)
	}
	renter := model.User{Phone: "13800000002"}
	if err := db.Create(&renter).Error; err != nil {
		t.Fatalf("创建租客失败: %v", err)
	}
	account := model.GameAccount{
		OwnerID:       owner.ID,
		Title:         "测试账号",
		ServerRegion:  "国服",
		LoginPlatform: "安卓",
	}
	if err := db.Create(&account).Error; err != nil {
		t.Fatalf("创建账号失败: %v", err)
	}
	listing := model.RentalListing{
		AccountID:     account.ID,
		OwnerID:       owner.ID,
		PriceCent:     1000,
		Status:        "rented",
		ReviewStatus:  "approved",
		InTransaction: true,
	}
	if err := db.Create(&listing).Error; err != nil {
		t.Fatalf("创建商品失败: %v", err)
	}
	order.OrderNo = "RO202606130001"
	order.ListingID = listing.ID
	order.AccountID = account.ID
	order.OwnerID = owner.ID
	order.RenterID = renter.ID
	if err := db.Create(&order).Error; err != nil {
		t.Fatalf("创建订单失败: %v", err)
	}
	return owner, renter, order
}

func TestRepositoryCancelRestoresNormalDisputeOrderStatus(t *testing.T) {
	db := setupDisputeTestDB(t)
	repo := NewRepository(db)
	_, renter, order := createDisputeOrderFixture(t, db, model.RentalOrder{
		Status:           "renting",
		HandoffStatus:    "received",
		SettlementStatus: "unsettled",
	})

	created, err := repo.Create(t.Context(), renter.ID, order.ID, CreateRequest{
		Type:        "cannot_login",
		Description: "无法登录账号",
	})
	if err != nil {
		t.Fatalf("创建申诉失败: %v", err)
	}
	if created.Status != "open" {
		t.Fatalf("申诉状态 = %s, want open", created.Status)
	}

	cancelled, err := repo.CancelByOrder(t.Context(), renter.ID, order.ID)
	if err != nil {
		t.Fatalf("取消申诉失败: %v", err)
	}
	if cancelled.Status != "cancelled" {
		t.Fatalf("取消后申诉状态 = %s, want cancelled", cancelled.Status)
	}

	var restored model.RentalOrder
	if err := db.First(&restored, order.ID).Error; err != nil {
		t.Fatalf("读取订单失败: %v", err)
	}
	if restored.Status != "renting" || restored.HandoffStatus != "received" || restored.SettlementStatus != "unsettled" {
		t.Fatalf("订单状态未恢复: status=%s handoff=%s settlement=%s", restored.Status, restored.HandoffStatus, restored.SettlementStatus)
	}
	var records []model.HandoffRecord
	if err := db.Where("order_id = ?", order.ID).Order("id ASC").Find(&records).Error; err != nil {
		t.Fatalf("读取交接记录失败: %v", err)
	}
	if len(records) != 2 {
		t.Fatalf("交接记录数量 = %d, want 2", len(records))
	}
	if records[0].Type != "dispute_opened" || records[1].Type != "dispute_cancelled" {
		t.Fatalf("交接记录类型 = %s/%s, want dispute_opened/dispute_cancelled", records[0].Type, records[1].Type)
	}
}

func TestRepositoryCancelRestoresCheckoutDisputeStatus(t *testing.T) {
	db := setupDisputeTestDB(t)
	repo := NewRepository(db)
	now := time.Now()
	_, renter, order := createDisputeOrderFixture(t, db, model.RentalOrder{
		Status:           "pending_checkout_accept",
		HandoffStatus:    "pending_renter_checkout",
		SettlementStatus: "pending",
	})
	checkout := model.OrderCheckout{
		OrderID:               order.ID,
		InitiatedBy:           renter.ID,
		Status:                "countered",
		OwnerAdjustmentReason: "补扣押金",
		OwnerAdjustedAt:       &now,
	}
	if err := db.Create(&checkout).Error; err != nil {
		t.Fatalf("创建结账记录失败: %v", err)
	}

	if _, err := repo.Create(t.Context(), renter.ID, order.ID, CreateRequest{
		Type:        "checkout_amount",
		Description: "不同意结账修正",
	}); err != nil {
		t.Fatalf("创建结账争议失败: %v", err)
	}

	if _, err := repo.CancelByOrder(t.Context(), renter.ID, order.ID); err != nil {
		t.Fatalf("取消结账争议失败: %v", err)
	}

	var restored model.RentalOrder
	if err := db.First(&restored, order.ID).Error; err != nil {
		t.Fatalf("读取订单失败: %v", err)
	}
	if restored.Status != "pending_checkout_accept" || restored.HandoffStatus != "pending_renter_checkout" || restored.SettlementStatus != "pending" {
		t.Fatalf("订单状态未恢复: status=%s handoff=%s settlement=%s", restored.Status, restored.HandoffStatus, restored.SettlementStatus)
	}

	var restoredCheckout model.OrderCheckout
	if err := db.First(&restoredCheckout, checkout.ID).Error; err != nil {
		t.Fatalf("读取结账记录失败: %v", err)
	}
	if restoredCheckout.Status != "countered" {
		t.Fatalf("结账状态 = %s, want countered", restoredCheckout.Status)
	}
	if restoredCheckout.RenterRejectedAt != nil {
		t.Fatalf("取消争议后 renter_rejected_at 应被清空")
	}
	var records []model.HandoffRecord
	if err := db.Where("order_id = ?", order.ID).Order("id ASC").Find(&records).Error; err != nil {
		t.Fatalf("读取交接记录失败: %v", err)
	}
	if len(records) != 2 {
		t.Fatalf("交接记录数量 = %d, want 2", len(records))
	}
	if records[0].Type != "checkout_dispute_opened" || records[1].Type != "checkout_dispute_cancelled" {
		t.Fatalf("交接记录类型 = %s/%s, want checkout_dispute_opened/checkout_dispute_cancelled", records[0].Type, records[1].Type)
	}
}

func TestBuildArbitrationSettlementSkipsFrozenReleaseWhenNoFrozenBalance(t *testing.T) {
	order := model.RentalOrder{
		ID:                  11,
		OrderNo:             "RO202606080001",
		RenterID:            101,
		OwnerID:             202,
		RentAmountCent:      20000,
		OwnerRentAmountCent: 18000,
		DepositAmountCent:   10000,
	}

	settlement, err := buildArbitrationSettlement(order, ArbitrateRequest{
		Result: "release_deposit",
		Remark: "测试裁决",
	}, 0)
	if err != nil {
		t.Fatalf("buildArbitrationSettlement() error = %v", err)
	}

	for _, entry := range settlement.Entries {
		if entry.BizType == "arbitration_release_frozen" {
			t.Fatalf("不应在无冻结余额时生成解冻流水: %+v", entry)
		}
	}
	if settlement.OwnerIncomeAmountCent != 18000 {
		t.Fatalf("OwnerIncomeAmountCent = %d, want 18000", settlement.OwnerIncomeAmountCent)
	}
	if settlement.RenterRefundAmountCent != 10000 {
		t.Fatalf("RenterRefundAmountCent = %d, want 10000", settlement.RenterRefundAmountCent)
	}
	for _, entry := range settlement.Entries {
		if entry.UserID == order.RenterID && entry.BizType == "arbitration_renter_refund" {
			t.Fatalf("外部支付订单不应生成租客钱包退款流水: %+v", entry)
		}
	}
}

func TestBuildArbitrationSettlementLimitsFrozenReleaseToExistingBalance(t *testing.T) {
	order := model.RentalOrder{
		ID:                  12,
		OrderNo:             "RO202606080002",
		RenterID:            101,
		OwnerID:             202,
		RentAmountCent:      20000,
		OwnerRentAmountCent: 18000,
		DepositAmountCent:   10000,
	}

	settlement, err := buildArbitrationSettlement(order, ArbitrateRequest{
		Result: "order_close",
		Remark: "测试裁决",
	}, 12000)
	if err != nil {
		t.Fatalf("buildArbitrationSettlement() error = %v", err)
	}

	releaseEntryCount := 0
	for _, entry := range settlement.Entries {
		if entry.BizType != "arbitration_release_frozen" {
			continue
		}
		releaseEntryCount++
		if entry.AmountCent != 12000 {
			t.Fatalf("release frozen amount cent = %d, want 12000", entry.AmountCent)
		}
	}
	if releaseEntryCount != 1 {
		t.Fatalf("release frozen entry count = %d, want 1", releaseEntryCount)
	}
}

func TestBuildArbitrationHandoffContent(t *testing.T) {
	content := buildArbitrationHandoffContent(ArbitrateRequest{
		Result: "partial_refund",
		Remark: "账号异常，退还部分租金。",
	}, arbitrationSettlement{
		RenterRefundAmountCent: 8000,
		OwnerIncomeAmountCent:  12000,
	})

	wantParts := []string{
		"客服仲裁结果：部分退款",
		"处理说明：账号异常，退还部分租金。",
		"退款给租客：¥80.0",
		"结算给号主：¥120.0",
	}
	for _, part := range wantParts {
		if !strings.Contains(content, part) {
			t.Fatalf("handoff content = %q, want include %q", content, part)
		}
	}
}
