package camel_oil

import (
	"context"
	"fmt"
	"sync"
	"sync/atomic"

	"github.com/gogf/gf/v2/os/glog"

	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/gogf/gf/v2/os/gmlock"
	"github.com/gogf/gf/v2/os/gtime"
	"github.com/shopspring/decimal"

	"kami/internal/consts"
	"kami/internal/dao"
	"kami/internal/model"
	"kami/internal/model/do"
	"kami/internal/model/entity"
	"kami/utility/config"
	"kami/utility/integration/camel_oil_api"
)

// ====================================================================================
// 预拉取订单管理相关方法
// ====================================================================================

// GetPrefetchOrderCapacity 获取当前可用订单容量
func (s *sCamelOil) GetPrefetchOrderCapacity(ctx context.Context, amount float64) (capacity int, err error) {
	m := dao.V1CamelOilPrefetchOrder.Ctx(ctx).DB(config.GetDatabaseV1())

	count, err := m.Where(dao.V1CamelOilPrefetchOrder.Columns().Status, consts.CamelOilPrefetchOrderStatusPending).
		WhereGT(dao.V1CamelOilPrefetchOrder.Columns().ExpireAt, gtime.Now()).
		Where(dao.V1CamelOilPrefetchOrder.Columns().Amount, amount).
		Count()
	if err != nil {
		return 0, gerror.Wrap(err, "查询预拉取订单库存失败")
	}

	return count, nil
}

