package dispute

import (
	"context"
	"errors"
	"fmt"
	"log"
	"time"

	"hfb_sys/backend/internal/model"
	"hfb_sys/backend/internal/modules/notification"
	"hfb_sys/backend/internal/modules/wallet"
	"hfb_sys/backend/pkg/money"

	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

func (r *Repository) Arbitrate(ctx context.Context, adminID uint64, id uint64, req ArbitrateRequest, meta AuditMeta) (*DisputeDTO, error) {
	var refund *refundAction
	err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		var row model.Dispute
		if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&row, id).Error; err != nil {
			return err
		}
		if row.Status != "open" && row.Status != "processing" {
			return ErrDisputeCannotHandle
		}
		var order model.RentalOrder
		if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&order, row.OrderID).Error; err != nil {
			return err
		}
		var listing model.RentalListing
		if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&listing, order.ListingID).Error; err != nil {
			return err
		}
		var account model.GameAccount
		if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&account, order.AccountID).Error; err != nil {
			return err
		}
		beforeOrderStatus := order.Status
		beforeHandoffStatus := order.HandoffStatus
		beforeSettlementStatus := order.SettlementStatus
		beforeListingStatus := listing.Status
		beforeAccountStatus := account.Status
		frozenBalance, err := renterFrozenBalance(tx, order.RenterID)
		if err != nil {
			return err
		}
		settlement, err := buildArbitrationSettlement(order, req, frozenBalance)
		if err != nil {
			return err
		}
		now := time.Now()
		row.Status = "resolved"
		row.ArbitrationResult = req.Result
		row.ArbitrationRemark = req.Remark
		row.HandledBy = &adminID
		row.HandledAt = &now
		order.Status = arbitrateOrderStatus(req.Result)
		order.SettlementStatus = "arbitrated"
		order.SettledAt = &now
		order.OwnerSettledAt = &now
		if order.HandoffStatus != "cancelled" && order.HandoffStatus != "returned" {
			order.HandoffStatus = "arbitrated"
		}
		if req.Result == "mark_abnormal" {
			listing.Status = "abnormal"
			listing.InTransaction = false
			listing.PublishedAt = nil
			account.Status = "abnormal"
		} else {
			// 仲裁会终结当前订单，账号统一归档下架，避免已结案账号重新出现在公开列表。
			listing.Status = "offline"
			listing.InTransaction = false
			listing.PublishedAt = nil
			account.Status = "offline"
		}
		if err := wallet.AppendEntries(tx, settlement.Entries...); err != nil {
			return err
		}
		// 押金暂扣：若订单被暂扣，则把本次退款中的押金部分挂起不退，仅退还剩余部分。
		refundAmountCent := order.ApplyDepositHold(settlement.RenterRefundAmountCent, settlement.RenterDepositRefundCent)
		if refundAmountCent > 0 {
			action, err := r.prepareRefund(&order, refundAmountCent, "arbitration_refund", "仲裁退款原路退还")
			if err != nil {
				return err
			}
			refund = action
		}
		if err := tx.Save(&row).Error; err != nil {
			return err
		}
		if err := tx.Save(&order).Error; err != nil {
			return err
		}
		if err := tx.Save(&listing).Error; err != nil {
			return err
		}
		if err := tx.Save(&account).Error; err != nil {
			return err
		}
		handoffRecord := model.HandoffRecord{
			OrderID:    order.ID,
			FromUserID: adminID,
			ToUserID:   order.RenterID,
			Type:       "admin_arbitration",
			Content:    buildArbitrationHandoffContent(req, settlement),
		}
		if err := tx.Create(&handoffRecord).Error; err != nil {
			return err
		}
		disputeID := row.ID
		if err := appendAuditLog(tx, adminID, "dispute.arbitrate", "dispute", row.ID, meta, map[string]any{
			"dispute_id":                 row.ID,
			"order_id":                   order.ID,
			"order_no":                   order.OrderNo,
			"result":                     req.Result,
			"remark":                     req.Remark,
			"input_amount_cent":          req.AmountCent,
			"renter_refund_amount_cent":  settlement.RenterRefundAmountCent,
			"owner_income_amount_cent":   settlement.OwnerIncomeAmountCent,
			"deposit_deduct_amount_cent": settlement.DepositDeductAmountCent,
			"before_order_status":        beforeOrderStatus,
			"after_order_status":         order.Status,
			"before_handoff_status":      beforeHandoffStatus,
			"after_handoff_status":       order.HandoffStatus,
			"before_settlement_status":   beforeSettlementStatus,
			"after_settlement_status":    order.SettlementStatus,
			"before_listing_status":      beforeListingStatus,
			"after_listing_status":       listing.Status,
			"before_account_status":      beforeAccountStatus,
			"after_account_status":       account.Status,
		}); err != nil {
			return err
		}
		if err := notification.Append(tx,
			notification.Entry{
				UserID:  order.RenterID,
				Type:    "arbitration",
				Title:   "申诉仲裁已完成",
				Content: "客服已给出仲裁结果，请在订单和申诉记录中查看处理说明。",
				BizType: "dispute",
				BizID:   &disputeID,
			},
			notification.Entry{
				UserID:  order.OwnerID,
				Type:    "arbitration",
				Title:   "申诉仲裁已完成",
				Content: "客服已给出仲裁结果，请在订单和申诉记录中查看处理说明。",
				BizType: "dispute",
				BizID:   &disputeID,
			},
		); err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		return nil, err
	}
	r.startRefundBestEffort(ctx, refund)
	var row disputeRow
	if err := r.baseQuery(ctx).Where("d.id = ?", id).First(&row).Error; err != nil {
		return nil, err
	}
	dto := row.toDTO()
	return &dto, nil
}

