// Package refund 提供退款申请的业务逻辑服务
// 包含退款申请的创建、审批、拒绝、退回、重新提交等完整生命周期管理
// 审批通过后异步执行佣金回扣和退款后资产处理
package refund

import (
	"context"
	"fmt"
	"math/rand"
	"strings"
	"time"

	"go.uber.org/zap"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"

	refundapprovalapp "github.com/break/junhong_cmp_fiber/internal/application/refundapproval"
	walletapp "github.com/break/junhong_cmp_fiber/internal/application/wallet"
	"github.com/break/junhong_cmp_fiber/internal/model"
	"github.com/break/junhong_cmp_fiber/internal/model/dto"
	"github.com/break/junhong_cmp_fiber/internal/store"
	"github.com/break/junhong_cmp_fiber/internal/store/postgres"
	"github.com/break/junhong_cmp_fiber/pkg/config"
	"github.com/break/junhong_cmp_fiber/pkg/constants"
	"github.com/break/junhong_cmp_fiber/pkg/errors"
	"github.com/break/junhong_cmp_fiber/pkg/middleware"

	deviceSvc "github.com/break/junhong_cmp_fiber/internal/service/device"
	iotCardSvc "github.com/break/junhong_cmp_fiber/internal/service/iot_card"
	packageSvc "github.com/break/junhong_cmp_fiber/internal/service/package"
)

// Service 退款业务服务
// 负责退款申请的 CRUD、审批流程、佣金回扣和退款后资产处理
type Service struct {
	db                          *gorm.DB
	refundStore                 *postgres.RefundStore
	orderStore                  *postgres.OrderStore
	commissionRecordStore       *postgres.CommissionRecordStore
	agentWalletStore            *postgres.AgentWalletStore
	agentWalletTransactionStore *postgres.AgentWalletTransactionStore
	stopResumeService           *iotCardSvc.StopResumeService
	deviceService               *deviceSvc.Service
	packageActivationService    *packageSvc.ActivationService
	iotCardStore                *postgres.IotCardStore
	deviceStore                 *postgres.DeviceStore
	assetWalletStore            *postgres.AssetWalletStore
	agentWalletRefundService    *walletapp.RefundService
	refundApprovalCreation      *refundapprovalapp.CreationService
	logger                      *zap.Logger
}

// New 创建退款业务服务实例
func New(
	db *gorm.DB,
	refundStore *postgres.RefundStore,
	orderStore *postgres.OrderStore,
	commissionRecordStore *postgres.CommissionRecordStore,
	agentWalletStore *postgres.AgentWalletStore,
	agentWalletTransactionStore *postgres.AgentWalletTransactionStore,
	stopResumeService *iotCardSvc.StopResumeService,
	deviceService *deviceSvc.Service,
	packageActivationService *packageSvc.ActivationService,
	iotCardStore *postgres.IotCardStore,
	deviceStore *postgres.DeviceStore,
	assetWalletStore *postgres.AssetWalletStore,
	logger *zap.Logger,
) *Service {
	return &Service{
		db:                          db,
		refundStore:                 refundStore,
		orderStore:                  orderStore,
		commissionRecordStore:       commissionRecordStore,
		agentWalletStore:            agentWalletStore,
		agentWalletTransactionStore: agentWalletTransactionStore,
		stopResumeService:           stopResumeService,
		deviceService:               deviceService,
		packageActivationService:    packageActivationService,
		iotCardStore:                iotCardStore,
		deviceStore:                 deviceStore,
		assetWalletStore:            assetWalletStore,
		logger:                      logger,
	}
}

// SetAgentWalletRefundService 注入代理主钱包统一退款回充用例。
func (s *Service) SetAgentWalletRefundService(service *walletapp.RefundService) {
	s.agentWalletRefundService = service
}

// SetRefundApprovalCreationService 注入退款企微审批申请用例。
func (s *Service) SetRefundApprovalCreationService(service *refundapprovalapp.CreationService) {
	s.refundApprovalCreation = service
}

// Create 创建退款申请
// 校验订单存在且已支付，检查是否存在活跃退款申请，生成退款单号并创建记录
func (s *Service) Create(ctx context.Context, req *dto.CreateRefundRequest) (*dto.RefundResponse, error) {
	userID := middleware.GetUserIDFromContext(ctx)
	if userID == 0 {
		return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
	}

	// 校验订单存在且已支付
	order, err := s.orderStore.GetByID(ctx, req.OrderID)
	if err != nil {
		return nil, errors.New(errors.CodeNotFound, "订单不存在")
	}
	if order.PaymentStatus != model.PaymentStatusPaid {
		if order.PaymentStatus == model.PaymentStatusRefunded {
			return nil, errors.New(errors.CodeInvalidStatus, "订单已退款，无法重复申请退款")
		}
		return nil, errors.New(errors.CodeInvalidStatus, "仅已支付订单可申请退款")
	}
	if err := validateRequestedRefundAmountByOrder(req.RequestedRefundAmount, order); err != nil {
		return nil, err
	}
	refundVoucherKey, err := normalizeRefundVoucherKey(req.RefundVoucherKey)
	if err != nil {
		return nil, err
	}

	// 从订单获取 shop_id：优先使用 SellerShopID，代理商买家使用 BuyerID
	var shopID *uint
	if order.SellerShopID != nil {
		shopID = order.SellerShopID
	} else if order.BuyerType == model.BuyerTypeAgent {
		shopID = &order.BuyerID
	}

	refund := &model.RefundRequest{
		RefundNo:              generateRefundNo(),
		OrderID:               req.OrderID,
		OrderNo:               order.OrderNo,
		OrderType:             order.OrderType,
		AssetIdentifier:       order.AssetIdentifier,
		IotCardID:             order.IotCardID,
		DeviceID:              order.DeviceID,
		PackageUsageID:        req.PackageUsageID,
		ShopID:                shopID,
		ActualReceivedAmount:  req.ActualReceivedAmount,
		RequestedRefundAmount: req.RequestedRefundAmount,
		RefundVoucherKey:      refundVoucherKey,
		RefundReason:          req.RefundReason,
		Status:                model.RefundStatusPending,
	}
	refund.Creator = userID
	refund.Updater = userID

	if s.refundApprovalCreation == nil {
		return nil, errors.New(errors.CodeServiceUnavailable, "退款审批能力未配置")
	}
	result, err := s.refundApprovalCreation.Execute(ctx, refundapprovalapp.CreateCommand{
		Refund: refund, SubmitterAccountID: userID,
	})
	if err != nil {
		return nil, err
	}

	resp := buildRefundResponse(result.Refund)
	resp.SubmitterName = result.SubmitterName
	resp.ApprovalProvider = constants.IntegrationProviderWeCom
	resp.ApprovalStatus = &result.ApprovalStatus
	resp.ApprovalStatusName = constants.GetApprovalStatusName(result.ApprovalStatus)
	return resp, nil
}

