package snapshot

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"time"

	"bindbox-game/internal/pkg/pay"
	"bindbox-game/internal/repository/mysql"
	"bindbox-game/internal/repository/mysql/model"

	"gorm.io/gorm"
)

// RollbackResult 回滚结果
type RollbackResult struct {
	Success               bool     `json:"success"`
	RollbackLogID         int64    `json:"rollback_log_id"`
	PointsRestored        int64    `json:"points_restored"`
	CouponsRestored       int      `json:"coupons_restored"`
	ItemCardsRestored     int      `json:"item_cards_restored"`
	InventoryRevoked      int      `json:"inventory_revoked"`
	RefundAmount          int64    `json:"refund_amount"`
	SubsequentOrdersCount int      `json:"subsequent_orders_count"`
	SubsequentOrders      []string `json:"subsequent_orders"`
	RedemptionsReverted   int      `json:"redemptions_reverted"`
	ErrorMsg              string   `json:"error_msg,omitempty"`
}

// RollbackService 回滚服务
type RollbackService interface {
	// ExecuteRollback 执行时间点完整回滚
	ExecuteRollback(ctx context.Context, orderID int64, operatorID int64, operatorName string, reason string) (*RollbackResult, error)
}

type rollbackService struct {
	db       mysql.Repo
	snapshot Service
}

// NewRollbackService 创建回滚服务
func NewRollbackService(db mysql.Repo, snapshot Service) RollbackService {
	return &rollbackService{
		db:       db,
		snapshot: snapshot,
	}
}