type arbitrationSettlement struct {
	Entries                 []wallet.Entry
	RenterRefundAmountCent  int64
	RenterDepositRefundCent int64
	OwnerIncomeAmountCent   int64
	DepositDeductAmountCent int64
}

func buildArbitrationSettlement(order model.RentalOrder, req ArbitrateRequest, renterFrozenBalanceCent int64) (arbitrationSettlement, error) {
	rentAmountCent := order.RentAmountCent
	depositAmountCent := order.DepositAmountCent
	ownerRentAmountCent := order.OwnerRentAmountCent
	if ownerRentAmountCent <= 0 || ownerRentAmountCent > rentAmountCent {
		ownerRentAmountCent = rentAmountCent
	}
	totalCent := rentAmountCent + depositAmountCent
	settlement := arbitrationSettlement{}
	orderID := order.ID
	releaseFrozenAmountCent := money.MinCent(totalCent, renterFrozenBalanceCent)
	if releaseFrozenAmountCent > 0 {
		settlement.Entries = append(settlement.Entries, wallet.Entry{
			UserID:      order.RenterID,
			OrderID:     &orderID,
			Direction:   "out",
			AmountCent:  releaseFrozenAmountCent,
			BalanceType: "frozen",
			BizType:     "arbitration_release_frozen",
			BizNo:       order.OrderNo,
			Remark:      "仲裁释放冻结金额",
		})
	}

	addRenterRefund := func(amountCent int64, remark string) {
		if amountCent <= 0 {
			return
		}
		settlement.RenterRefundAmountCent += amountCent
	}
	addOwnerIncome := func(amountCent int64, remark string) {
		if amountCent <= 0 {
			return
		}
		settlement.OwnerIncomeAmountCent += amountCent
		settlement.Entries = append(settlement.Entries, wallet.Entry{
			UserID:      order.OwnerID,
			OrderID:     &orderID,
			Direction:   "in",
			AmountCent:  amountCent,
			BalanceType: "available",
			BizType:     "arbitration_owner_income",
			BizNo:       order.OrderNo,
			Remark:      remark,
		})
	}

	switch req.Result {
	case "full_refund":
		addRenterRefund(totalCent, "仲裁全额退款")
		settlement.RenterDepositRefundCent = depositAmountCent
	case "partial_refund":
		if req.AmountCent <= 0 || req.AmountCent > totalCent {
			return settlement, ErrInvalidDispute
		}
		addRenterRefund(req.AmountCent, "仲裁部分退款")
		// 部分退款为合并金额，无法精确拆分租金/押金，按押金优先归类以便暂扣。
		settlement.RenterDepositRefundCent = money.MinCent(req.AmountCent, depositAmountCent)
		addOwnerIncome(money.MinCent(totalCent-req.AmountCent, ownerRentAmountCent+depositAmountCent), "仲裁剩余金额结算给号主")
	case "release_deposit":
		addOwnerIncome(ownerRentAmountCent, "仲裁确认订单金额结算给号主")
		addRenterRefund(depositAmountCent, "仲裁释放押金给租客")
		settlement.RenterDepositRefundCent = depositAmountCent
	case "deduct_deposit", "compensate_owner":
		deductAmountCent := req.AmountCent
		if deductAmountCent <= 0 {
			deductAmountCent = depositAmountCent
		}
		if deductAmountCent > depositAmountCent {
			return settlement, ErrInvalidDispute
		}
		settlement.DepositDeductAmountCent = deductAmountCent
		addOwnerIncome(ownerRentAmountCent+deductAmountCent, "仲裁订单金额及押金赔付结算给号主")
		addRenterRefund(depositAmountCent-deductAmountCent, "仲裁退回剩余押金给租客")
		settlement.RenterDepositRefundCent = depositAmountCent - deductAmountCent
	case "order_close":
		// Only release frozen funds. No available-balance settlement happens in development mode.
	case "mark_abnormal":
		// 标记异常只释放冻结账务，后续由客服继续线下复核。
	default:
		return settlement, ErrInvalidDispute
	}
	return settlement, nil
}