// List 分页查询退款申请列表
func (s *Service) List(ctx context.Context, req *dto.RefundListRequest) (*dto.RefundListResponse, error) {
	opts := &store.QueryOptions{
		Page:     req.Page,
		PageSize: req.PageSize,
		OrderBy:  "tb_refund_request.created_at DESC",
	}
	if opts.Page == 0 {
		opts.Page = 1
	}
	if opts.PageSize == 0 {
		opts.PageSize = constants.DefaultPageSize
	}

	filters := &postgres.RefundListFilters{
		Status:          req.Status,
		OrderID:         req.OrderID,
		ShopID:          req.ShopID,
		AssetIdentifier: req.AssetIdentifier,
	}

	requests, total, err := s.refundStore.List(ctx, opts, filters)
	if err != nil {
		return nil, errors.Wrap(errors.CodeInternalError, err, "查询退款申请列表失败")
	}
	submitterNames, err := s.loadSubmitterNames(ctx, refundSubmitterIDs(requests))
	if err != nil {
		return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询退款提交人失败")
	}
	approvalSummaries, err := s.loadApprovalSummaries(ctx, requests)
	if err != nil {
		return nil, err
	}

	items := make([]dto.RefundResponse, 0, len(requests))
	for _, r := range requests {
		item := buildRefundResponse(r)
		item.SubmitterName = submitterNames[r.Creator]
		applyApprovalSummary(item, approvalSummaries, r.ApprovalInstanceID)
		items = append(items, *item)
	}

	return &dto.RefundListResponse{
		Items: items,
		Total: total,
		Page:  opts.Page,
		Size:  opts.PageSize,
	}, nil
}

// GetByID 根据 ID 查询退款申请详情
func (s *Service) GetByID(ctx context.Context, id uint) (*dto.RefundResponse, error) {
	refund, err := s.refundStore.GetByID(ctx, id)
	if err != nil {
		return nil, errors.New(errors.CodeNotFound, "退款申请不存在")
	}
	resp := buildRefundResponse(refund)
	resp.SubmitterName = s.loadSubmitterNameBestEffort(ctx, refund.Creator)
	approvalSummaries, err := s.loadApprovalSummaries(ctx, []*model.RefundRequest{refund})
	if err != nil {
		return nil, err
	}
	applyApprovalSummary(resp, approvalSummaries, refund.ApprovalInstanceID)
	return resp, nil
}

// Approve 审批通过退款申请
// 条件更新 WHERE status=1，设置审批信息
// 事务提交成功后异步执行佣金回扣和退款后资产处理
func (s *Service) Approve(ctx context.Context, id uint, req *dto.ApproveRefundRequest) error {
	if !legacyRefundManualEnabled() {
		return errors.New(errors.CodeInvalidStatus, "退款人工审批入口已停用，请查看企业微信审批状态")
	}
	userID := middleware.GetUserIDFromContext(ctx)
	if userID == 0 {
		return errors.New(errors.CodeUnauthorized, "未授权访问")
	}
	if err := ensureRefundProcessor(ctx); err != nil {
		return err
	}

	refund, err := s.refundStore.GetByID(ctx, id)
	if err != nil {
		return errors.New(errors.CodeNotFound, "退款申请不存在")
	}
	if refund.Status != model.RefundStatusPending {
		return errors.New(errors.CodeInvalidStatus, "仅待审批状态可审批通过")
	}
	if refund.ApprovalInstanceID != nil {
		return errors.New(errors.CodeInvalidStatus, "该退款申请由企业微信审批决定，不能人工审批")
	}

	now := time.Now()
	approvedAmount := refund.RequestedRefundAmount
	if req.ApprovedRefundAmount != nil {
		approvedAmount = *req.ApprovedRefundAmount
	}
	order, err := s.orderStore.GetByID(ctx, refund.OrderID)
	if err != nil {
		return errors.New(errors.CodeNotFound, "订单不存在")
	}
	if err := validateApprovedRefundAmount(approvedAmount, refund.RequestedRefundAmount, order); err != nil {
		return err
	}

	// 事务内同步更新退款状态、订单支付状态和钱包回款，避免订单已退款但资金未退回。
	if err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		result := tx.Model(&model.RefundRequest{}).
			Where("id = ? AND status = ?", id, model.RefundStatusPending).
			Updates(map[string]any{
				"status":                 model.RefundStatusApproved,
				"processor_id":           userID,
				"processed_at":           now,
				"approved_refund_amount": approvedAmount,
				"remark":                 req.Remark,
				"updater":                userID,
				"updated_at":             now,
			})
		if result.Error != nil {
			return errors.Wrap(errors.CodeInternalError, result.Error, "审批退款申请失败")
		}
		if result.RowsAffected == 0 {
			return errors.New(errors.CodeInvalidStatus, "退款申请状态已变更，请刷新后重试")
		}

		orderResult := tx.Model(&model.Order{}).
			Where("id = ? AND payment_status = ?", refund.OrderID, model.PaymentStatusPaid).
			Updates(map[string]any{
				"payment_status": model.PaymentStatusRefunded,
				"updated_at":     now,
			})
		if orderResult.Error != nil {
			return errors.Wrap(errors.CodeInternalError, orderResult.Error, "更新订单退款状态失败")
		}
		if orderResult.RowsAffected == 0 {
			return errors.New(errors.CodeInvalidStatus, "订单状态已变更，无法执行退款审批")
		}

		if err := s.refundWalletPayment(ctx, tx, refund, order, approvedAmount, userID); err != nil {
			return err
		}

		return nil
	}); err != nil {
		return err
	}

	// 事务提交成功后，异步执行佣金回扣和退款后资产处理（失败不影响审批结果）
	go func() {
		asyncCtx := context.Background()
		s.deductAllCommission(asyncCtx, id)
	}()

	go func() {
		asyncCtx := context.Background()
		s.handleRefundAssetProcessing(asyncCtx, id)
	}()

	return nil
}

