package mysql

import (
	"context"
	"database/sql"

	"hyapp/pkg/appcode"
	"hyapp/pkg/xerr"
)

// lockVipCommand 在任何会员、账户、返现或资源业务锁之前串行化同 App、同 command_id 的 VIP 写命令。
//
// 仅依赖 wallet_transactions 或 resource_grants 的唯一键时，两个首发事务可能先锁住不同业务行，
// 再同时争抢幂等唯一键，导致后到请求泄漏余额不足、降级或 MySQL duplicate 等可变结果。守卫行与
// 业务事实处于同一事务：失败会一起回滚，成功后则让重放请求先等待原事务提交，再读取稳定回执。
func (r *Repository) lockVipCommand(ctx context.Context, tx *sql.Tx, commandID string, bizType string, requestHash string, nowMS int64, conflictCode xerr.Code) error {
	if _, err := tx.ExecContext(ctx, `
		INSERT INTO vip_command_locks (
		   app_code, command_id, biz_type, request_hash, created_at_ms, updated_at_ms
		 ) VALUES (?, ?, ?, ?, ?, ?)
		 ON DUPLICATE KEY UPDATE command_id = VALUES(command_id)`,
		appcode.FromContext(ctx), commandID, bizType, requestHash, nowMS, nowMS,
	); err != nil {
		return err
	}

	// UPSERT 已经承担等待未提交同键事务的职责；FOR UPDATE 再读取已提交语义，既校验跨 VIP
	// 命令类型复用，也不依赖 MySQL RowsAffected/CLIENT_FOUND_ROWS 的驱动差异。
	var storedBizType string
	var storedRequestHash string
	if err := tx.QueryRowContext(ctx, `
		SELECT biz_type, request_hash
		FROM vip_command_locks
		WHERE app_code = ? AND command_id = ?
		FOR UPDATE`,
		appcode.FromContext(ctx), commandID,
	).Scan(&storedBizType, &storedRequestHash); err != nil {
		return err
	}
	if storedBizType != bizType || storedRequestHash != requestHash {
		return xerr.New(conflictCode, "vip command idempotency conflict")
	}
	return nil
}
