package user

import (
	"context"
	"fmt"
	"time"

	"go.uber.org/zap"

	"bindbox-game/internal/repository/mysql/dao"
	"bindbox-game/internal/repository/mysql/model"
)

// GrantRewardRequest 奖励发放请求
type GrantRewardRequest struct {
	ProductID    int64  `json:"product_id" binding:"required"` // 商品ID
	Quantity     int    `json:"quantity" binding:"min=1"`      // 发放数量
	ActivityID   *int64 `json:"activity_id,omitempty"`         // 活动ID（可选）
	RewardID     *int64 `json:"reward_id,omitempty"`           // 奖励配置ID（可选）
	AddressID    *int64 `json:"address_id,omitempty"`          // 收货地址ID（可选，实物商品需要）
	Remark       string `json:"remark,omitempty"`              // 备注
	PointsAmount int64  `json:"points_amount,omitempty"`       // 消耗积分
}

// GrantRewardResponse 奖励发放响应
type GrantRewardResponse struct {
	Success      bool    `json:"success"`
	OrderID      int64   `json:"order_id"`
	InventoryIDs []int64 `json:"inventory_ids"`
	Message      string  `json:"message,omitempty"`
}

// GrantReward 给用户发放奖励（事务处理）
func (s *service) GrantReward(ctx context.Context, userID int64, req GrantRewardRequest) (*GrantRewardResponse, error) {
	logger := zap.L().With(zap.Int64("user_id", userID), zap.Int64("product_id", req.ProductID))

	var (
		orderID      int64
		inventoryIDs []int64
	)

	logger.Info("开始发放奖励", zap.Any("request", req))

	// 执行事务
	err := s.writeDB.Transaction(func(tx *dao.Query) error {
		logger.Info("开始事务处理")

		// 1. 检查奖励配置库存（如果提供了reward_id）
		if req.RewardID != nil {
			logger.Info("检查奖励配置", zap.Int64("reward_id", *req.RewardID))
			rewardSetting, err := tx.ActivityRewardSettings.WithContext(ctx).Where(
				tx.ActivityRewardSettings.ID.Eq(*req.RewardID),
			).First()
			if err != nil {
				logger.Error("查询奖励配置失败", zap.Error(err))
				return fmt.Errorf("查询奖励配置失败: %w", err)
			}
			if rewardSetting == nil {
				return fmt.Errorf("奖励配置不存在")
			}
			if rewardSetting.Quantity < int64(req.Quantity) {
				return fmt.Errorf("奖励库存不足，剩余%d，需要%d", rewardSetting.Quantity, req.Quantity)
			}

			// 扣减奖励库存
			_, err = tx.ActivityRewardSettings.WithContext(ctx).Where(
				tx.ActivityRewardSettings.ID.Eq(*req.RewardID),
			).Update(tx.ActivityRewardSettings.Quantity, rewardSetting.Quantity-int64(req.Quantity))
			if err != nil {
				logger.Error("扣减奖励库存失败", zap.Error(err))
				return fmt.Errorf("扣减奖励库存失败: %w", err)
			}
			logger.Info("奖励库存扣减成功")
		}

		// 2. 生成订单号
		orderNo := generateOrderNo()
		logger.Info("生成订单号", zap.String("order_no", orderNo))

		// 3. 创建系统发放订单
		now := time.Now()
		// 对于已支付的订单，设置取消时间为MySQL可接受的最小有效时间（1970-01-01）
		// 避免使用零时间导致MySQL的'0000-00-00'错误
		minValidTime := time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC)
		order := &model.Orders{
			OrderNo:        orderNo,
			UserID:         userID,
			SourceType:     3, // 系统发放
			Status:         2, // 已支付
			TotalAmount:    0,
			DiscountAmount: 0,
			PointsAmount:   req.PointsAmount,
			ActualAmount:   0,
			IsConsumed:     0,
			PaidAt:         now,          // 设置支付时间为当前时间
			CancelledAt:    minValidTime, // 设置取消时间为最小有效时间，避免MySQL错误
			Remark:         req.Remark,
			CreatedAt:      now,
			UpdatedAt:      now,
		}

		logger.Info("创建订单", zap.Any("order", order))
		err := tx.Orders.WithContext(ctx).Create(order)
		if err != nil {
			logger.Error("创建订单失败", zap.Error(err))
			return fmt.Errorf("创建订单失败: %w", err)
		}
		orderID = order.ID
		logger.Info("订单创建成功", zap.Int64("order_id", orderID))

		// 4. 查询商品信息快照
		product, err := tx.Products.WithContext(ctx).Where(tx.Products.ID.Eq(req.ProductID)).First()
		if err != nil {
			logger.Error("查询商品失败", zap.Error(err))
			return fmt.Errorf("查询商品失败: %w", err)
		}

		// 检查商品库存是否充足
		if product.Stock < int64(req.Quantity) {
			logger.Error("商品库存不足", zap.Int64("stock", product.Stock), zap.Int("need", req.Quantity))
			return fmt.Errorf("商品库存不足，请联系客服处理")
		}

		// 扣减商品库存
		_, err = tx.Products.WithContext(ctx).Where(
			tx.Products.ID.Eq(req.ProductID),
		).Update(tx.Products.Stock, product.Stock-int64(req.Quantity))
		if err != nil {
			logger.Error("扣减商品库存失败", zap.Error(err))
			return fmt.Errorf("扣减商品库存失败: %w", err)
		}
		logger.Info("商品库存扣减成功", zap.Int64("product_id", req.ProductID), zap.Int("quantity", req.Quantity))

		// 5. 创建订单项（按数量创建多个）
		for i := 0; i < req.Quantity; i++ {
			orderItem := &model.OrderItems{
				OrderID:       orderID,
				ProductID:     req.ProductID,
				Title:         product.Name,
				Quantity:      1,
				Price:         0, // 成交单价（分）
				TotalAmount:   0, // 行应付总额（分）
				ProductImages: product.ImagesJSON,
				Status:        1, // 行状态：1正常
			}

			err := tx.OrderItems.WithContext(ctx).Create(orderItem)
			if err != nil {
				logger.Error("创建订单项失败", zap.Error(err))
				return fmt.Errorf("创建订单项失败: %w", err)
			}

			// 6. 创建用户资产记录
			inventory := &model.UserInventory{
				UserID:    userID,
				ProductID: req.ProductID,
				OrderID:   orderID,
				ActivityID: func() int64 {
					if req.ActivityID != nil {
						return *req.ActivityID
					} else {
						return 0
					}
				}(),
				RewardID: func() int64 {
					if req.RewardID != nil {
						return *req.RewardID
					} else {
						return 0
					}
				}(),
				Status: 1, // 持有状态
				Remark: func() string {
					if req.Remark != "" {
						return req.Remark
					}
					return product.Name
				}(),
			}

			err = tx.UserInventory.WithContext(ctx).Create(inventory)
			if err != nil {
				logger.Error("创建用户资产失败", zap.Error(err))
				return fmt.Errorf("创建用户资产失败: %w", err)
			}

			inventoryIDs = append(inventoryIDs, inventory.ID)

			// 7. 如果需要发货（提供了地址ID），创建发货记录
			if req.AddressID != nil {
				// 设置发货和签收时间为MySQL可接受的最小有效时间（1970-01-01）
				// 避免使用零时间导致MySQL的'0000-00-00'错误
				minValidTime := time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC)
				shippingRecord := &model.ShippingRecords{
					UserID:      userID,
					OrderID:     orderID,
					OrderItemID: orderItem.ID,
					InventoryID: inventory.ID,
					ProductID:   req.ProductID,
					AddressID:   *req.AddressID,
					Quantity:    1,
					Status:      1,            // 待发货
					ShippedAt:   minValidTime, // 设置发货时间为最小有效时间
					ReceivedAt:  minValidTime, // 设置签收时间为最小有效时间
					CreatedAt:   time.Now(),
					UpdatedAt:   time.Now(),
				}

				err := tx.ShippingRecords.WithContext(ctx).Create(shippingRecord)
				if err != nil {
					logger.Error("创建发货记录失败", zap.Error(err))
					return fmt.Errorf("创建发货记录失败: %w", err)
				}
			}
		}

		return nil
	})

	if err != nil {
		return &GrantRewardResponse{
			Success: false,
			Message: err.Error(),
		}, err
	}

	logger.Info("奖励发放成功",
		zap.Int64("order_id", orderID),
		zap.Int64s("inventory_ids", inventoryIDs),
		zap.Int("quantity", req.Quantity),
	)

	return &GrantRewardResponse{
		Success:      true,
		OrderID:      orderID,
		InventoryIDs: inventoryIDs,
		Message:      fmt.Sprintf("成功发放%d个奖励", req.Quantity),
	}, nil
}