// ExecuteRollback 执行时间点完整回滚
// 回滚到目标订单消费前的状态，撤销该时间点之后的所有变化
func (s *rollbackService) ExecuteRollback(ctx context.Context, orderID int64, operatorID int64, operatorName string, reason string) (*RollbackResult, error) {
	result := &RollbackResult{}

	// 1. 查询目标订单
	var order model.Orders
	if err := s.db.GetDbR().WithContext(ctx).Where("id = ?", orderID).First(&order).Error; err != nil {
		return nil, fmt.Errorf("订单不存在: %w", err)
	}

	// 2. 检查订单状态（只有已支付状态可以回滚）
	if order.Status != 2 {
		return nil, errors.New("订单状态不允许回滚，只有已支付订单可以回滚")
	}

	// 3. 检查是否已有回滚记录
	var existingRollback model.AuditRollbackLogs
	if err := s.db.GetDbR().WithContext(ctx).Where("order_id = ? AND status = 1", orderID).First(&existingRollback).Error; err == nil {
		return nil, errors.New("该订单已经执行过回滚")
	}

	// 4. 获取快照
	beforeSnap, afterSnap, err := s.snapshot.GetSnapshotsByOrderID(ctx, orderID)
	if err != nil {
		return nil, fmt.Errorf("获取快照失败: %w", err)
	}
	if beforeSnap == nil {
		return nil, errors.New("未找到消费前快照")
	}

	// 5. 解析目标状态
	var targetState UserStateSnapshot
	if err := json.Unmarshal([]byte(beforeSnap.SnapshotData), &targetState); err != nil {
		return nil, fmt.Errorf("解析快照失败: %w", err)
	}

	// 获取快照时间点
	snapshotTime := beforeSnap.CreatedAt

	// 6. 开始事务执行时间点完整回滚
	var rollbackLogID int64
	changesApplied := map[string]any{}
	var totalRefundAmount int64

	err = s.db.GetDbW().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		userID := order.UserID

		// ============ 步骤1: 查询并回滚该时间点之后的所有订单 ============
		var subsequentOrders []model.Orders
		tx.Where("user_id = ? AND id > ? AND status = 2", userID, orderID).Order("id DESC").Find(&subsequentOrders)

		subsequentOrderNos := []string{}
		for _, subOrder := range subsequentOrders {
			subsequentOrderNos = append(subsequentOrderNos, subOrder.OrderNo)

			// 作废后续订单产生的资产
			tx.Model(&model.UserInventory{}).Where("order_id = ? AND status IN (1,3)", subOrder.ID).Update("status", 2)

			// 恢复后续订单使用的优惠券
			if subOrder.CouponID > 0 {
				tx.Model(&model.UserCoupons{}).Where("id = ?", subOrder.CouponID).Updates(map[string]any{
					"status":        1,
					"used_order_id": 0,
					"used_at":       nil,
				})
			}

			// 恢复后续订单使用的道具卡
			if subOrder.ItemCardID > 0 {
				tx.Model(&model.UserItemCards{}).Where("id = ?", subOrder.ItemCardID).Updates(map[string]any{
					"status":           1,
					"used_draw_log_id": 0,
					"used_activity_id": 0,
					"used_issue_id":    0,
					"used_at":          nil,
				})
			}

			// 取消发货记录
			tx.Model(&model.ShippingRecords{}).Where("order_id = ?", subOrder.ID).Update("status", 5)

			// 更新订单状态为已回滚
			tx.Model(&subOrder).Update("status", 5)

			// 累计退款金额
			if subOrder.ActualAmount > 0 {
				totalRefundAmount += subOrder.ActualAmount
			}
		}
		result.SubsequentOrdersCount = len(subsequentOrders)
		result.SubsequentOrders = subsequentOrderNos
		changesApplied["subsequent_orders"] = subsequentOrderNos

		// ============ 步骤2: 撤销该时间点之后的积分兑换记录 ============
		// 查询资产兑换记录（status=3表示已兑换）
		var redeemedInventories []model.UserInventory
		tx.Where("user_id = ? AND status = 3 AND updated_at > ?", userID, snapshotTime).Find(&redeemedInventories)
		for _, inv := range redeemedInventories {
			// 恢复资产状态为持有
			tx.Model(&inv).Update("status", 1)
			result.RedemptionsReverted++
		}
		changesApplied["redemptions_reverted"] = result.RedemptionsReverted

		// ============ 步骤3: 撤销该时间点之后的优惠券兑换 ============
		// 查询该时间点之后获取的优惠券（积分兑换的）
		var redeemedCoupons []model.UserCoupons
		tx.Where("user_id = ? AND created_at > ? AND status = 1 AND source LIKE '%redeem%'", userID, snapshotTime).Find(&redeemedCoupons)
		for _, coupon := range redeemedCoupons {
			// 作废兑换的优惠券
			tx.Model(&coupon).Update("status", 3) // 过期/作废
		}
		changesApplied["coupons_voided"] = len(redeemedCoupons)

		// ============ 步骤4: 撤销该时间点之后的道具卡兑换 ============
		var redeemedCards []model.UserItemCards
		tx.Where("user_id = ? AND created_at > ? AND status = 1 AND source LIKE '%redeem%'", userID, snapshotTime).Find(&redeemedCards)
		for _, card := range redeemedCards {
			// 作废兑换的道具卡
			tx.Model(&card).Update("status", 3)
		}
		changesApplied["item_cards_voided"] = len(redeemedCards)

		// ============ 步骤5: 恢复目标订单的优惠券 ============
		if order.CouponID > 0 {
			for _, c := range targetState.Coupons {
				if c.UserCouponID == order.CouponID {
					updates := map[string]any{
						"status":         c.Status,
						"balance_amount": c.BalanceAmount,
						"used_order_id":  0,
						"used_at":        nil,
					}
					if err := tx.Model(&model.UserCoupons{}).Where("id = ?", order.CouponID).Updates(updates).Error; err != nil {
						return fmt.Errorf("恢复优惠券失败: %w", err)
					}
					result.CouponsRestored++
					break
				}
			}
			if result.CouponsRestored == 0 {
				updates := map[string]any{
					"status":        1,
					"used_order_id": 0,
					"used_at":       nil,
				}
				if err := tx.Model(&model.UserCoupons{}).Where("id = ?", order.CouponID).Updates(updates).Error; err == nil {
					result.CouponsRestored++
				}
			}
		}
		changesApplied["coupons_restored"] = result.CouponsRestored

		// ============ 步骤6: 恢复目标订单的道具卡 ============
		if order.ItemCardID > 0 {
			for _, c := range targetState.ItemCards {
				if c.UserItemCardID == order.ItemCardID {
					updates := map[string]any{
						"status":           c.Status,
						"used_draw_log_id": 0,
						"used_activity_id": 0,
						"used_issue_id":    0,
						"used_at":          nil,
					}
					if err := tx.Model(&model.UserItemCards{}).Where("id = ?", order.ItemCardID).Updates(updates).Error; err != nil {
						return fmt.Errorf("恢复道具卡失败: %w", err)
					}
					result.ItemCardsRestored++
					break
				}
			}
			if result.ItemCardsRestored == 0 {
				updates := map[string]any{
					"status":           1,
					"used_draw_log_id": 0,
					"used_activity_id": 0,
					"used_issue_id":    0,
					"used_at":          nil,
				}
				if err := tx.Model(&model.UserItemCards{}).Where("id = ?", order.ItemCardID).Updates(updates).Error; err == nil {
					result.ItemCardsRestored++
				}
			}
		}
		changesApplied["item_cards_restored"] = result.ItemCardsRestored

		// ============ 步骤7: 作废目标订单产生的资产 ============
		updateRes := tx.Model(&model.UserInventory{}).Where("order_id = ? AND status IN (1,3)", orderID).Update("status", 2)
		if updateRes.Error != nil {
			return fmt.Errorf("作废资产失败: %w", updateRes.Error)
		}
		result.InventoryRevoked = int(updateRes.RowsAffected)
		changesApplied["inventory_revoked"] = result.InventoryRevoked

		// ============ 步骤8: 取消目标订单发货记录 ============
		tx.Model(&model.ShippingRecords{}).Where("order_id = ?", orderID).Update("status", 5)

		// ============ 步骤9: 恢复积分到目标状态 ============
		if targetState.Points != nil {
			var currentPoints model.UserPoints
			if err := tx.Where("user_id = ?", userID).First(&currentPoints).Error; err == nil {
				pointsDiff := targetState.Points.Balance - currentPoints.Points
				if pointsDiff != 0 {
					if err := tx.Model(&currentPoints).Update("points", targetState.Points.Balance).Error; err != nil {
						return fmt.Errorf("恢复积分失败: %w", err)
					}
					// 记录积分流水
					ledger := &model.UserPointsLedger{
						UserID:   userID,
						Action:   "rollback_restore",
						Points:   pointsDiff,
						RefTable: "orders",
						RefID:    order.OrderNo,
						Remark:   fmt.Sprintf("时间点回滚恢复积分(含后续%d单): %s", len(subsequentOrders), reason),
					}
					if err := tx.Create(ledger).Error; err != nil {
						return fmt.Errorf("记录积分流水失败: %w", err)
					}
					result.PointsRestored = pointsDiff
					changesApplied["points_restored"] = pointsDiff
				}
			}
		}

		// ============ 步骤10: 更新目标订单状态为已回滚 ============
		if err := tx.Model(&order).Update("status", 5).Error; err != nil {
			return fmt.Errorf("更新订单状态失败: %w", err)
		}

		// 累计目标订单退款金额
		if order.ActualAmount > 0 {
			totalRefundAmount += order.ActualAmount
		}

		// ============ 步骤11: 记录回滚日志 ============
		changesJSON, _ := json.Marshal(changesApplied)
		var beforeSnapID, afterSnapID int64
		if beforeSnap != nil {
			beforeSnapID = beforeSnap.ID
		}
		if afterSnap != nil {
			afterSnapID = afterSnap.ID
		}
		rollbackLog := &model.AuditRollbackLogs{
			OrderID:          orderID,
			OrderNo:          order.OrderNo,
			UserID:           userID,
			OperatorID:       operatorID,
			OperatorName:     operatorName,
			RollbackType:     2, // 2=时间点完整回滚
			BeforeSnapshotID: afterSnapID,
			AfterSnapshotID:  beforeSnapID,
			ChangesApplied:   string(changesJSON),
			Reason:           reason,
			Status:           1,
		}
		if err := tx.Create(rollbackLog).Error; err != nil {
			return fmt.Errorf("记录回滚日志失败: %w", err)
		}
		rollbackLogID = rollbackLog.ID

		return nil
	})

	if err != nil {
		result.Success = false
		result.ErrorMsg = err.Error()
		return result, err
	}

	// ============ 步骤12: 调用微信退款（事务外执行） ============
	if totalRefundAmount > 0 {
		// 为每个需要退款的订单发起退款
		s.refundOrders(ctx, order.UserID, orderID, reason, result)
	}

	result.Success = true
	result.RollbackLogID = rollbackLogID

	return result, nil
}