// refundWalletPayment 处理钱包支付订单的退款回款。
func (s *Service) refundWalletPayment(ctx context.Context, tx *gorm.DB, refund *model.RefundRequest, order *model.Order, amount int64, operatorID uint) error {
	if order.PaymentMethod != model.PaymentMethodWallet {
		return nil
	}

	switch order.BuyerType {
	case model.BuyerTypeAgent:
		return s.refundAgentWalletPayment(ctx, tx, refund, order, amount, operatorID)
	case model.BuyerTypePersonal:
		return s.refundAssetWalletPayment(ctx, tx, refund, order, amount, operatorID)
	default:
		return errors.New(errors.CodeInvalidParam, "不支持的钱包退款买家类型")
	}
}

// refundAgentWalletPayment 将代理钱包支付的订单退款退回原扣款主钱包。
func (s *Service) refundAgentWalletPayment(ctx context.Context, tx *gorm.DB, refund *model.RefundRequest, order *model.Order, amount int64, operatorID uint) error {
	if s.agentWalletRefundService == nil {
		return errors.New(errors.CodeInternalError, "代理主钱包退款能力未配置")
	}

	legacyPayerShopID, legacyRelatedShopID, _ := resolveAgentWalletRefundShopID(order)
	legacyDeductAmount := order.TotalAmount
	if order.ActualPaidAmount != nil && *order.ActualPaidAmount > 0 {
		legacyDeductAmount = *order.ActualPaidAmount
	}
	requestID := ""
	if value := middleware.GetRequestIDFromContext(ctx); value != nil {
		requestID = *value
	}
	correlationID := refund.RefundNo
	if correlationID == "" {
		correlationID = order.OrderNo
	}
	remark := fmt.Sprintf("订单%s退款退回预充值钱包", order.OrderNo)
	assetType, assetID, assetIdentifier := buildRefundWalletTransactionAssetSnapshot(order)
	_, err := s.agentWalletRefundService.RefundInTx(ctx, tx, walletapp.RefundCommand{
		OrderID: order.ID, RefundID: refund.ID, Amount: amount,
		LegacyPayerShopID: legacyPayerShopID, LegacyDeductAmount: legacyDeductAmount,
		LegacyRelatedShopID: legacyRelatedShopID, UserID: operatorID, Creator: operatorID,
		AssetType: assetType, AssetID: assetID, AssetIdentifier: assetIdentifier,
		Remark: remark, RequestID: requestID, CorrelationID: correlationID,
	})
	return err
}

// resolveAgentWalletRefundShopID 兼容旧订单：没有扣款流水时按订单角色推导原扣款店铺。
func resolveAgentWalletRefundShopID(order *model.Order) (uint, *uint, error) {
	if order.PurchaseRole == model.PurchaseRolePurchaseForSubordinate {
		if order.OperatorID == nil || *order.OperatorID == 0 {
			return 0, nil, errors.New(errors.CodeInternalError, "代理代购订单缺少原扣款代理")
		}
		relatedShopID := order.BuyerID
		return *order.OperatorID, &relatedShopID, nil
	}

	if order.OperatorID != nil && *order.OperatorID > 0 && order.OperatorType == model.OperatorAccountTypeAgent {
		return *order.OperatorID, nil, nil
	}
	if order.BuyerID == 0 {
		return 0, nil, errors.New(errors.CodeInternalError, "订单买家店铺为空")
	}
	return order.BuyerID, nil, nil
}

// refundAssetWalletPayment 将个人资产钱包支付的订单退款退回原资产钱包。
func (s *Service) refundAssetWalletPayment(ctx context.Context, tx *gorm.DB, refund *model.RefundRequest, order *model.Order, amount int64, operatorID uint) error {
	var existing model.AssetWalletTransaction
	err := tx.WithContext(ctx).
		Where("reference_type = ? AND reference_no = ? AND transaction_type = ? AND status = ?",
			constants.ReferenceTypeRefund, refund.RefundNo, constants.AssetTransactionTypeRefund, constants.TransactionStatusSuccess).
		First(&existing).Error
	if err == nil {
		if existing.Amount != amount {
			return errors.New(errors.CodeConflict, "退款申请已存在不一致的资产钱包回款流水")
		}
		return nil
	}
	if err != gorm.ErrRecordNotFound {
		return errors.Wrap(errors.CodeDatabaseError, err, "复核资产钱包退款流水失败")
	}
	wallet, err := s.resolveAssetRefundWallet(tx, order)
	if err != nil {
		return err
	}

	balanceBefore := wallet.Balance
	if err := s.assetWalletStore.AddBalanceWithTx(ctx, tx, wallet.ID, amount); err != nil {
		return errors.Wrap(errors.CodeDatabaseError, err, "退回资产钱包失败")
	}

	refType := constants.ReferenceTypeRefund
	refNo := refund.RefundNo
	remark := fmt.Sprintf("订单%s退款退回资产钱包", order.OrderNo)
	transaction := &model.AssetWalletTransaction{
		AssetWalletID:   wallet.ID,
		ResourceType:    wallet.ResourceType,
		ResourceID:      wallet.ResourceID,
		UserID:          operatorID,
		TransactionType: constants.AssetTransactionTypeRefund,
		Amount:          amount,
		BalanceBefore:   balanceBefore,
		BalanceAfter:    balanceBefore + amount,
		Status:          constants.TransactionStatusSuccess,
		ReferenceType:   &refType,
		ReferenceNo:     &refNo,
		Remark:          &remark,
		Creator:         operatorID,
		ShopIDTag:       wallet.ShopIDTag,
		EnterpriseIDTag: wallet.EnterpriseIDTag,
	}
	if err := tx.WithContext(ctx).Create(transaction).Error; err != nil {
		return errors.Wrap(errors.CodeDatabaseError, err, "创建资产钱包退款流水失败")
	}

	return nil
}