// generateOrderNo 生成订单号
func generateOrderNo() string {
	// 使用当前时间戳 + 随机数生成订单号
	// 格式：RG + 年月日时分秒 + 6位随机数
	return fmt.Sprintf("RG%s%d",
		time.Now().Format("20060102150405"),
		time.Now().UnixNano()%1000000,
	)
}

// GrantRewardToOrderRequest 在现有订单上发放奖励的请求参数
type GrantRewardToOrderRequest struct {
	OrderID    int64  `json:"order_id" binding:"required"`   // 现有订单ID
	ProductID  int64  `json:"product_id" binding:"required"` // 商品ID
	Quantity   int    `json:"quantity" binding:"min=1"`      // 发放数量
	ActivityID *int64 `json:"activity_id,omitempty"`         // 活动ID（可选）
	RewardID   *int64 `json:"reward_id,omitempty"`           // 奖励配置ID（可选）
	Remark     string `json:"remark,omitempty"`              // 备注
}

// GrantRewardToOrderResponse 在现有订单上发放奖励的响应
type GrantRewardToOrderResponse struct {
	Success      bool    `json:"success"`
	OrderID      int64   `json:"order_id"`
	InventoryIDs []int64 `json:"inventory_ids"`
	Message      string  `json:"message,omitempty"`
}