// refundOrders 为回滚的订单发起退款
func (s *rollbackService) refundOrders(ctx context.Context, userID int64, startOrderID int64, reason string, result *RollbackResult) {
	// 查询需要退款的订单（状态已变为5=已回滚）
	var ordersToRefund []model.Orders
	s.db.GetDbR().WithContext(ctx).Where("user_id = ? AND id >= ? AND status = 5 AND actual_amount > 0", userID, startOrderID).Find(&ordersToRefund)

	wc, err := pay.NewWechatPayClient(ctx)
	if err != nil {
		return
	}

	for _, refundOrder := range ordersToRefund {
		refundNo := fmt.Sprintf("RB%s-%d", refundOrder.OrderNo, time.Now().Unix())
		_, status, err := wc.RefundOrder(ctx, refundOrder.OrderNo, refundNo, refundOrder.ActualAmount, refundOrder.ActualAmount, fmt.Sprintf("时间点回滚: %s", reason))
		if err == nil {
			refundRecord := &model.PaymentRefunds{
				OrderID:      refundOrder.ID,
				OrderNo:      refundOrder.OrderNo,
				RefundNo:     refundNo,
				Channel:      "wechat_jsapi",
				Status:       status,
				AmountRefund: refundOrder.ActualAmount,
				Reason:       fmt.Sprintf("时间点回滚: %s", reason),
			}
			s.db.GetDbW().WithContext(ctx).Create(refundRecord)
			// 加入格位清理逻辑
			if s.snapshot != nil {
				// 获取 activity 实例并清理，由于 rollbackService 持有 snapshot service，可间接获取或重新 new
				// 这里直接调用我们新增的逻辑
				_ = s.db.GetDbW().WithContext(ctx).Exec("DELETE FROM issue_position_claims WHERE order_id = ?", refundOrder.ID).Error
			}
			result.RefundAmount += refundOrder.ActualAmount
		}
	}
}