// PrefetchOrderConcurrently 使用所有可用账号并发拉取订单，直到获取到可用订单为止
func (s *sCamelOil) PrefetchOrderConcurrently(ctx context.Context, amount float64) (result *model.PrefetchOrderResult, err error) {
	s.SavePrefetchOrderLog(ctx, fmt.Sprintf("开始并发拉取预拉取订单 - 目标金额: %.2f", amount))

	// 获取设置
	settings, err := s.GetCamelOilSettings(ctx)
	if err != nil {
		glog.Errorf(ctx, "获取骆驼模块设置失败: %v", err)
		s.SavePrefetchOrderLog(ctx, fmt.Sprintf("获取骆驼模块设置失败 - 错误: %s", err.Error()))
		return nil, err
	}
	s.SavePrefetchOrderLog(ctx, fmt.Sprintf("获取骆驼模块设置成功 - 并发账号数限制: %d", settings.PrefetchConcurrencyAccounts))

	s.SavePrefetchOrderLog(ctx, "开始查询可用的在线账号")

	m := dao.V1CamelOilAccount.Ctx(ctx).DB(config.GetDatabaseV1())
	var onlineAccounts []*entity.V1CamelOilAccount
	err = m.Where(dao.V1CamelOilAccount.Columns().Status, consts.CamelOilAccountStatusOnline).
		WhereLT(dao.V1CamelOilAccount.Columns().DailyOrderCount, consts.CamelOilAccountDailyOrderLimit).
		OrderRandom().
		Scan(&onlineAccounts)
	if err != nil {
		glog.Errorf(ctx, "查询在线账号失败: %v", err)
		s.SavePrefetchOrderLog(ctx, fmt.Sprintf("查询在线账号失败 - 错误: %s", err.Error()))
		return nil, gerror.Wrap(err, "查询在线账号失败")
	}

	s.SavePrefetchOrderLog(ctx, fmt.Sprintf("查询到 %d 个在线可用账号", len(onlineAccounts)))
	if len(onlineAccounts) == 0 {
		glog.Warningf(ctx, "暂无在线账号可用")
		s.SavePrefetchOrderLog(ctx, "并发拉取订单失败 - 原因: 暂无在线账号可用")
		return nil, gerror.New("暂无在线账号可用")
	}

	// 打印可用账号信息（脱敏处理）
	for i, account := range onlineAccounts {
		glog.Debugf(ctx, "可用账号 %d: ID=%d, 昵称=%s, 今日下单数=%d, 手机号=%s***%s",
			i+1, account.Id, account.AccountName, account.DailyOrderCount,
			account.Phone[:3], account.Phone[len(account.Phone)-4:])
		s.SavePrefetchOrderLog(ctx, fmt.Sprintf("可用账号 %d: ID=%d, 昵称=%s, 今日下单数=%d, 手机号=%s***%s",
			i+1, account.Id, account.AccountName, account.DailyOrderCount,
			account.Phone[:3], account.Phone[len(account.Phone)-4:]))
	}

	// 2. 使用设置中的并发数量限制并发
	concurrencyLimit := min(len(onlineAccounts), settings.PrefetchConcurrencyAccounts)
	s.SavePrefetchOrderLog(ctx, fmt.Sprintf("设置并发限制 - 并发数: %d, 在线账号数: %d, 系统并发限制: %d",
		concurrencyLimit, len(onlineAccounts), settings.PrefetchConcurrencyAccounts))

	var (
		resultChan = make(chan *model.PrefetchOrderResult, 1)
		errorChan  = make(chan error, len(onlineAccounts))
		semaphore  = make(chan struct{}, concurrencyLimit) // 信信道控制并发量
		wg         sync.WaitGroup
		mu         sync.Mutex
		found      = false
	)

	// 3. 每个账号起一个协程尝试拉取，控制并发量
	for _, account := range onlineAccounts {
		acc := account // 避免闭包陷阱
		wg.Go(func() {
			// 获取信信道控制
			semaphore <- struct{}{}
			defer func() { <-semaphore }()

			// 检查是否已经找到订单
			mu.Lock()
			if found {
				mu.Unlock()
				return
			}
			mu.Unlock()

			// 拉取订单
			platformOrderId, payUrl, err2 := camel_oil_api.NewClient(ctx).CreateCamelOilOrder(ctx, acc.Phone, acc.Token, amount)
			if err2 != nil {
				if err2.Error() == "auth_error" {
					_ = s.UpdateAccountStatus(ctx, acc.Id, consts.CamelOilAccountStatusInvalid, consts.CamelOilAccountChangeTypeInvalidate, "账号token失效")
				}
				errorChan <- err2
				return
			}

			// 成功拉取订单，返回结果
			mu.Lock()
			if !found {
				found = true
				resultChan <- &model.PrefetchOrderResult{
					PlatformOrderNo: platformOrderId,
					AlipayUrl:       payUrl,
					AccountId:       acc.Id,
					AccountName:     acc.AccountName,
					Amount:          amount,
				}
			}
			mu.Unlock()
		})
	}

	// 4. 等待所有协程完成並关闭Channel
	go func() {
		wg.Wait()
		close(resultChan)
		close(errorChan)
	}()

	// 5. 等待第一个成功的订单
	if res := <-resultChan; res != nil {
		return res, nil
	}

	// 6. 所有账号都拉取失败
	var lastErr error
	for err = range errorChan {
		lastErr = err
	}

	return nil, gerror.Wrap(lastErr, "所有账号拉取订单失败")
}