// resolveAssetRefundWallet 优先按原扣款流水定位回款资产钱包。
func (s *Service) resolveAssetRefundWallet(tx *gorm.DB, order *model.Order) (*model.AssetWallet, error) {
	var deductTx model.AssetWalletTransaction
	err := tx.Where("reference_type = ? AND reference_no = ? AND transaction_type = ?",
		constants.ReferenceTypeOrder, order.OrderNo, constants.AssetTransactionTypeDeduct).
		Order("id ASC").
		First(&deductTx).Error
	if err == nil {
		return lockAssetWalletByID(tx, deductTx.AssetWalletID)
	}
	if err != gorm.ErrRecordNotFound {
		return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询原资产钱包扣款流水失败")
	}

	resourceType, resourceID, err := resolveOrderAssetWalletResource(order)
	if err != nil {
		return nil, err
	}
	return lockAssetWalletByResource(tx, resourceType, resourceID)
}

// resolveOrderAssetWalletResource 从订单载体推导资产钱包归属。
func resolveOrderAssetWalletResource(order *model.Order) (string, uint, error) {
	switch order.OrderType {
	case model.OrderTypeSingleCard:
		if order.IotCardID == nil || *order.IotCardID == 0 {
			return "", 0, errors.New(errors.CodeInternalError, "单卡订单缺少卡ID")
		}
		return constants.AssetWalletResourceTypeIotCard, *order.IotCardID, nil
	case model.OrderTypeDevice:
		if order.DeviceID == nil || *order.DeviceID == 0 {
			return "", 0, errors.New(errors.CodeInternalError, "设备订单缺少设备ID")
		}
		return constants.AssetWalletResourceTypeDevice, *order.DeviceID, nil
	default:
		return "", 0, errors.New(errors.CodeInvalidParam, "未知订单类型")
	}
}

// lockAssetWalletByID 锁定资产钱包。
func lockAssetWalletByID(tx *gorm.DB, walletID uint) (*model.AssetWallet, error) {
	var wallet model.AssetWallet
	if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
		Where("id = ?", walletID).
		First(&wallet).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, errors.New(errors.CodeWalletNotFound, "资产钱包不存在")
		}
		return nil, errors.Wrap(errors.CodeDatabaseError, err, "锁定资产钱包失败")
	}
	return &wallet, nil
}

// lockAssetWalletByResource 按资产归属锁定资产钱包。
func lockAssetWalletByResource(tx *gorm.DB, resourceType string, resourceID uint) (*model.AssetWallet, error) {
	var wallet model.AssetWallet
	if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
		Where("resource_type = ? AND resource_id = ?", resourceType, resourceID).
		First(&wallet).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, errors.New(errors.CodeWalletNotFound, "资产钱包不存在")
		}
		return nil, errors.Wrap(errors.CodeDatabaseError, err, "锁定资产钱包失败")
	}
	return &wallet, nil
}

// Reject 审批拒绝退款申请
// 条件更新 WHERE status=1
func (s *Service) Reject(ctx context.Context, id uint, req *dto.RejectRefundRequest) error {
	if !legacyRefundManualEnabled() {
		return errors.New(errors.CodeInvalidStatus, "退款人工审批入口已停用，请查看企业微信审批状态")
	}
	userID := middleware.GetUserIDFromContext(ctx)
	if userID == 0 {
		return errors.New(errors.CodeUnauthorized, "未授权访问")
	}
	if err := ensureRefundProcessor(ctx); err != nil {
		return err
	}

	now := time.Now()
	result := s.db.WithContext(ctx).
		Model(&model.RefundRequest{}).
		Where("id = ? AND status = ? AND approval_instance_id IS NULL", id, model.RefundStatusPending).
		Updates(map[string]any{
			"status":        model.RefundStatusRejected,
			"processor_id":  userID,
			"processed_at":  now,
			"reject_reason": req.RejectReason,
			"updater":       userID,
			"updated_at":    now,
		})
	if result.Error != nil {
		return errors.Wrap(errors.CodeInternalError, result.Error, "拒绝退款申请失败")
	}
	if result.RowsAffected == 0 {
		return errors.New(errors.CodeInvalidStatus, "退款申请状态已变更，请刷新后重试")
	}
	return nil
}

func legacyRefundManualEnabled() bool {
	cfg := config.Get()
	return cfg == nil || cfg.Approval.LegacyRefundManualEnabled
}