// GrantRewardToOrder 在现有订单上发放奖励（不创建新订单）
// 用于抽奖中奖场景：用户支付的抽奖订单中奖后，直接在该订单上添加中奖商品
func (s *service) GrantRewardToOrder(ctx context.Context, userID int64, req GrantRewardToOrderRequest) (*GrantRewardToOrderResponse, error) {
	logger := zap.L().With(zap.Int64("user_id", userID), zap.Int64("order_id", req.OrderID), zap.Int64("product_id", req.ProductID))

	var inventoryIDs []int64

	logger.Info("开始在现有订单上发放奖励", zap.Any("request", req))

	// 执行事务
	err := s.writeDB.Transaction(func(tx *dao.Query) error {
		logger.Info("开始事务处理")

		// 1. 验证订单存在且属于该用户
		order, err := tx.Orders.WithContext(ctx).Where(
			tx.Orders.ID.Eq(req.OrderID),
			tx.Orders.UserID.Eq(userID),
		).First()
		if err != nil || order == nil {
			logger.Error("订单不存在或不属于该用户", zap.Error(err))
			return fmt.Errorf("订单不存在或不属于该用户")
		}

		// 【关键校验】验证订单状态必须为已支付(2)，未支付的订单不能发奖
		if order.Status != 2 {
			logger.Error("订单状态不正确，无法发奖", zap.Int32("status", order.Status))
			return fmt.Errorf("订单未支付或状态异常(status=%d)，无法发奖", order.Status)
		}

		// 2. 检查奖励配置库存（如果提供了reward_id）
		if req.RewardID != nil {
			logger.Info("检查奖励配置", zap.Int64("reward_id", *req.RewardID))

			// 【使用乐观锁扣减库存】直接用 Quantity > 0 作为更新条件，避免竞态
			result, err := tx.ActivityRewardSettings.WithContext(ctx).Where(
				tx.ActivityRewardSettings.ID.Eq(*req.RewardID),
				tx.ActivityRewardSettings.Quantity.Gt(0), // 乐观锁：只有库存>0才能扣减
			).UpdateSimple(tx.ActivityRewardSettings.Quantity.Add(-int64(req.Quantity)))
			if err != nil {
				logger.Error("扣减奖励库存失败", zap.Error(err))
				return fmt.Errorf("扣减奖励库存失败: %w", err)
			}
			if result.RowsAffected == 0 {
				logger.Error("奖励库存不足或不存在")
				return fmt.Errorf("奖励库存不足或不存在")
			}
			logger.Info("奖励库存扣减成功（乐观锁）")
		}

		// 3. 查询商品信息快照
		product, err := tx.Products.WithContext(ctx).Where(tx.Products.ID.Eq(req.ProductID)).First()
		if err != nil {
			logger.Error("查询商品失败", zap.Error(err))
			return fmt.Errorf("查询商品失败: %w", err)
		}

		// 4. 在现有订单上添加订单项和用户资产（按数量创建多个）
		for i := 0; i < req.Quantity; i++ {
			orderItem := &model.OrderItems{
				OrderID:       req.OrderID,
				ProductID:     req.ProductID,
				Title:         product.Name,
				Quantity:      1,
				Price:         0, // 中奖商品，无需额外付费
				TotalAmount:   0,
				ProductImages: product.ImagesJSON,
				Status:        1, // 行状态：1正常
			}

			err := tx.OrderItems.WithContext(ctx).Create(orderItem)
			if err != nil {
				logger.Error("创建订单项失败", zap.Error(err))
				return fmt.Errorf("创建订单项失败: %w", err)
			}

			// 5. 创建用户资产记录（关联到原订单）
			inventory := &model.UserInventory{
				UserID:    userID,
				ProductID: req.ProductID,
				OrderID:   req.OrderID, // 关联到原抽奖订单
				ActivityID: func() int64 {
					if req.ActivityID != nil {
						return *req.ActivityID
					}
					return 0
				}(),
				RewardID: func() int64 {
					if req.RewardID != nil {
						return *req.RewardID
					}
					return 0
				}(),
				Status: 1, // 持有状态
				Remark: func() string {
					if req.Remark != "" {
						return req.Remark
					}
					return product.Name
				}(),
			}

			err = tx.UserInventory.WithContext(ctx).Create(inventory)
			if err != nil {
				logger.Error("创建用户资产失败", zap.Error(err))
				return fmt.Errorf("创建用户资产失败: %w", err)
			}

			inventoryIDs = append(inventoryIDs, inventory.ID)
		}

		return nil
	})

	if err != nil {
		return &GrantRewardToOrderResponse{
			Success: false,
			Message: err.Error(),
		}, err
	}

	logger.Info("在现有订单上发放奖励成功",
		zap.Int64("order_id", req.OrderID),
		zap.Int64s("inventory_ids", inventoryIDs),
		zap.Int("quantity", req.Quantity),
	)

	return &GrantRewardToOrderResponse{
		Success:      true,
		OrderID:      req.OrderID,
		InventoryIDs: inventoryIDs,
		Message:      fmt.Sprintf("成功发放%d个奖励到订单", req.Quantity),
	}, nil
}