// PrefetchOrder 拉取单个订单（用于单个账号）
func (s *sCamelOil) PrefetchOrder(ctx context.Context, account *entity.V1CamelOilAccount, amount float64) (prefetchId int64, err error) {

	// 记录开始拉取订单的日志
	s.SavePrefetchOrderLog(ctx, fmt.Sprintf("开始预拉取订单 - 账号ID: %d, 账号名称: %s, 金额: %.2f, 手机号: %s***%s",
		account.Id, account.AccountName, amount,
		account.Phone[:3], account.Phone[len(account.Phone)-4:]))

	// 1. 从骆驼平台拉取订单
	s.SavePrefetchOrderLog(ctx, fmt.Sprintf("调用骆驼平台API - 账号ID: %d, 账号名称: %s, 金额: %.2f, 操作: CreateCamelOilOrder",
		account.Id, account.AccountName, amount))

	platformOrderId, payUrl, err := camel_oil_api.NewClient(ctx).CreateCamelOilOrder(ctx, account.Phone, account.Token, amount)

	if err != nil {
		s.SavePrefetchOrderLog(ctx, fmt.Sprintf("骆驼平台API调用失败 - 账号ID: %d, 账号名称: %s, 错误: %s",
			account.Id, account.AccountName, err.Error()))

		if err.Error() == "auth_error" {
			s.SavePrefetchOrderLog(ctx, fmt.Sprintf("账号token失效 - 账号ID: %d, 账号名称: %s, 处理: 更新为无效状态",
				account.Id, account.AccountName))
			_ = s.UpdateAccountStatus(ctx, account.Id, consts.CamelOilAccountStatusInvalid, consts.CamelOilAccountChangeTypeInvalidate, "账号token失效")
			return 0, gerror.Wrap(err, "账号token失效，拉取订单失败")
		}
		return 0, gerror.Wrap(err, "从骆驼平台拉取订单失败")
	}

	s.SavePrefetchOrderLog(ctx, fmt.Sprintf("骆驼平台API调用成功 - 账号ID: %d, 账号名称: %s, 平台订单号: %s, 支付链接: %t",
		account.Id, account.AccountName, platformOrderId, payUrl != ""))

	// 3. 保存预拉取订单记录
	s.SavePrefetchOrderLog(ctx, fmt.Sprintf("保存预拉取订单到数据库 - 账号ID: %d, 账号名称: %s, 平台订单号: %s, 状态: 待匹配",
		account.Id, account.AccountName, platformOrderId))

	result, err := dao.V1CamelOilPrefetchOrder.Ctx(ctx).DB(config.GetDatabaseV1()).Insert(&do.V1CamelOilPrefetchOrder{
		AccountId:       account.Id,
		AccountName:     account.AccountName,
		Amount:          decimal.NewFromFloat(amount),
		PlatformOrderNo: platformOrderId,
		AlipayUrl:       payUrl,
		Status:          consts.CamelOilPrefetchOrderStatusPending, // 待匹配
		ExpireAt:        gtime.Now().Add(consts.CamelOilPrefetchOrderExpireDuration),
	})

	if err != nil {
		glog.Errorf(ctx, "保存预拉取订单到数据库失败，账号ID=%d, 错误: %v", account.Id, err)
		s.SavePrefetchOrderLog(ctx, fmt.Sprintf("保存预拉取订单失败 - 账号ID: %d, 账号名称: %s, 错误: %s",
			account.Id, account.AccountName, err.Error()))
		return 0, gerror.Wrap(err, "保存预拉取订单失败")
	}

	prefetchId, err = result.LastInsertId()
	if err != nil {
		glog.Errorf(ctx, "获取预拉取订单ID失败，账号ID=%d, 错误: %v", account.Id, err)
		s.SavePrefetchOrderLog(ctx, fmt.Sprintf("获取预拉取订单ID失败 - 账号ID: %d, 账号名称: %s, 错误: %s",
			account.Id, account.AccountName, err.Error()))
		return 0, gerror.Wrap(err, "获取预拉取订单ID失败")
	}

	glog.Infof(ctx, "预拉取订单保存成功，ID=%d, 账号ID=%d", prefetchId, account.Id)
	s.SavePrefetchOrderLog(ctx, fmt.Sprintf("预拉取订单保存成功 - 预拉取ID: %d, 账号ID: %d, 账号名称: %s, 平台订单号: %s",
		prefetchId, account.Id, account.AccountName, platformOrderId))

	// 4. 记录预拉取订单历史
	glog.Infof(ctx, "开始记录预拉取订单历史...")
	_ = s.recordPrefetchOrderHistory(ctx, prefetchId, consts.CamelOilPrefetchOrderChangeTypeFetch,
		account.Id, account.AccountName, fmt.Sprintf("从骆驼平台拉取订单，平台订单号: %s", platformOrderId), nil)

	s.SavePrefetchOrderLog(ctx, fmt.Sprintf("预拉取订单历史记录完成 - 预拉取ID: %d, 操作类型: 拉取订单, 账号ID: %d",
		prefetchId, account.Id))

	glog.Infof(ctx, "预拉取订单创建成功，ID=%d, 账号=%s, 平台订单号=%s", prefetchId, account.Phone, platformOrderId)
	s.SavePrefetchOrderLog(ctx, fmt.Sprintf("预拉取订单流程完成 - 预拉取ID: %d, 账号ID: %d, 账号名称: %s, 平台订单号: %s, 手机号: %s***%s, 金额: %.2f",
		prefetchId, account.Id, account.AccountName, platformOrderId,
		account.Phone[:3], account.Phone[len(account.Phone)-4:], amount))

	return prefetchId, nil
}