// Return 退回退款申请
// 条件更新 WHERE status=1，退回后可重新提交
func (s *Service) Return(ctx context.Context, id uint, req *dto.ReturnRefundRequest) error {
	userID := middleware.GetUserIDFromContext(ctx)
	if userID == 0 {
		return errors.New(errors.CodeUnauthorized, "未授权访问")
	}
	if err := ensureRefundProcessor(ctx); err != nil {
		return err
	}

	now := time.Now()
	result := s.db.WithContext(ctx).
		Model(&model.RefundRequest{}).
		Where("id = ? AND status = ? AND approval_instance_id IS NULL", id, model.RefundStatusPending).
		Updates(map[string]any{
			"status":       model.RefundStatusReturned,
			"processor_id": userID,
			"processed_at": now,
			"remark":       req.Remark,
			"updater":      userID,
			"updated_at":   now,
		})
	if result.Error != nil {
		return errors.Wrap(errors.CodeInternalError, result.Error, "退回退款申请失败")
	}
	if result.RowsAffected == 0 {
		return errors.New(errors.CodeInvalidStatus, "退款申请状态已变更，请刷新后重试")
	}
	return nil
}

// ensureRefundProcessor 确保只有平台侧账号可以处理审批类动作。
func ensureRefundProcessor(ctx context.Context) error {
	userType := middleware.GetUserTypeFromContext(ctx)
	if userType == constants.UserTypeSuperAdmin || userType == constants.UserTypePlatform {
		return nil
	}
	return errors.New(errors.CodeForbidden, "无权限处理退款申请")
}

// Resubmit 重新提交退款申请
// 条件更新 WHERE status=4（已退回），修改部分字段后重新进入待审批状态
func (s *Service) Resubmit(ctx context.Context, id uint, req *dto.ResubmitRefundRequest) error {
	userID := middleware.GetUserIDFromContext(ctx)
	if userID == 0 {
		return errors.New(errors.CodeUnauthorized, "未授权访问")
	}

	refund, err := s.refundStore.GetByID(ctx, id)
	if err != nil {
		return errors.New(errors.CodeInvalidStatus, "仅已退回状态可重新提交")
	}
	if refund.Status != model.RefundStatusReturned {
		return errors.New(errors.CodeInvalidStatus, "仅已退回状态可重新提交")
	}

	requestedRefundAmount := refund.RequestedRefundAmount
	if req.RequestedRefundAmount != nil {
		requestedRefundAmount = *req.RequestedRefundAmount
	}
	refundVoucherKey := refund.RefundVoucherKey
	if req.RefundVoucherKey != nil {
		normalized, normErr := normalizeRefundVoucherKey(*req.RefundVoucherKey)
		if normErr != nil {
			return normErr
		}
		refundVoucherKey = normalized
	}

	order, err := s.orderStore.GetByID(ctx, refund.OrderID)
	if err != nil {
		return errors.New(errors.CodeNotFound, "订单不存在")
	}
	if err := validateRequestedRefundAmountByOrder(requestedRefundAmount, order); err != nil {
		return err
	}

	now := time.Now()
	updates := map[string]any{
		"status":     model.RefundStatusPending,
		"updater":    userID,
		"updated_at": now,
	}
	if req.ActualReceivedAmount != nil {
		updates["actual_received_amount"] = *req.ActualReceivedAmount
	}
	if req.RequestedRefundAmount != nil {
		updates["requested_refund_amount"] = *req.RequestedRefundAmount
	}
	if req.RefundVoucherKey != nil {
		updates["refund_voucher_key"] = refundVoucherKey
	}
	if req.RefundReason != nil {
		updates["refund_reason"] = *req.RefundReason
	}

	result := s.db.WithContext(ctx).
		Model(&model.RefundRequest{}).
		Where("id = ? AND status = ?", id, model.RefundStatusReturned).
		Updates(updates)
	if result.Error != nil {
		return errors.Wrap(errors.CodeInternalError, result.Error, "重新提交退款申请失败")
	}
	if result.RowsAffected == 0 {
		return errors.New(errors.CodeInvalidStatus, "仅已退回状态可重新提交")
	}
	return nil
}

// deductAllCommission 幂等回扣该订单所有已入账佣金。
// 每条佣金在独立事务中锁定并失效；全部完成后才设置退款单完成标记。
func (s *Service) deductAllCommission(ctx context.Context, refundID uint) {
	logger := s.logger

	// 查询退款单
	var refund model.RefundRequest
	if err := s.db.Where("id = ?", refundID).First(&refund).Error; err != nil {
		logger.Error("佣金回扣：查询退款单失败", zap.Uint("refund_id", refundID), zap.Error(err))
		return
	}
	if refund.CommissionDeducted {
		return
	}

	// 查询该订单所有已入账佣金记录
	var commissions []model.CommissionRecord
	if err := s.db.Where("order_id = ? AND status = ?", refund.OrderID, constants.CommissionStatusReleased).Find(&commissions).Error; err != nil {
		logger.Error("佣金回扣：查询佣金记录失败", zap.Uint("refund_id", refundID), zap.Uint("order_id", refund.OrderID), zap.Error(err))
		return
	}

	if len(commissions) == 0 {
		logger.Info("佣金回扣：无已入账佣金记录", zap.Uint("refund_id", refundID))
		// 无佣金需要回扣，直接标记完成
		s.db.Model(&model.RefundRequest{}).Where("id = ?", refundID).Update("commission_deducted", true)
		return
	}

	allSucceeded := true
	// 对每条佣金记录执行扣减
	for _, commission := range commissions {
		if err := s.deductSingleCommission(ctx, &refund, &commission); err != nil {
			allSucceeded = false
			logger.Error("佣金回扣：单条佣金扣减失败",
				zap.Uint("refund_id", refundID),
				zap.Uint("commission_id", commission.ID),
				zap.Uint("shop_id", commission.ShopID),
				zap.Int64("amount", commission.Amount),
				zap.Error(err),
			)
			// 继续处理下一条，不中断
		}
	}
	if !allSucceeded {
		return
	}

	// 全部完成后标记退款单佣金已回扣
	if err := s.db.Model(&model.RefundRequest{}).Where("id = ?", refundID).Update("commission_deducted", true).Error; err != nil {
		logger.Error("佣金回扣：更新回扣标记失败", zap.Uint("refund_id", refundID), zap.Error(err))
	}
}