func (r *Repository) prepareRefund(order *model.RentalOrder, amountCent int64, bizType string, remark string) (*refundAction, error) {
	if amountCent <= 0 {
		return nil, nil
	}
	if r.refundStarter == nil {
		return nil, ErrDependencyUnavailable
	}
	order.RefundStatus = "pending"
	order.RefundAmountCent = amountCent
	order.RefundedAt = nil
	return &refundAction{
		OrderID:          order.ID,
		RefundAmountCent: amountCent,
		BizType:          bizType,
		Remark:           remark,
	}, nil
}

func (r *Repository) startRefundBestEffort(ctx context.Context, action *refundAction) {
	if action == nil || r.refundStarter == nil {
		return
	}
	if _, err := r.refundStarter.StartRefund(ctx, action.OrderID, action.RefundAmountCent, action.BizType, action.Remark); err != nil {
		log.Printf("[dispute] start refund failed order_id=%d biz_type=%s amount_cent=%d err=%v", action.OrderID, action.BizType, action.RefundAmountCent, err)
	}
}

func renterFrozenBalance(tx *gorm.DB, renterID uint64) (int64, error) {
	var account model.WalletAccount
	err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
		Where("user_id = ?", renterID).
		First(&account).Error
	if errors.Is(err, gorm.ErrRecordNotFound) {
		return 0, nil
	}
	if err != nil {
		return 0, err
	}
	return account.FrozenBalanceCent, nil
}

func arbitrateOrderStatus(result string) string {
	switch result {
	case "full_refund", "partial_refund", "order_close":
		return "closed"
	case "mark_abnormal":
		return "abnormal"
	default:
		return "completed"
	}
}

func buildArbitrationHandoffContent(req ArbitrateRequest, settlement arbitrationSettlement) string {
	content := fmt.Sprintf("客服仲裁结果：%s", arbitrationResultLabel(req.Result))
	if req.Remark != "" {
		content += "\n处理说明：" + req.Remark
	}
	if settlement.RenterRefundAmountCent > 0 {
		content += "\n退款给租客：" + money.FormatWithSymbol(settlement.RenterRefundAmountCent)
	}
	if settlement.OwnerIncomeAmountCent > 0 {
		content += "\n结算给号主：" + money.FormatWithSymbol(settlement.OwnerIncomeAmountCent)
	}
	if settlement.DepositDeductAmountCent > 0 {
		content += "\n押金扣除：" + money.FormatWithSymbol(settlement.DepositDeductAmountCent)
	}
	return content
}

func arbitrationResultLabel(result string) string {
	switch result {
	case "full_refund":
		return "全额退款"
	case "partial_refund":
		return "部分退款"
	case "deduct_deposit":
		return "扣押金"
	case "release_deposit":
		return "释放押金"
	case "compensate_owner":
		return "赔付号主"
	case "order_close":
		return "关闭订单"
	case "mark_abnormal":
		return "标记异常"
	default:
		return result
	}
}