// ConcurrentPrefetchOrders 使用多个账号并发拉取订单
func (s *sCamelOil) ConcurrentPrefetchOrders(ctx context.Context, amount float64, targetCount int) (successCount int, err error) {
	glog.Infof(ctx, "开始批量并发拉取预拉取订单，目标数量: %d, 金额: %.2f", targetCount, amount)
	s.SavePrefetchOrderLog(ctx, fmt.Sprintf("开始批量并发拉取预拉取订单 - 目标数量: %d, 金额: %.2f", targetCount, amount))

	// 获取设置
	settings, err := s.GetCamelOilSettings(ctx)
	if err != nil {
		glog.Errorf(ctx, "获取骆驼模块设置失败: %v", err)
		s.SavePrefetchOrderLog(ctx, fmt.Sprintf("批量拉取获取设置失败 - 目标数量: %d, 金额: %.2f, 错误: %s", targetCount, amount, err.Error()))
		return 0, err
	}

	glog.Infof(ctx, "获取骆驼模块设置成功，并发限制: %d", settings.PrefetchConcurrencyAccounts)
	s.SavePrefetchOrderLog(ctx, fmt.Sprintf("批量拉取获取设置成功 - 目标数量: %d, 金额: %.2f, 并发限制: %d", targetCount, amount, settings.PrefetchConcurrencyAccounts))

	// 1. 获取所有在线账号
	glog.Infof(ctx, "开始查询在线账号用于批量拉取...")
	s.SavePrefetchOrderLog(ctx, fmt.Sprintf("批量拉取查询在线账号 - 目标数量: %d, 金额: %.2f", targetCount, amount))

	m := dao.V1CamelOilAccount.Ctx(ctx).DB(config.GetDatabaseV1())
	var onlineAccounts []*entity.V1CamelOilAccount

	err = m.Where(dao.V1CamelOilAccount.Columns().Status, consts.CamelOilAccountStatusOnline).
		WhereLT(dao.V1CamelOilAccount.Columns().DailyOrderCount, consts.CamelOilAccountDailyOrderLimit).
		Scan(&onlineAccounts)
	if err != nil {
		glog.Errorf(ctx, "查询在线账号失败: %v", err)
		s.SavePrefetchOrderLog(ctx, fmt.Sprintf("批量拉取查询在线账号失败 - 目标数量: %d, 金额: %.2f, 错误: %s", targetCount, amount, err.Error()))
		return 0, gerror.Wrap(err, "查询在线账号失败")
	}

	glog.Infof(ctx, "查询到 %d 个在线账号用于批量拉取", len(onlineAccounts))
	s.SavePrefetchOrderLog(ctx, fmt.Sprintf("批量拉取查询到在线账号 - 目标数量: %d, 金额: %.2f, 可用账号数: %d", targetCount, amount, len(onlineAccounts)))

	if len(onlineAccounts) == 0 {
		glog.Warningf(ctx, "暂无在线账号可用于拉取订单")
		s.SavePrefetchOrderLog(ctx, fmt.Sprintf("批量拉取失败 - 目标数量: %d, 金额: %.2f, 原因: 暂无在线账号可用", targetCount, amount))
		return 0, gerror.New("暂无在线账号可用于拉取订单")
	}

	// 2. 使用设置中的并发数量限制并发
	concurrencyLimit := settings.PrefetchConcurrencyAccounts
	glog.Infof(ctx, "设置并发限制: %d", concurrencyLimit)
	s.SavePrefetchOrderLog(ctx, fmt.Sprintf("批量拉取设置并发控制 - 目标数量: %d, 金额: %.2f, 并发数: %d, 账号数: %d", targetCount, amount, concurrencyLimit, len(onlineAccounts)))

	var (
		wg             sync.WaitGroup
		semaphore      = make(chan struct{}, concurrencyLimit)
		successCounter int32
		failureCounter int32
		accountIndex   int32
	)

	targetRemaining := int32(targetCount)
	accountIndex = 0
	const maxRetries = 10 // 失败计数是成功计数的10倍，每个循环重试10次

	glog.Infof(ctx, "开始批量并发拉取循环，剩余目标: %d, 最大重试: %d", targetRemaining, maxRetries)
	s.SavePrefetchOrderLog(ctx, fmt.Sprintf("批量拉取开始循环 - 目标数量: %d, 金额: %.2f, 剩余目标: %d, 最大重试: %d", targetCount, amount, targetRemaining, maxRetries))

	loopCount := 0
	for {
		loopCount++
		// 原子方式检查并减少目标数量
		current := atomic.LoadInt32(&targetRemaining)
		if current <= 0 {
			glog.Infof(ctx, "批量拉取目标已完成，总循环次数: %d", loopCount)
			s.SavePrefetchOrderLog(ctx, fmt.Sprintf("批量拉取目标已完成 - 目标数量: %d, 金额: %.2f, 总循环次数: %d", targetCount, amount, loopCount))
			break
		}

		account := onlineAccounts[int(atomic.LoadInt32(&accountIndex))%len(onlineAccounts)]
		atomic.AddInt32(&accountIndex, 1)

		glog.Debugf(ctx, "第%d次循环，使用账号: %s (ID: %d), 剩余目标: %d", loopCount, account.AccountName, account.Id, current)
		if loopCount <= 5 { // 只记录前5次的详细日志，避免日志过多
			s.SavePrefetchOrderLog(ctx, fmt.Sprintf("批量拉取循环 %d - 账号: %s (ID: %d), 剩余目标: %d, 手机号: %s***%s",
				loopCount, account.AccountName, account.Id, current,
				account.Phone[:3], account.Phone[len(account.Phone)-4:]))
		}

		semaphore <- struct{}{} // 获取信号量

		wg.Go(func() {
			defer wg.Done()
			defer func() { <-semaphore }() // 释放信号量

			// 为了提高效率，每个账号可以拉取多单，每单重试10次
			for i := 0; i < 2; i++ {
				// 原子方式检查是否还需要继续拉取
				if atomic.LoadInt32(&targetRemaining) <= 0 {
					break
				}

				// 重试机制：每个订单最多重试10次
				var success bool
				retryCount := 0
				for retry := 0; retry < maxRetries; retry++ {
					retryCount++
					_, err = s.PrefetchOrder(ctx, account, amount)
					if err == nil {
						success = true
						if retry > 0 {
							glog.Infof(ctx, "账号 %s 拉取订单成功，重试了 %d 次", account.AccountName, retry)
							s.SavePrefetchOrderLog(ctx, fmt.Sprintf("账号拉取订单重试成功 - 账号: %s (ID: %d), 金额: %.2f, 重试次数: %d, 手机号: %s***%s",
								account.AccountName, account.Id, amount, retry,
								account.Phone[:3], account.Phone[len(account.Phone)-4:]))
						}
						break
					}
					atomic.AddInt32(&failureCounter, 1)

					if retry < maxRetries-1 {
						glog.Debugf(ctx, "账号%s拉取订单失败(第%d次重试): %v", account.Phone, retry+1, err)
					} else {
						glog.Warningf(ctx, "账号%s拉取订单失败(已重试%d次): %v", account.Phone, maxRetries, err)
						s.SavePrefetchOrderLog(ctx, fmt.Sprintf("账号拉取订单最终失败 - 账号: %s (ID: %d), 金额: %.2f, 重试次数: %d, 错误: %v, 手机号: %s***%s",
							account.AccountName, account.Id, amount, maxRetries, err.Error(),
							account.Phone[:3], account.Phone[len(account.Phone)-4:]))
					}
				}

				if success {
					atomic.AddInt32(&successCounter, 1)
				}
				atomic.AddInt32(&targetRemaining, -1)
			}
		})
	}

	glog.Infof(ctx, "等待所有批量拉取协程完成...")
	s.SavePrefetchOrderLog(ctx, fmt.Sprintf("批量拉取等待协程完成 - 目标数量: %d, 金额: %.2f, 总循环次数: %d", targetCount, amount, loopCount))

	wg.Wait()
	close(semaphore) // 关闭信号量通道，允许 GC 回收

	successCount = int(atomic.LoadInt32(&successCounter))
	failureCount := int(atomic.LoadInt32(&failureCounter))

	glog.Infof(ctx, "批量并发拉取订单完成，目标: %d, 成功: %d, 失败: %d, 总重试次数: %d", targetCount, successCount, failureCount, loopCount)
	s.SavePrefetchOrderLog(ctx, fmt.Sprintf("批量并发拉取订单完成 - 目标数量: %d, 金额: %.2f, 成功: %d, 失败: %d, 总循环次数: %d",
		targetCount, amount, successCount, failureCount, loopCount))

	// 记录成功率
	if successCount > 0 {
		successRate := float64(successCount) / float64(targetCount) * 100
		glog.Infof(ctx, "批量拉取成功率: %.2f%% (%d/%d)", successRate, successCount, targetCount)
		s.SavePrefetchOrderLog(ctx, fmt.Sprintf("批量拉取成功率 - 金额: %.2f, 成功率: %.2f%% (%d/%d)", amount, successRate, successCount, targetCount))
	} else {
		glog.Warningf(ctx, "批量拉取完全失败，成功率为 0%%")
		s.SavePrefetchOrderLog(ctx, fmt.Sprintf("批量拉取完全失败 - 金额: %.2f, 目标数量: %d, 失败数: %d, 成功率: 0%%", amount, targetCount, failureCount))
	}

	return successCount, nil
}