// deductSingleCommission 扣减单条佣金记录对应的代理钱包余额
// 使用乐观锁扣减（允许余额为负数），并创建交易流水
func (s *Service) deductSingleCommission(ctx context.Context, refund *model.RefundRequest, commission *model.CommissionRecord) error {
	return s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		var current model.CommissionRecord
		if err := tx.WithContext(ctx).Clauses(clause.Locking{Strength: "UPDATE"}).Where("id = ?", commission.ID).First(&current).Error; err != nil {
			return errors.Wrap(errors.CodeDatabaseError, err, "锁定退款佣金记录失败")
		}
		if current.Status == constants.CommissionStatusInvalid {
			return nil
		}
		if current.Status != constants.CommissionStatusReleased {
			return errors.New(errors.CodeInvalidStatus, "退款佣金状态不允许回扣")
		}
		var existingCount int64
		if err := tx.WithContext(ctx).Model(&model.AgentWalletTransaction{}).
			Where("reference_type = ? AND reference_id = ? AND transaction_type = ? AND status = ?",
				constants.ReferenceTypeCommission, current.ID, constants.AgentTransactionTypeCommissionDeduct, constants.TransactionStatusSuccess).
			Count(&existingCount).Error; err != nil {
			return errors.Wrap(errors.CodeDatabaseError, err, "复核退款佣金回扣流水失败")
		}
		if existingCount > 0 {
			if err := tx.WithContext(ctx).Model(&model.CommissionRecord{}).Where("id = ?", current.ID).
				Update("status", constants.CommissionStatusInvalid).Error; err != nil {
				return errors.Wrap(errors.CodeDatabaseError, err, "复核后失效退款佣金记录失败")
			}
			return nil
		}
		var wallet model.AgentWallet
		if err := tx.WithContext(ctx).Clauses(clause.Locking{Strength: "UPDATE"}).
			Where("shop_id = ? AND wallet_type = ?", current.ShopID, constants.AgentWalletTypeCommission).
			First(&wallet).Error; err != nil {
			return errors.Wrap(errors.CodeDatabaseError, err, "锁定退款佣金钱包失败")
		}
		result := tx.WithContext(ctx).Model(&model.AgentWallet{}).
			Where("id = ? AND version = ?", wallet.ID, wallet.Version).
			Updates(map[string]any{"balance": gorm.Expr("balance - ?", current.Amount), "version": gorm.Expr("version + 1")})
		if result.Error != nil {
			return errors.Wrap(errors.CodeDatabaseError, result.Error, "扣减退款佣金钱包失败")
		}
		if result.RowsAffected != 1 {
			return errors.New(errors.CodeConflict, "退款佣金钱包版本已变化")
		}
		refType, refID := constants.ReferenceTypeCommission, current.ID
		transaction := &model.AgentWalletTransaction{
			AgentWalletID: wallet.ID, ShopID: current.ShopID, UserID: refund.Creator,
			TransactionType: constants.AgentTransactionTypeCommissionDeduct, Amount: -current.Amount,
			BalanceBefore: wallet.Balance, BalanceAfter: wallet.Balance - current.Amount,
			Status: constants.TransactionStatusSuccess, ReferenceType: &refType, ReferenceID: &refID,
			Creator: refund.Creator, ShopIDTag: current.ShopID,
		}
		if err := tx.WithContext(ctx).Create(transaction).Error; err != nil {
			return errors.Wrap(errors.CodeDatabaseError, err, "创建退款佣金回扣流水失败")
		}
		updated := tx.WithContext(ctx).Model(&model.CommissionRecord{}).
			Where("id = ? AND status = ?", current.ID, constants.CommissionStatusReleased).
			Update("status", constants.CommissionStatusInvalid)
		if updated.Error != nil {
			return errors.Wrap(errors.CodeDatabaseError, updated.Error, "失效退款佣金记录失败")
		}
		if updated.RowsAffected != 1 {
			return errors.New(errors.CodeConflict, "退款佣金状态已变化")
		}
		return nil
	})
}