// SupplementPrefetchOrders 补充预拉取订单，当库存不足时调用
func (s *sCamelOil) SupplementPrefetchOrders(ctx context.Context) (supplementedCount int, err error) {
	gmlock.Lock(consts.CamelOilPrefetchTaskLockKey)
	defer gmlock.Unlock(consts.CamelOilPrefetchTaskLockKey)

	// 获取设置
	settings, err := s.GetCamelOilSettings(ctx)
	if err != nil {
		glog.Errorf(ctx, "获取骆驼模块设置失败: %v", err)
		return 0, err
	}

	// 如果没有设置面额配置，直接返回
	if len(settings.TargetDenominations) == 0 {
		return 0, nil
	}

	successCount := 0
	for _, denom := range settings.TargetDenominations {
		// 1. 获取当前库存
		currentCapacity, err2 := s.GetPrefetchOrderCapacity(ctx, float64(denom.Denomination))
		if err2 != nil {
			return 0, gerror.Wrap(err2, "获取预拉取订单库存失败")
		}

		// 2. 如果库存充足，无需补充
		if currentCapacity >= denom.Capacity {
			continue
		}

		// 3. 计算需要补充的数量
		needCount := denom.Capacity - currentCapacity
		s.SavePrefetchOrderLog(ctx, fmt.Sprintf("面额 %d 预拉取订单库存不足，需要补充 %d 单", denom.Denomination, needCount))
		glog.Infof(ctx, "面额 %d 预拉取订单库存不足，需要补充 %d 单", denom.Denomination, needCount)

		// 4. 并发拉取订单
		success, err := s.ConcurrentPrefetchOrders(ctx, float64(denom.Denomination), needCount)
		if err != nil {
			glog.Errorf(ctx, "面额 %d 并发拉取订单失败: %v", denom.Denomination, err)
			continue
		}
		successCount += success
	}
	return successCount, nil
}