// handleRefundAssetProcessing 幂等处理退款后的资产状态。
// 包括退款套餐精准失效、尝试接续待生效主套餐和必要时停机；全部完成后才设置完成标记。
func (s *Service) handleRefundAssetProcessing(ctx context.Context, refundID uint) {
	logger := s.logger

	// 查询退款单
	var refund model.RefundRequest
	if err := s.db.Where("id = ?", refundID).First(&refund).Error; err != nil {
		logger.Error("退款资产处理：查询退款单失败", zap.Uint("refund_id", refundID), zap.Error(err))
		return
	}
	if refund.AssetReset {
		return
	}

	// 查询关联订单
	var order model.Order
	if err := s.db.Where("id = ?", refund.OrderID).First(&order).Error; err != nil {
		logger.Error("退款资产处理：查询订单失败", zap.Uint("refund_id", refundID), zap.Uint("order_id", refund.OrderID), zap.Error(err))
		return
	}

	// 确定资产类型和 ID
	var assetType string
	var assetID uint
	switch order.OrderType {
	case model.OrderTypeSingleCard:
		if order.IotCardID == nil {
			logger.Error("退款资产处理：单卡订单缺少 iot_card_id", zap.Uint("order_id", order.ID))
			return
		}
		assetType = "iot_card"
		assetID = *order.IotCardID
	case model.OrderTypeDevice:
		if order.DeviceID == nil {
			logger.Error("退款资产处理：设备订单缺少 device_id", zap.Uint("order_id", order.ID))
			return
		}
		assetType = "device"
		assetID = *order.DeviceID
	default:
		logger.Error("退款资产处理：未知订单类型", zap.String("order_type", order.OrderType))
		return
	}

	// 1. 按退款单精准失效套餐（仅处理本次退款订单关联套餐）
	if s.packageActivationService == nil {
		logger.Error("退款资产处理：套餐激活服务未注入",
			zap.Uint("refund_id", refund.ID),
			zap.Uint("order_id", order.ID))
		return
	}
	if err := s.packageActivationService.InvalidatePackagesForRefund(ctx, assetType, assetID, order.ID, refund.ID, refund.RefundNo, refund.PackageUsageID); err != nil {
		fields := []zap.Field{
			zap.String("asset_type", assetType),
			zap.Uint("asset_id", assetID),
			zap.Uint("order_id", order.ID),
			zap.Uint("refund_id", refund.ID),
			zap.String("refund_no", refund.RefundNo),
			zap.Error(err),
		}
		if refund.PackageUsageID != nil {
			fields = append(fields, zap.Uint("package_usage_id", *refund.PackageUsageID))
		}
		logger.Error("退款资产处理：退款套餐精准失效失败", fields...)
		return
	}

	// 2. 尝试按购买顺序接续待生效主套餐
	if _, err := s.packageActivationService.ActivateNextPendingMainPackage(ctx, assetType, assetID); err != nil {
		logger.Error("退款资产处理：接续激活待生效套餐失败",
			zap.String("asset_type", assetType),
			zap.Uint("asset_id", assetID),
			zap.Uint("refund_id", refund.ID),
			zap.Error(err))
		return
	}

	hasActiveMain, err := s.packageActivationService.HasActiveMainPackage(ctx, assetType, assetID)
	if err != nil {
		logger.Error("退款资产处理：查询生效主套餐失败",
			zap.String("asset_type", assetType),
			zap.Uint("asset_id", assetID),
			zap.Uint("refund_id", refund.ID),
			zap.Error(err))
		return
	}
	if !hasActiveMain {
		// 3. 无可用主套餐时才停机；退款不再重置世代或重建钱包。
		if !s.stopAsset(ctx, assetType, assetID) {
			return
		}
	}

	// 4. 标记退款后资产处理已完成
	if err := s.db.Model(&model.RefundRequest{}).Where("id = ?", refundID).Update("asset_reset", true).Error; err != nil {
		logger.Error("退款资产处理：更新处理标记失败", zap.Uint("refund_id", refundID), zap.Error(err))
	}
}

// stopAsset 根据资产类型执行停机操作
func (s *Service) stopAsset(ctx context.Context, assetType string, assetID uint) bool {
	logger := s.logger

	switch assetType {
	case "iot_card":
		// 单卡停机：需要先查卡获取 iccid
		card, err := s.iotCardStore.GetByID(ctx, assetID)
		if err != nil {
			logger.Error("退款资产处理：查询卡信息失败", zap.Uint("card_id", assetID), zap.Error(err))
			return false
		}
		if s.stopResumeService == nil {
			logger.Error("退款资产处理：单卡停机服务未注入", zap.Uint("card_id", assetID))
			return false
		}
		if err := s.stopResumeService.ManualStopCard(ctx, card.ICCID); err != nil {
			logger.Error("退款资产处理：单卡停机失败", zap.String("iccid", card.ICCID), zap.Error(err))
			return false
		}
	case "device":
		if s.stopResumeService == nil {
			logger.Error("退款资产处理：设备停机服务未注入", zap.Uint("device_id", assetID))
			return false
		}
		var cards []model.IotCard
		if err := s.db.WithContext(ctx).Model(&model.IotCard{}).
			Joins("JOIN tb_device_sim_binding binding ON binding.iot_card_id = tb_iot_card.id AND binding.deleted_at IS NULL").
			Where("binding.device_id = ? AND binding.bind_status = ?", assetID, constants.BindStatusBound).
			Find(&cards).Error; err != nil {
			logger.Error("退款资产处理：查询设备绑定卡失败", zap.Uint("device_id", assetID), zap.Error(err))
			return false
		}
		for _, card := range cards {
			if err := s.stopResumeService.ManualStopCard(ctx, card.ICCID); err != nil {
				logger.Error("退款资产处理：设备绑定卡停机失败",
					zap.Uint("device_id", assetID), zap.Uint("card_id", card.ID), zap.Error(err))
				return false
			}
		}
	}
	return true
}

// generateRefundNo 生成退款单号
// 格式：RF + 日期时间 + 6位随机数
func generateRefundNo() string {
	now := time.Now()
	randomNum := rand.Intn(1000000)
	return fmt.Sprintf("RF%s%06d", now.Format("20060102150405"), randomNum)
}

// validateRequestedRefundAmountByOrder 校验申请退款金额不得超过订单实收金额
func validateRequestedRefundAmountByOrder(requestedRefundAmount int64, order *model.Order) error {
	if order.ActualPaidAmount == nil {
		return errors.New(errors.CodeInternalError, "订单实收金额不能为空")
	}
	if requestedRefundAmount > *order.ActualPaidAmount {
		return errors.New(errors.CodeInvalidParam, "申请退款金额不能大于订单实收金额")
	}
	return nil
}

// validateApprovedRefundAmount 校验审批退款金额不能超过申请金额和订单实收金额。
func validateApprovedRefundAmount(approvedAmount int64, requestedRefundAmount int64, order *model.Order) error {
	if approvedAmount <= 0 {
		return errors.New(errors.CodeInvalidParam, "审批退款金额必须大于0")
	}
	if approvedAmount > requestedRefundAmount {
		return errors.New(errors.CodeInvalidParam, "审批退款金额不能大于申请退款金额")
	}
	return validateRequestedRefundAmountByOrder(approvedAmount, order)
}

func normalizeRefundVoucherKey(keys []string) (model.StringJSONBArray, error) {
	if len(keys) == 0 {
		return nil, errors.New(errors.CodeInvalidParam, "退款申请必须上传退款凭证")
	}
	if len(keys) > 5 {
		return nil, errors.New(errors.CodeInvalidParam, "退款凭证最多上传5个")
	}
	normalized := make(model.StringJSONBArray, 0, len(keys))
	for _, key := range keys {
		key = strings.TrimSpace(key)
		if key == "" {
			return nil, errors.New(errors.CodeInvalidParam, "退款凭证对象存储 Key 不能为空")
		}
		normalized = append(normalized, key)
	}
	return normalized, nil
}

// buildRefundResponse 将退款 Model 转换为 DTO 响应
func buildRefundResponse(r *model.RefundRequest) *dto.RefundResponse {
	assetType := ""
	if r.OrderType == model.OrderTypeSingleCard {
		assetType = "card"
	} else if r.OrderType == model.OrderTypeDevice {
		assetType = "device"
	}

	resp := &dto.RefundResponse{
		ID:                    r.ID,
		RefundNo:              r.RefundNo,
		OrderID:               r.OrderID,
		OrderNo:               r.OrderNo,
		AssetIdentifier:       r.AssetIdentifier,
		AssetType:             assetType,
		IotCardID:             r.IotCardID,
		DeviceID:              r.DeviceID,
		PackageUsageID:        r.PackageUsageID,
		ShopID:                r.ShopID,
		ShopName:              r.ShopName,
		ActualReceivedAmount:  r.ActualReceivedAmount,
		RequestedRefundAmount: r.RequestedRefundAmount,
		ApprovedRefundAmount:  r.ApprovedRefundAmount,
		RefundVoucherKey:      []string(r.RefundVoucherKey),
		RefundReason:          r.RefundReason,
		Status:                r.Status,
		StatusName:            constants.GetRefundStatusName(r.Status),
		ProcessorID:           r.ProcessorID,
		RejectReason:          r.RejectReason,
		Remark:                r.Remark,
		CommissionDeducted:    r.CommissionDeducted,
		AssetReset:            r.AssetReset,
		SubmitterID:           r.Creator,
		ApprovalInstanceID:    r.ApprovalInstanceID,
		Creator:               r.Creator,
		Updater:               r.Updater,
		CreatedAt:             r.CreatedAt.Format("2006-01-02 15:04:05"),
		UpdatedAt:             r.UpdatedAt.Format("2006-01-02 15:04:05"),
	}

	if r.ProcessedAt != nil {
		resp.ProcessedAt = r.ProcessedAt.Format("2006-01-02 15:04:05")
	}

	return resp
}

type approvalSummary struct {
	Provider string
	Status   int
}

func (s *Service) loadApprovalSummaries(ctx context.Context, refunds []*model.RefundRequest) (map[uint]approvalSummary, error) {
	ids := make([]uint, 0, len(refunds))
	seen := make(map[uint]struct{}, len(refunds))
	for _, refund := range refunds {
		if refund == nil || refund.ApprovalInstanceID == nil || *refund.ApprovalInstanceID == 0 {
			continue
		}
		id := *refund.ApprovalInstanceID
		if _, exists := seen[id]; exists {
			continue
		}
		seen[id] = struct{}{}
		ids = append(ids, id)
	}
	summaries := make(map[uint]approvalSummary, len(ids))
	if len(ids) == 0 {
		return summaries, nil
	}
	var instances []model.ApprovalInstance
	if err := s.db.WithContext(ctx).Select("id", "provider", "status").Where("id IN ?", ids).Find(&instances).Error; err != nil {
		return nil, errors.Wrap(errors.CodeDatabaseError, err, "批量查询退款审批状态失败")
	}
	for _, instance := range instances {
		summaries[instance.ID] = approvalSummary{Provider: instance.Provider, Status: instance.Status}
	}
	return summaries, nil
}

func applyApprovalSummary(response *dto.RefundResponse, summaries map[uint]approvalSummary, instanceID *uint) {
	if response == nil || instanceID == nil {
		return
	}
	summary, exists := summaries[*instanceID]
	if !exists {
		return
	}
	status := summary.Status
	response.ApprovalProvider = summary.Provider
	response.ApprovalStatus = &status
	response.ApprovalStatusName = constants.GetApprovalStatusName(status)
}

func refundSubmitterIDs(requests []*model.RefundRequest) []uint {
	ids := make([]uint, 0, len(requests))
	for _, request := range requests {
		if request != nil && request.Creator > 0 {
			ids = append(ids, request.Creator)
		}
	}
	return ids
}

func (s *Service) loadSubmitterNames(ctx context.Context, ids []uint) (map[uint]string, error) {
	accounts, err := postgres.NewAccountStore(s.db, nil).GetDisplayAccountsByIDs(ctx, ids)
	if err != nil {
		return nil, err
	}
	names := make(map[uint]string, len(accounts))
	for _, account := range accounts {
		names[account.ID] = account.Username
	}
	return names, nil
}

func (s *Service) loadSubmitterNameBestEffort(ctx context.Context, id uint) string {
	names, err := s.loadSubmitterNames(ctx, []uint{id})
	if err != nil {
		s.logger.Warn("查询退款提交人失败", zap.Uint("submitter_id", id), zap.Error(err))
		return ""
	}
	return names[id]
}

// buildRefundWalletTransactionAssetSnapshot 从订单中生成退款流水的资产快照。
func buildRefundWalletTransactionAssetSnapshot(order *model.Order) (string, uint, string) {
	if order == nil {
		return "", 0, ""
	}
	switch order.OrderType {
	case model.OrderTypeSingleCard:
		if order.IotCardID == nil {
			return constants.AssetTypeIotCard, 0, order.AssetIdentifier
		}
		return constants.AssetTypeIotCard, *order.IotCardID, order.AssetIdentifier
	case model.OrderTypeDevice:
		if order.DeviceID == nil {
			return constants.AssetTypeDevice, 0, order.AssetIdentifier
		}
		return constants.AssetTypeDevice, *order.DeviceID, order.AssetIdentifier
	default:
		return "", 0, order.AssetIdentifier
	}
}