// MatchPrefetchOrder 将预拉取订单与用户订单进行匹配
func (s *sCamelOil) MatchPrefetchOrder(ctx context.Context, orderId string, amount float64) (result *model.PrefetchOrderResult, err error) {
	// 2. 查询待匹配的预拉取订单（同金额）
	var prefetchOrder *entity.V1CamelOilPrefetchOrder
	err = dao.V1CamelOilPrefetchOrder.Ctx(ctx).DB(config.GetDatabaseV1()).
		Where(dao.V1CamelOilPrefetchOrder.Columns().Status, consts.CamelOilPrefetchOrderStatusPending).
		Where(dao.V1CamelOilPrefetchOrder.Columns().Amount, amount).
		WhereGTE(dao.V1CamelOilPrefetchOrder.Columns().ExpireAt, gtime.Now()).
		OrderAsc(dao.V1CamelOilPrefetchOrder.Columns().CreatedAt).
		Scan(&prefetchOrder)

	if err != nil {
		return nil, gerror.Wrap(err, "查询预拉取订单失败")
	}

	if prefetchOrder == nil {
		return nil, gerror.New("暂无匹配的预拉取订单，请稍后重试")
	}

	// 3. 使用事务更新预拉取订单和用户订单
	err = config.GetDatabaseV1().Ctx(ctx).Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		// 3.1 更新预拉取订单：标记为已匹配
		_, err = dao.V1CamelOilPrefetchOrder.Ctx(ctx).DB(config.GetDatabaseV1()).TX(tx).
			Where(dao.V1CamelOilPrefetchOrder.Columns().Id, prefetchOrder.Id).
			Update(&do.V1CamelOilPrefetchOrder{
				Status:    consts.CamelOilPrefetchOrderStatusMatched,
				OrderNo:   orderId,
				MatchedAt: gtime.Now(),
			})

		if err != nil {
			return gerror.Wrap(err, "更新预拉取订单状态失败")
		}
		if err != nil {
			return gerror.Wrap(err, "更新用户订单失败")
		}

		// 3.3 记录预拉取订单历史
		_ = s.recordPrefetchOrderHistory(ctx, prefetchOrder.Id, consts.CamelOilPrefetchOrderChangeTypeMatch,
			prefetchOrder.AccountId, prefetchOrder.AccountName,
			fmt.Sprintf("与用户订单匹配，订单ID=%s", orderId), tx)

		return nil
	})

	if err != nil {
		return nil, err
	}

	result = &model.PrefetchOrderResult{
		PlatformOrderNo: prefetchOrder.PlatformOrderNo,
		AlipayUrl:       prefetchOrder.AlipayUrl,
		AccountId:       prefetchOrder.AccountId,
		AccountName:     prefetchOrder.AccountName,
		Amount:          prefetchOrder.Amount.InexactFloat64(),
	}

	glog.Infof(ctx, "预拉取订单匹配成功，预拉取ID=%d, 用户订单ID=%s, 平台订单号=%s",
		prefetchOrder.Id, orderId, prefetchOrder.PlatformOrderNo)

	return result, nil
}

// recordPrefetchOrderHistory 记录预拉取订单历史
func (s *sCamelOil) recordPrefetchOrderHistory(ctx context.Context, prefetchId int64,
	changeType consts.CamelOilPrefetchOrderChangeType, accountId int64, accountName, remark string, tx gdb.TX) error {
	m := dao.V1CamelOilPrefetchOrderHistory.Ctx(ctx).DB(config.GetDatabaseV1())
	if tx != nil {
		m = m.TX(tx)
	}
	_, err := m.Insert(&do.V1CamelOilPrefetchOrderHistory{
		PrefetchId:  prefetchId,
		ChangeType:  string(changeType),
		AccountId:   accountId,
		AccountName: accountName,
		Remark:      remark,
	})

	if err != nil {
		glog.Errorf(ctx, "记录预拉取订单历史失败: %v", err)
	}

	return err
}
