<?php
/**
 * /api/providers/mexico_trade.php — FINAL (LAST)
 * - Fix: لا يعتمد حصراً على pair_id (fallback via symbol)
 * - Fix: يستخدم trade_order_id (وليس order_id)
 * - يسجل في transactions بشكل ديناميكي حسب الأعمدة الموجودة
 * - Precheck: selfSymbols لتفسير خطأ 10007 (symbol not support api)
 * - ✅ يدعم MARKET / LIMIT حسب trade_orders.order_type و/أو ui_order_mode
 * - ✅ يمنع تكرار إنشاء أوامر جديدة:
 *      - إذا كان provider_order_id موجود (إن كان العمود موجود)
 *      - أو يستخرج orderId من آخر transactions response_data لنفس trade_order_id
 * - ✅ يجلب exchangeInfo (tickSize/stepSize/minNotional/minQty/maxQty) لتعديل السعر/الكمية وتفادي أخطاء الحد الأدنى
 * - ✅ FIX مهم: yc_clean_decimal_str لا يحذف أصفار الأعداد الصحيحة (830 لا تصبح 83)
 *      + يدعم الأرقام العربية والفاصلة العربية (٫) + فصل آلاف (٬)
 * - ✅ NEW: provider_update() لتعديل أوامر LIMIT عبر المزود (Cancel + Recreate) بشكل آمن
 * - ملاحظة: take_profit/stop_loss ما زالت تُعامل كـ LIMIT (ليست Trigger حقيقية)
 */

if (!defined('EAZZYBIT_INTERNAL_CALL')) {
  if (session_status() === PHP_SESSION_NONE) session_start();

  header("Access-Control-Allow-Origin: https://eazzybit.com");
  header("Vary: Origin");
  header("Access-Control-Allow-Credentials: true");
  header("Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With");
  header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
  header("Access-Control-Max-Age: 600");
  header("Content-Type: application/json; charset=UTF-8");
  if (($_SERVER['REQUEST_METHOD'] ?? '') === 'OPTIONS') { http_response_code(204); exit; }
}

/* ================= Helpers ================= */
function yc_read_input(): array {
  $raw = file_get_contents('php://input') ?: '';
  $json = json_decode($raw, true);
  if (is_array($json)) return $json;
  return $_POST ?? $_GET ?? [];
}

function yc_table_exists(PDO $conn, string $table): bool {
  $st = $conn->prepare("SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=:t LIMIT 1");
  $st->execute([':t'=>$table]);
  return (bool)$st->fetchColumn();
}

function yc_col_exists(PDO $conn, string $table, string $col): bool {
  $st = $conn->prepare("SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=:t AND COLUMN_NAME=:c LIMIT 1");
  $st->execute([':t'=>$table, ':c'=>$col]);
  return (bool)$st->fetchColumn();
}

/**
 * ✅ تنظيف آمن للأرقام:
 * - يحافظ على أصفار الأعداد الصحيحة (830 تبقى 830)
 * - يحذف أصفار الكسور فقط (12.3400 -> 12.34)
 * - يدعم الأرقام العربية والفاصلة العربية + آلاف عربية
 */
function yc_clean_decimal_str($v, bool $allowSign=false): string {
  $s = trim((string)$v);
  if ($s === '') return '0';

  // أرقام عربية + فواصل عربية
  $s = strtr($s, [
    '٠'=>'0','١'=>'1','٢'=>'2','٣'=>'3','٤'=>'4','٥'=>'5','٦'=>'6','٧'=>'7','٨'=>'8','٩'=>'9',
    '٫'=>'.','٬'=>'', // آلاف عربية
  ]);

  // إذا لا يوجد نقطة ويوجد فاصلة، اعتبرها فاصلة عشرية
  if (strpos($s, '.') === false && strpos($s, ',') !== false) {
    $s = str_replace(',', '.', $s);
  } else {
    // غير ذلك: اعتبر الفاصلة فاصل آلاف واحذفها
    $s = str_replace(',', '', $s);
  }

  $sign = '';
  if ($allowSign && $s !== '' && ($s[0] === '-' || $s[0] === '+')) {
    $sign = ($s[0] === '-') ? '-' : '';
    $s = substr($s, 1);
  }

  $s = preg_replace('/[^0-9.]/', '', $s);

  // توحيد تعدد النقاط
  if (substr_count($s, '.') > 1) {
    $parts = explode('.', $s);
    $s = array_shift($parts) . '.' . implode('', $parts);
  }

  if ($s === '' || $s === '.') $s = '0';

  if (strpos($s, '.') !== false) {
    [$a,$b] = explode('.', $s, 2);
    $a = ltrim($a, '0'); if ($a === '') $a = '0';
    $b = substr($b, 0, 18);
    $b = rtrim($b, '0'); // ✅ فقط كسور
    $s = $a . ($b !== '' ? '.' . $b : '');
  } else {
    // ✅ عدد صحيح: لا تحذف الأصفار الختامية
    $s = ltrim($s, '0');
    if ($s === '') $s = '0';
  }

  if ($allowSign && $sign === '-' && $s !== '0') $s = '-' . $s;
  return $s;
}

function yc_dec_add(string $a, string $b, int $scale=18): string {
  if (function_exists('bcadd')) return bcadd($a, $b, $scale);
  $x = (float)$a + (float)$b;
  $s = number_format($x, $scale, '.', '');
  return rtrim(rtrim($s,'0'),'.') ?: '0';
}
function yc_dec_sub(string $a, string $b, int $scale=18): string {
  if (function_exists('bcsub')) return bcsub($a, $b, $scale);
  $x = (float)$a - (float)$b;
  $s = number_format($x, $scale, '.', '');
  return rtrim(rtrim($s,'0'),'.') ?: '0';
}
function yc_dec_mul(string $a, string $b, int $scale=18): string {
  if (function_exists('bcmul')) return bcmul($a, $b, $scale);
  $x = (float)$a * (float)$b;
  $s = number_format($x, $scale, '.', '');
  return rtrim(rtrim($s,'0'),'.') ?: '0';
}
function yc_dec_div(string $a, string $b, int $scale=18): string {
  $b = yc_clean_decimal_str($b,false);
  if ($b === '0') return '0';
  if (function_exists('bcdiv')) return bcdiv($a, $b, $scale);
  $x = (float)$a / (float)$b;
  $s = number_format($x, $scale, '.', '');
  return rtrim(rtrim($s,'0'),'.') ?: '0';
}
function yc_dec_cmp(string $a, string $b, int $scale=18): int {
  if (function_exists('bccomp')) return bccomp($a, $b, $scale);
  $x = (float)$a; $y = (float)$b;
  if (abs($x - $y) < 1e-12) return 0;
  return ($x < $y) ? -1 : 1;
}

function yc_floor_to_step(string $value, string $step, int $scale=18): string {
  $value = yc_clean_decimal_str($value,false);
  $step  = yc_clean_decimal_str($step,false);
  if ($step === '0') return $value;
  if (function_exists('bcdiv') && function_exists('bcmul')) {
    $q = bcdiv($value, $step, 0);          // floor quotient
    $r = bcmul($q, $step, $scale);
    return yc_clean_decimal_str($r,false);
  }
  $v = (float)$value; $s = (float)$step;
  if ($s <= 0) return $value;
  $q = floor($v / $s);
  $r = $q * $s;
  $out = number_format($r, $scale, '.', '');
  return rtrim(rtrim($out,'0'),'.') ?: '0';
}

function yc_ceil_to_step(string $value, string $step, int $scale=18): string {
  $value = yc_clean_decimal_str($value,false);
  $step  = yc_clean_decimal_str($step,false);
  if ($step === '0') return $value;

  $floor = yc_floor_to_step($value, $step, $scale);
  if (yc_dec_cmp($floor, $value, $scale) === 0) return $floor;
  return yc_clean_decimal_str(yc_dec_add($floor, $step, $scale), false);
}

function yc_insert_ledger(PDO $conn, int $userId, int $assetId, string $amount, string $balanceType, string $refType, int $refId, string $status, string $note): void {
  $cols = ['user_id','asset_id','amount','balance_type','ref_type','ref_id','note'];
  $vals = [':uid',':aid',':amt',':bt',':rt',':rid',':note'];
  $p = [':uid'=>$userId, ':aid'=>$assetId, ':amt'=>$amount, ':bt'=>$balanceType, ':rt'=>$refType, ':rid'=>$refId, ':note'=>$note];

  if (yc_col_exists($conn,'ledger_entries','status')) { $cols[]='status'; $vals[]=':st'; $p[':st']=$status; }
  if (yc_col_exists($conn,'ledger_entries','created_at')) { $cols[]='created_at'; $vals[]='NOW()'; }

  $sql = "INSERT INTO ledger_entries (".implode(',',$cols).") VALUES (".implode(',',$vals).")";
  $st = $conn->prepare($sql);
  $st->execute($p);
}

function yc_update_trade_order(PDO $conn, int $id, string $status, ?string $reason, ?string $providerOrderId, ?string $clientOrderId): void {
  $set = [];
  $p = [':id'=>$id];

  if (yc_col_exists($conn,'trade_orders','status')) { $set[]="status=:st"; $p[':st']=$status; }
  if (yc_col_exists($conn,'trade_orders','status_reason')) { $set[]="status_reason=:rs"; $p[':rs']=$reason; }
  if (yc_col_exists($conn,'trade_orders','provider_order_id')) { $set[]="provider_order_id=:po"; $p[':po']=$providerOrderId; }
  if (yc_col_exists($conn,'trade_orders','client_order_id')) { $set[]="client_order_id=:co"; $p[':co']=$clientOrderId; }
  if (yc_col_exists($conn,'trade_orders','updated_at')) { $set[]="updated_at=NOW()"; }

  if (!$set) return;

  $sql = "UPDATE trade_orders SET ".implode(', ', $set)." WHERE id=:id";
  $st = $conn->prepare($sql);
  $st->execute($p);
}

/**
 * ✅ transactions table في قاعدة بياناتك:
 * type enum('order','api') => لازم نستخدم 'api'
 */
function yc_insert_tx(PDO $conn, int $tradeOrderId, int $userId, int $providerId, string $endpoint, string $reqType, array $reqData, $respData, ?int $statusCode, ?string $txid, string $status): void {
  if (!yc_table_exists($conn,'transactions')) return;

  $possible = [
    'order_id' => $tradeOrderId,
    'user_id' => $userId,
    'provider_id' => $providerId,
    'type' => 'api',
    'endpoint' => $endpoint,
    'request_type' => $reqType,
    'request_data' => json_encode($reqData, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES),
    'response_data' => (is_string($respData) ? $respData : json_encode($respData, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES)),
    'status_code' => $statusCode,
    'txid' => $txid,
    'status' => $status,
  ];

  $cols = [];
  $vals = [];
  $p = [];

  foreach ($possible as $c=>$v) {
    if (yc_col_exists($conn,'transactions',$c)) {
      $cols[] = $c;
      $ph = ':'.$c;
      $vals[] = $ph;
      $p[$ph] = $v;
    }
  }
  if (yc_col_exists($conn,'transactions','created_at')) { $cols[]='created_at'; $vals[]='NOW()'; }

  if (!$cols) return;

  $sql = "INSERT INTO transactions (".implode(',',$cols).") VALUES (".implode(',',$vals).")";
  $st = $conn->prepare($sql);
  $st->execute($p);
}

function yc_mexc_signed_query(array $params, string $secret): string {
  ksort($params);
  $qs = http_build_query($params);
  $sig = hash_hmac('sha256', $qs, $secret);
  return $qs . '&signature=' . $sig;
}

function yc_http(string $method, string $url, array $headers, ?string $body = null, int $timeout = 20): array {
  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL, $url);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
  curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
  curl_setopt($ch, CURLOPT_CUSTOMREQUEST, strtoupper($method));
  if ($body !== null) curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
  $resp = curl_exec($ch);
  $http = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
  $err  = curl_error($ch);
  curl_close($ch);
  return ['ok'=>($err==='' && $http>0), 'http'=>$http, 'err'=>$err, 'resp'=>$resp];
}

/* ================= MEXC Filters (exchangeInfo) ================= */
function yc_mexc_get_exchange_filters(string $apiUrl, array $headers, string $symbol): array {
  $symbol = strtoupper(trim($symbol));
  if ($symbol === '') return [];

  $url = rtrim($apiUrl,'/') . '/api/v3/exchangeInfo?symbol=' . urlencode($symbol);
  $res = yc_http('GET', $url, $headers, null, 15);
  if (!$res['ok']) return [];

  $arr = json_decode((string)$res['resp'], true);
  if (!is_array($arr)) return [];

  $sym = null;
  if (isset($arr['symbols']) && is_array($arr['symbols']) && count($arr['symbols']) > 0) {
    $sym = $arr['symbols'][0];
  } elseif (isset($arr['data']) && is_array($arr['data']) && isset($arr['data']['symbols']) && is_array($arr['data']['symbols']) && count($arr['data']['symbols']) > 0) {
    $sym = $arr['data']['symbols'][0];
  }
  if (!is_array($sym)) return [];

  $filters = $sym['filters'] ?? null;
  if (!is_array($filters)) return [];

  $out = [
    'tickSize' => '0',
    'stepSize' => '0',
    'minQty' => '0',
    'maxQty' => '0',
    'minNotional' => '0',
  ];

  foreach ($filters as $f) {
    $type = strtoupper((string)($f['filterType'] ?? $f['filter_type'] ?? ''));
    if ($type === 'PRICE_FILTER') {
      $out['tickSize'] = yc_clean_decimal_str($f['tickSize'] ?? $f['tick_size'] ?? '0', false);
    } elseif ($type === 'LOT_SIZE') {
      $out['stepSize'] = yc_clean_decimal_str($f['stepSize'] ?? $f['step_size'] ?? '0', false);
      $out['minQty']   = yc_clean_decimal_str($f['minQty'] ?? $f['min_qty'] ?? '0', false);
      $out['maxQty']   = yc_clean_decimal_str($f['maxQty'] ?? $f['max_qty'] ?? '0', false);
    } elseif ($type === 'MIN_NOTIONAL' || $type === 'NOTIONAL') {
      $out['minNotional'] = yc_clean_decimal_str($f['minNotional'] ?? $f['min_notional'] ?? '0', false);
    }
  }

  return $out;
}

function yc_extract_order_id_from_transactions(PDO $conn, int $tradeOrderId): string {
  if (!yc_table_exists($conn,'transactions')) return '';

  $sql = "SELECT txid, response_data
          FROM transactions
          WHERE order_id=:oid AND endpoint='/api/v3/order' AND request_type='POST'
          ORDER BY id DESC
          LIMIT 1";
  try {
    $st = $conn->prepare($sql);
    $st->execute([':oid'=>$tradeOrderId]);
    $row = $st->fetch(PDO::FETCH_ASSOC);
    if (!$row) return '';

    $txid = (string)($row['txid'] ?? '');
    if ($txid !== '') return $txid;

    $resp = (string)($row['response_data'] ?? '');
    $j = json_decode($resp, true);
    if (is_array($j)) {
      $oid = (string)($j['orderId'] ?? '');
      if ($oid !== '') return $oid;
    }

    if (preg_match('/"orderId"\s*:\s*"?([0-9]+)"?/i', $resp, $m)) return (string)$m[1];

  } catch (Throwable $e) {
    return '';
  }
  return '';
}

/* ================= Provider Execute ================= */
function provider_execute(PDO $conn, array $ctx): array {
  $tradeOrderId = (int)($ctx['trade_order_id'] ?? 0);
  $providerId   = (int)($ctx['provider_id'] ?? 0);

  if ($tradeOrderId <= 0) return ['success'=>false,'status'=>'canceled','message'=>'trade_order_id غير صحيح.'];

  foreach (['trade_orders','providers','ledger_entries','assets','asset_pairs'] as $t){
    if (!yc_table_exists($conn,$t)) return ['success'=>false,'status'=>'canceled','message'=>"جداول النظام غير مكتملة: {$t}"];
  }

  try {
    $conn->beginTransaction();

    // lock order
    $stO = $conn->prepare("SELECT * FROM trade_orders WHERE id=:id FOR UPDATE");
    $stO->execute([':id'=>$tradeOrderId]);
    $o = $stO->fetch(PDO::FETCH_ASSOC);

    if (!$o) { $conn->rollBack(); return ['success'=>false,'status'=>'canceled','message'=>'الطلب غير موجود.']; }

    $status = (string)($o['status'] ?? '');
    if ($status !== 'pending') {
      $conn->commit();
      return ['success'=>($status==='completed'),'status'=>$status,'message'=>'تمت معالجة الطلب مسبقاً.','trade_order_id'=>$tradeOrderId];
    }

    $userId = (int)($o['user_id'] ?? 0);
    $symbol = strtoupper((string)($o['symbol'] ?? ''));

    // pair resolve: prefer pair_id, else fallback via symbol -> asset_pairs.tv_symbol
    $pair = null;
    $pairId = (int)($o['pair_id'] ?? 0);

    if ($pairId > 0 && yc_col_exists($conn,'asset_pairs','id')) {
      $stP = $conn->prepare("SELECT id, base_asset_id, quote_asset_id, tv_symbol FROM asset_pairs WHERE id=:id LIMIT 1");
      $stP->execute([':id'=>$pairId]);
      $pair = $stP->fetch(PDO::FETCH_ASSOC) ?: null;
    }

    if (!$pair) {
      if ($symbol === '') {
        yc_update_trade_order($conn,$tradeOrderId,'canceled','pair_id/symbol غير متوفر.',null,null);
        $conn->commit();
        return ['success'=>false,'status'=>'canceled','message'=>'pair_id/symbol غير متوفر.'];
      }
      $stP = $conn->prepare("SELECT id, base_asset_id, quote_asset_id, tv_symbol FROM asset_pairs WHERE tv_symbol=:s LIMIT 1");
      $stP->execute([':s'=>$symbol]);
      $pair = $stP->fetch(PDO::FETCH_ASSOC) ?: null;
      if (!$pair) {
        yc_update_trade_order($conn,$tradeOrderId,'canceled','الزوج غير موجود في asset_pairs.',null,null);
        $conn->commit();
        return ['success'=>false,'status'=>'canceled','message'=>'الزوج غير موجود.'];
      }
      $pairId = (int)$pair['id'];
      if (yc_col_exists($conn,'trade_orders','pair_id')) {
        $stU = $conn->prepare("UPDATE trade_orders SET pair_id=:pid WHERE id=:id");
        $stU->execute([':pid'=>$pairId, ':id'=>$tradeOrderId]);
      }
    }

    $baseAssetId  = (int)$pair['base_asset_id'];
    $quoteAssetId = (int)$pair['quote_asset_id'];
    if ($symbol === '') $symbol = strtoupper((string)($pair['tv_symbol'] ?? ''));

    $side = strtolower((string)($o['side'] ?? 'buy'));
    $side = ($side === 'sell') ? 'sell' : 'buy';

    $qtyBase  = yc_clean_decimal_str($o['qty_base'] ?? '0', false);
    $qtyQuote = yc_clean_decimal_str($o['qty_quote'] ?? '0', false);

    $holdAmount  = yc_clean_decimal_str($o['hold_amount'] ?? '0', false);
    $holdAssetId = (int)($o['hold_asset_id'] ?? 0);
    $feeRate     = yc_clean_decimal_str($o['fee_rate'] ?? '0', false);

    if ($providerId <= 0) $providerId = (int)($o['provider_id'] ?? 0);

    // بيانات المزود
    $stPr = $conn->prepare("SELECT * FROM providers WHERE id=:id AND is_active=1 LIMIT 1");
    $stPr->execute([':id'=>$providerId]);
    $prov = $stPr->fetch(PDO::FETCH_ASSOC);
    if (!$prov) {
      yc_insert_ledger($conn,$userId,$holdAssetId,$holdAmount,'available','trade',$tradeOrderId,'posted',"Refund available (trade #{$tradeOrderId})");
      yc_insert_ledger($conn,$userId,$holdAssetId,yc_dec_sub('0',$holdAmount,18),'locked','trade',$tradeOrderId,'posted',"Unlock locked (trade #{$tradeOrderId})");
      yc_update_trade_order($conn,$tradeOrderId,'canceled','بيانات المزود غير موجودة.',null,null);
      $conn->commit();
      return ['success'=>false,'status'=>'canceled','message'=>'بيانات المزود غير موجودة.'];
    }

    $apiKey    = (string)($prov['username'] ?? '');
    $apiSecret = (string)($prov['password'] ?? '');
    $apiUrl    = rtrim((string)($prov['api_url'] ?? ''), '/');

    if ($apiKey==='' || $apiSecret==='' || $apiUrl==='') {
      yc_insert_ledger($conn,$userId,$holdAssetId,$holdAmount,'available','trade',$tradeOrderId,'posted',"Refund available (trade #{$tradeOrderId})");
      yc_insert_ledger($conn,$userId,$holdAssetId,yc_dec_sub('0',$holdAmount,18),'locked','trade',$tradeOrderId,'posted',"Unlock locked (trade #{$tradeOrderId})");
      yc_update_trade_order($conn,$tradeOrderId,'canceled','بيانات المزود غير مكتملة.',null,null);
      $conn->commit();
      return ['success'=>false,'status'=>'canceled','message'=>'بيانات المزود غير مكتملة.'];
    }

    // أكواد الأصول
    $stC = $conn->prepare("SELECT code FROM assets WHERE id=:id LIMIT 1");
    $stC->execute([':id'=>$baseAssetId]);
    $baseCode = strtoupper((string)($stC->fetchColumn() ?: ''));
    $stC->execute([':id'=>$quoteAssetId]);
    $quoteCode = strtoupper((string)($stC->fetchColumn() ?: ''));

    $headers = [
      "x-mexc-apikey: {$apiKey}",
      "Content-Type: application/json"
    ];

    /* ================= Precheck: selfSymbols ================= */
    $timestamp = (int)round(microtime(true) * 1000);
    $selfParams = ['timestamp'=>$timestamp, 'recvWindow'=>5000];
    $selfUrl = $apiUrl . '/api/v3/selfSymbols?' . yc_mexc_signed_query($selfParams, $apiSecret);
    $self = yc_http('GET', $selfUrl, $headers, null, 20);

    $selfArr = is_string($self['resp']) ? json_decode($self['resp'], true) : null;
    if (is_array($selfArr) && isset($selfArr['data']) && is_array($selfArr['data'])) {
      if (!in_array($symbol, $selfArr['data'], true)) {
        yc_insert_tx($conn,$tradeOrderId,$userId,$providerId,'/api/v3/selfSymbols','GET',$selfParams,$selfArr,$self['http'],null,'failed');

        yc_insert_ledger($conn,$userId,$holdAssetId,$holdAmount,'available','trade',$tradeOrderId,'posted',"Refund available (trade #{$tradeOrderId})");
        yc_insert_ledger($conn,$userId,$holdAssetId,yc_dec_sub('0',$holdAmount,18),'locked','trade',$tradeOrderId,'posted',"Unlock locked (trade #{$tradeOrderId})");
        yc_update_trade_order($conn,$tradeOrderId,'canceled',"هذا الرمز غير مفعل للتداول عبر API لهذا المفتاح: {$symbol}",null,null);

        $conn->commit();
        return [
          'success'=>false,'status'=>'canceled',
          'message'=>"symbol not enabled for API (selfSymbols): {$symbol}",
          'selfSymbols_count'=>count($selfArr['data'])
        ];
      }
    } else {
      yc_insert_tx($conn,$tradeOrderId,$userId,$providerId,'/api/v3/selfSymbols','GET',$selfParams,($self['resp'] ?: $self['err']),$self['http'],null,'info');
    }

    /* ================= Decide Order Mode ================= */
    $orderTypeDb = strtolower((string)($o['order_type'] ?? 'market'));
    $uiModeCtx   = strtolower((string)($ctx['ui_order_mode'] ?? ''));
    $uiModeDb    = strtolower((string)($o['ui_order_mode'] ?? ''));

    $uiMode = $uiModeCtx !== '' ? $uiModeCtx : $uiModeDb;
    $uiMode = preg_replace('/[^a-z0-9_]/','', $uiMode);

    $isLimit = ($orderTypeDb === 'limit') || in_array($uiMode, ['limit','take_profit','stop_loss'], true);

    $limitPrice = yc_clean_decimal_str($o['price'] ?? '0', false);

    /* ================= Read exchangeInfo filters ================= */
    $filters = yc_mexc_get_exchange_filters($apiUrl, $headers, $symbol);
    $tickSize = $filters['tickSize'] ?? '0';
    $stepSize = $filters['stepSize'] ?? '0';
    $minNotional = $filters['minNotional'] ?? '0';
    $minQty = $filters['minQty'] ?? '0';
    $maxQty = $filters['maxQty'] ?? '0';

    if ($isLimit && yc_dec_cmp($tickSize,'0') > 0 && yc_dec_cmp($limitPrice,'0') > 0) {
      $limitPrice = yc_floor_to_step($limitPrice, $tickSize, 18);
    }

    /* ================= Determine existing provider_order_id (no duplicates) ================= */
    $existingProviderOrderId = (string)($o['provider_order_id'] ?? '');
    $existingClientOrderId   = (string)($o['client_order_id'] ?? '');

    if ($existingProviderOrderId === '') {
      $existingProviderOrderId = yc_extract_order_id_from_transactions($conn, $tradeOrderId);
    }

    $providerOrderId = '';
    $clientOrderId   = '';

    if ($existingProviderOrderId !== '') {
      $providerOrderId = $existingProviderOrderId;
      $clientOrderId   = $existingClientOrderId ?: ('EBT_'.$tradeOrderId);
      goto QUERY_STATUS;
    }

    /* ================= Place Order (MARKET/LIMIT) ================= */
    $endpoint = '/api/v3/order';

    if ($isLimit) {
      if (yc_dec_cmp($limitPrice,'0') <= 0) {
        yc_insert_ledger($conn,$userId,$holdAssetId,$holdAmount,'available','trade',$tradeOrderId,'posted',"Refund available (trade #{$tradeOrderId})");
        yc_insert_ledger($conn,$userId,$holdAssetId,yc_dec_sub('0',$holdAmount,18),'locked','trade',$tradeOrderId,'posted',"Unlock locked (trade #{$tradeOrderId})");
        yc_update_trade_order($conn,$tradeOrderId,'canceled','سعر LIMIT غير متوفر في الطلب.',null,null);
        $conn->commit();
        return ['success'=>false,'status'=>'canceled','message'=>'سعر LIMIT غير متوفر في الطلب.'];
      }

      // LIMIT: quantity (base) + filters
      $qBase = $qtyBase;

      if (yc_dec_cmp($stepSize,'0') > 0) {
        // BUY: ceil, SELL: floor
        $qBase = ($side === 'buy')
          ? yc_ceil_to_step($qBase, $stepSize, 18)
          : yc_floor_to_step($qBase, $stepSize, 18);
      }

      if (yc_dec_cmp($minQty,'0') > 0 && yc_dec_cmp($qBase,$minQty) < 0) {
        if ($side === 'buy') {
          $qBase = (yc_dec_cmp($stepSize,'0') > 0) ? yc_ceil_to_step($minQty, $stepSize, 18) : $minQty;
        } else {
          $reason = "الكمية أقل من الحد الأدنى (minQty={$minQty}).";
          yc_insert_ledger($conn,$userId,$holdAssetId,$holdAmount,'available','trade',$tradeOrderId,'posted',"Refund available (trade #{$tradeOrderId})");
          yc_insert_ledger($conn,$userId,$holdAssetId,yc_dec_sub('0',$holdAmount,18),'locked','trade',$tradeOrderId,'posted',"Unlock locked (trade #{$tradeOrderId})");
          yc_update_trade_order($conn,$tradeOrderId,'canceled',$reason,null,null);
          $conn->commit();
          return ['success'=>false,'status'=>'canceled','message'=>$reason];
        }
      }

      if (yc_dec_cmp($maxQty,'0') > 0 && yc_dec_cmp($qBase,$maxQty) > 0) {
        $reason = "الكمية أعلى من الحد الأقصى (maxQty={$maxQty}).";
        yc_insert_ledger($conn,$userId,$holdAssetId,$holdAmount,'available','trade',$tradeOrderId,'posted',"Refund available (trade #{$tradeOrderId})");
        yc_insert_ledger($conn,$userId,$holdAssetId,yc_dec_sub('0',$holdAmount,18),'locked','trade',$tradeOrderId,'posted',"Unlock locked (trade #{$tradeOrderId})");
        yc_update_trade_order($conn,$tradeOrderId,'canceled',$reason,null,null);
        $conn->commit();
        return ['success'=>false,'status'=>'canceled','message'=>$reason];
      }

      if (yc_dec_cmp($minNotional,'0') > 0) {
        $notional = yc_dec_mul($qBase, $limitPrice, 18);

        if (yc_dec_cmp($notional, $minNotional) < 0) {
          if ($side === 'buy') {
            $needBase = yc_dec_div($minNotional, $limitPrice, 18);
            $needBase = (yc_dec_cmp($stepSize,'0') > 0) ? yc_ceil_to_step($needBase, $stepSize, 18) : $needBase;

            $newNotional = yc_dec_mul($needBase, $limitPrice, 18);
            $limitAllowance = (yc_dec_cmp($qtyQuote,'0') > 0) ? yc_dec_mul($qtyQuote, '1.02', 18) : '0';

            if (yc_dec_cmp($limitAllowance,'0') > 0 && yc_dec_cmp($newNotional, $limitAllowance) > 0) {
              $reason = "قيمة الأمر أقل من الحد الأدنى ولا يمكن رفعها تلقائياً (minNotional={$minNotional} {$quoteCode}).";
              yc_insert_ledger($conn,$userId,$holdAssetId,$holdAmount,'available','trade',$tradeOrderId,'posted',"Refund available (trade #{$tradeOrderId})");
              yc_insert_ledger($conn,$userId,$holdAssetId,yc_dec_sub('0',$holdAmount,18),'locked','trade',$tradeOrderId,'posted',"Unlock locked (trade #{$tradeOrderId})");
              yc_update_trade_order($conn,$tradeOrderId,'canceled',$reason,null,null);
              $conn->commit();
              return ['success'=>false,'status'=>'canceled','message'=>$reason];
            }

            $qBase = $needBase;
          } else {
            $reason = "قيمة البيع أقل من الحد الأدنى (minNotional={$minNotional} {$quoteCode}).";
            yc_insert_ledger($conn,$userId,$holdAssetId,$holdAmount,'available','trade',$tradeOrderId,'posted',"Refund available (trade #{$tradeOrderId})");
            yc_insert_ledger($conn,$userId,$holdAssetId,yc_dec_sub('0',$holdAmount,18),'locked','trade',$tradeOrderId,'posted',"Unlock locked (trade #{$tradeOrderId})");
            yc_update_trade_order($conn,$tradeOrderId,'canceled',$reason,null,null);
            $conn->commit();
            return ['success'=>false,'status'=>'canceled','message'=>$reason];
          }
        }
      }

      $p = [
        'symbol'     => $symbol,
        'side'       => strtoupper($side),
        'type'       => 'LIMIT',
        'timeInForce'=> 'GTC',
        'price'      => $limitPrice,
        'quantity'   => $qBase,
        'timestamp'  => (int)round(microtime(true) * 1000),
        'recvWindow' => 5000,
        'newClientOrderId' => 'EBT_'.$tradeOrderId
      ];

    } else {
      // MARKET
      if ($side === 'buy' && yc_dec_cmp($minNotional,'0') > 0) {
        $qTry = yc_dec_cmp($qtyQuote,'0') > 0 ? $qtyQuote : $holdAmount;
        if (yc_dec_cmp($qTry, $minNotional) < 0) {
          $reason = "قيمة الشراء أقل من الحد الأدنى (minNotional={$minNotional} {$quoteCode}).";
          yc_insert_ledger($conn,$userId,$holdAssetId,$holdAmount,'available','trade',$tradeOrderId,'posted',"Refund available (trade #{$tradeOrderId})");
          yc_insert_ledger($conn,$userId,$holdAssetId,yc_dec_sub('0',$holdAmount,18),'locked','trade',$tradeOrderId,'posted',"Unlock locked (trade #{$tradeOrderId})");
          yc_update_trade_order($conn,$tradeOrderId,'canceled',$reason,null,null);
          $conn->commit();
          return ['success'=>false,'status'=>'canceled','message'=>$reason];
        }
      }

      $p = [
        'symbol'     => $symbol,
        'side'       => strtoupper($side),
        'type'       => 'MARKET',
        'timestamp'  => (int)round(microtime(true) * 1000),
        'recvWindow' => 5000,
        'newClientOrderId' => 'EBT_'.$tradeOrderId
      ];

      if ($side === 'buy') {
        if (yc_dec_cmp($qtyQuote,'0') > 0) $p['quoteOrderQty'] = $qtyQuote;
        elseif (yc_dec_cmp($qtyBase,'0') > 0) $p['quantity'] = $qtyBase;
        else $p['quoteOrderQty'] = $holdAmount;
      } else {
        if (yc_dec_cmp($qtyBase,'0') <= 0) {
          yc_insert_ledger($conn,$userId,$holdAssetId,$holdAmount,'available','trade',$tradeOrderId,'posted',"Refund available (trade #{$tradeOrderId})");
          yc_insert_ledger($conn,$userId,$holdAssetId,yc_dec_sub('0',$holdAmount,18),'locked','trade',$tradeOrderId,'posted',"Unlock locked (trade #{$tradeOrderId})");
          yc_update_trade_order($conn,$tradeOrderId,'canceled','qty_base غير صالح للبيع.',null,null);
          $conn->commit();
          return ['success'=>false,'status'=>'canceled','message'=>'qty_base غير صالح للبيع.'];
        }
        $p['quantity'] = $qtyBase;
      }
    }

    $url = $apiUrl . $endpoint . '?' . yc_mexc_signed_query($p, $apiSecret);
    $call = yc_http('POST', $url, $headers, json_encode(new stdClass()), 20);

    $respArr = is_string($call['resp']) ? json_decode($call['resp'], true) : null;

    $tmpOrderId = is_array($respArr) ? (string)($respArr['orderId'] ?? '') : '';
    yc_insert_tx($conn,$tradeOrderId,$userId,$providerId,$endpoint,'POST',$p,($respArr ?: $call['resp'] ?: $call['err']),$call['http'],($tmpOrderId !== '' ? $tmpOrderId : null),($call['ok'] ? 'info':'failed'));

    if (!$call['ok']) {
      yc_insert_ledger($conn,$userId,$holdAssetId,$holdAmount,'available','trade',$tradeOrderId,'posted',"Refund available (trade #{$tradeOrderId})");
      yc_insert_ledger($conn,$userId,$holdAssetId,yc_dec_sub('0',$holdAmount,18),'locked','trade',$tradeOrderId,'posted',"Unlock locked (trade #{$tradeOrderId})");
      yc_update_trade_order($conn,$tradeOrderId,'canceled','فشل الاتصال بالمزود: '.$call['err'],null,null);
      $conn->commit();
      return ['success'=>false,'status'=>'canceled','message'=>'فشل الاتصال بالمزود.','http_code'=>$call['http']];
    }

    if (is_array($respArr) && isset($respArr['code']) && (int)$respArr['code'] !== 200 && empty($respArr['orderId'])) {
      $reason = (string)($respArr['msg'] ?? 'Provider error');
      yc_insert_ledger($conn,$userId,$holdAssetId,$holdAmount,'available','trade',$tradeOrderId,'posted',"Refund available (trade #{$tradeOrderId})");
      yc_insert_ledger($conn,$userId,$holdAssetId,yc_dec_sub('0',$holdAmount,18),'locked','trade',$tradeOrderId,'posted',"Unlock locked (trade #{$tradeOrderId})");
      yc_update_trade_order($conn,$tradeOrderId,'canceled',$reason,null,null);
      $conn->commit();
      return ['success'=>false,'status'=>'canceled','message'=>$reason,'raw'=>$respArr];
    }

    $providerOrderId = (string)($respArr['orderId'] ?? '');
    $clientOrderId   = (string)($respArr['clientOrderId'] ?? $respArr['origClientOrderId'] ?? ('EBT_'.$tradeOrderId));

    if ($providerOrderId === '') {
      yc_update_trade_order($conn,$tradeOrderId,'pending','تم إرسال الطلب للمزود (بدون orderId واضح).',null,$clientOrderId);
      $conn->commit();
      return ['success'=>true,'status'=>'pending','message'=>'تم إرسال الطلب للمزود (Pending).','raw'=>$respArr];
    }

    yc_update_trade_order($conn,$tradeOrderId,'pending','تم إرسال الطلب للمزود، بانتظار حالة التنفيذ.',$providerOrderId,$clientOrderId);

    /* ================= Query Order Status ================= */
QUERY_STATUS:
    $q = [
      'symbol'=>$symbol,
      'orderId'=>$providerOrderId,
      'timestamp'=>(int)round(microtime(true) * 1000),
      'recvWindow'=>5000
    ];
    $qurl = $apiUrl . '/api/v3/order?' . yc_mexc_signed_query($q, $apiSecret);
    $qcall = yc_http('GET', $qurl, $headers, null, 20);
    $qarr = is_string($qcall['resp']) ? json_decode($qcall['resp'], true) : null;

    yc_insert_tx($conn,$tradeOrderId,$userId,$providerId,'/api/v3/order','GET',$q,($qarr ?: $qcall['resp'] ?: $qcall['err']),$qcall['http'],$providerOrderId,($qcall['ok'] ? 'info':'failed'));

    if (!$qcall['ok'] || !is_array($qarr)) {
      yc_update_trade_order($conn,$tradeOrderId,'pending','تعذر قراءة حالة الأمر من المزود حالياً.',$providerOrderId,$clientOrderId);
      $conn->commit();
      return [
        'success'=>true,
        'status'=>'pending',
        'message'=>'تم إرسال/تسجيل الأمر، وتعذر جلب الحالة حالياً (Pending).',
        'provider_order_id'=>$providerOrderId,
        'client_order_id'=>$clientOrderId
      ];
    }

    $orderStatus = strtoupper((string)($qarr['status'] ?? ''));
    $executedQty = yc_clean_decimal_str($qarr['executedQty'] ?? '0', false);
    $cumQuote    = yc_clean_decimal_str($qarr['cummulativeQuoteQty'] ?? '0', false);

    if ($orderStatus === '' || in_array($orderStatus, ['NEW','PARTIALLY_FILLED','PARTIALLYFILLED'], true)) {
      yc_update_trade_order($conn,$tradeOrderId,'pending','الأمر قيد الانتظار لدى المزود.',$providerOrderId,$clientOrderId);
      $conn->commit();
      return [
        'success'=>true,
        'status'=>'pending',
        'message'=>'تم إرسال الطلب للمزود (Pending).',
        'provider_order_id'=>$providerOrderId,
        'client_order_id'=>$clientOrderId,
        'raw'=>$qarr
      ];
    }

    $isFilled = ($orderStatus === 'FILLED');
    $isFinalNotFilled = in_array($orderStatus, ['CANCELED','CANCELLED','EXPIRED','REJECTED'], true);

    if (!$isFilled && !$isFinalNotFilled) {
      $reason = 'فشل تنفيذ الأمر: '.$orderStatus;
      yc_insert_ledger($conn,$userId,$holdAssetId,$holdAmount,'available','trade',$tradeOrderId,'posted',"Refund available (trade #{$tradeOrderId})");
      yc_insert_ledger($conn,$userId,$holdAssetId,yc_dec_sub('0',$holdAmount,18),'locked','trade',$tradeOrderId,'posted',"Unlock locked (trade #{$tradeOrderId})");
      yc_update_trade_order($conn,$tradeOrderId,'canceled',$reason,$providerOrderId,$clientOrderId);
      $conn->commit();
      return ['success'=>false,'status'=>'canceled','message'=>$reason,'provider_order_id'=>$providerOrderId,'raw'=>$qarr];
    }

    if (!$isFilled && yc_dec_cmp($executedQty,'0') <= 0 && yc_dec_cmp($cumQuote,'0') <= 0) {
      $reason = 'فشل/إلغاء دون تنفيذ: '.$orderStatus;
      yc_insert_ledger($conn,$userId,$holdAssetId,$holdAmount,'available','trade',$tradeOrderId,'posted',"Refund available (trade #{$tradeOrderId})");
      yc_insert_ledger($conn,$userId,$holdAssetId,yc_dec_sub('0',$holdAmount,18),'locked','trade',$tradeOrderId,'posted',"Unlock locked (trade #{$tradeOrderId})");
      yc_update_trade_order($conn,$tradeOrderId,'canceled',$reason,$providerOrderId,$clientOrderId);
      $conn->commit();
      return ['success'=>false,'status'=>'canceled','message'=>$reason,'provider_order_id'=>$providerOrderId,'raw'=>$qarr];
    }

    /* ================= Settlement ================= */
    yc_insert_ledger($conn,$userId,$holdAssetId,yc_dec_sub('0',$holdAmount,18),'locked','trade',$tradeOrderId,'posted',"Trade consumed locked (trade #{$tradeOrderId})");

    if ($side === 'buy') {
      if (yc_dec_cmp($executedQty,'0') > 0) {
        yc_insert_ledger($conn,$userId,$baseAssetId,$executedQty,'available','trade',$tradeOrderId,'posted',"Trade BUY credit {$baseCode} (trade #{$tradeOrderId})");
      }

      $actualFee   = yc_dec_mul($cumQuote, $feeRate, 18);
      $actualTotal = yc_dec_add($cumQuote, $actualFee, 18);

      $diff = yc_dec_sub($holdAmount, $actualTotal, 18);
      if (yc_dec_cmp($diff,'0') > 0) {
        yc_insert_ledger($conn,$userId,$quoteAssetId,$diff,'available','trade',$tradeOrderId,'posted',"Trade BUY refund {$quoteCode} diff (trade #{$tradeOrderId})");
      }

    } else {
      $gross = $cumQuote;
      $fee   = yc_dec_mul($gross, $feeRate, 18);
      $net   = yc_dec_sub($gross, $fee, 18);

      if (yc_dec_cmp($net,'0') > 0) {
        yc_insert_ledger($conn,$userId,$quoteAssetId,$net,'available','trade',$tradeOrderId,'posted',"Trade SELL credit {$quoteCode} (trade #{$tradeOrderId})");
      }

      if (!$isFilled) {
        $remainingBase = yc_dec_sub($holdAmount, $executedQty, 18);
        if (yc_dec_cmp($remainingBase,'0') > 0) {
          yc_insert_ledger($conn,$userId,$baseAssetId,$remainingBase,'available','trade',$tradeOrderId,'posted',"Trade SELL refund remaining {$baseCode} (trade #{$tradeOrderId})");
        }
      }
    }

    $finalReason = $isFilled ? 'تم تنفيذ الطلب بنجاح.' : ('تم تنفيذ جزء من الطلب ثم انتهى بحالة: '.$orderStatus);
    yc_update_trade_order($conn,$tradeOrderId,'completed',$finalReason,$providerOrderId,$clientOrderId);

    $conn->commit();
    return [
      'success'=>true,
      'status'=>'completed',
      'message'=>$finalReason,
      'provider_order_id'=>$providerOrderId,
      'client_order_id'=>$clientOrderId,
      'executedQty'=>$executedQty,
      'cummulativeQuoteQty'=>$cumQuote,
      'raw'=>$qarr,
      'filters_used'=>$filters
    ];

  } catch (Throwable $e) {
    if ($conn->inTransaction()) $conn->rollBack();
    return ['success'=>false,'status'=>'canceled','message'=>'خطأ في تنفيذ المزود: '.$e->getMessage()];
  }
}

/* ================= Provider Update (LIMIT edit via Cancel + Recreate) ================= */
/**
 * provider_update(PDO $conn, array $ctx): array
 * - يُستخدم من update_trade_order.php
 * - شرط: وجود provider_order_id (أو يمرر في ctx)
 * - أمان: لا يسمح بالتعديل إذا كان الأمر FILLED أو تم تنفيذ جزء منه (executedQty > 0)
 * - تنفيذ: GET status -> DELETE cancel -> POST new LIMIT
 * - لا يقوم بتعديل ledger (الـ ledger يتولاه update_trade_order.php)
 * - يقوم بتحديث provider_order_id/client_order_id في trade_orders إذا كانت الأعمدة موجودة
 *
 * ctx:
 *  trade_order_id, provider_id, provider_order_id, symbol, side,
 *  new_price, new_qty_base, new_qty_quote
 */
function provider_update(PDO $conn, array $ctx): array {
  $tradeOrderId = (int)($ctx['trade_order_id'] ?? 0);
  $providerId   = (int)($ctx['provider_id'] ?? 0);
  $symbol       = strtoupper(trim((string)($ctx['symbol'] ?? '')));
  $side         = strtolower(trim((string)($ctx['side'] ?? 'buy')));
  $side         = ($side === 'sell') ? 'sell' : 'buy';

  $newPrice     = yc_clean_decimal_str($ctx['new_price'] ?? '0', false);
  $newQtyBase   = yc_clean_decimal_str($ctx['new_qty_base'] ?? '0', false);

  if ($tradeOrderId <= 0) return ['success'=>false,'message'=>'trade_order_id غير صحيح.'];
  if (yc_dec_cmp($newPrice,'0') <= 0) return ['success'=>false,'message'=>'سعر التعديل غير صالح.'];
  if (yc_dec_cmp($newQtyBase,'0') <= 0) return ['success'=>false,'message'=>'كمية التعديل غير صالحة.'];

  foreach (['trade_orders','providers'] as $t) {
    if (!yc_table_exists($conn,$t)) return ['success'=>false,'message'=>"جداول النظام غير مكتملة: {$t}"];
  }

  // اقرأ الطلب (غالباً مقفول FOR UPDATE من update_trade_order.php)
  $stO = $conn->prepare("SELECT * FROM trade_orders WHERE id=:id LIMIT 1");
  $stO->execute([':id'=>$tradeOrderId]);
  $o = $stO->fetch(PDO::FETCH_ASSOC);
  if (!$o) return ['success'=>false,'message'=>'الطلب غير موجود.'];

  $status = strtolower((string)($o['status'] ?? ''));
  if ($status !== 'pending') {
    return ['success'=>false,'message'=>'لا يمكن تعديل الطلب لأن حالته ليست pending.', 'status'=>$status];
  }

  $orderType = strtolower((string)($o['order_type'] ?? ''));
  if ($orderType !== 'limit') {
    return ['success'=>false,'message'=>'provider_update يدعم LIMIT فقط.', 'order_type'=>$orderType];
  }

  if ($providerId <= 0) $providerId = (int)($o['provider_id'] ?? 0);
  if ($symbol === '') $symbol = strtoupper((string)($o['symbol'] ?? ''));

  $providerOrderId = (string)($ctx['provider_order_id'] ?? '');
  if ($providerOrderId === '') $providerOrderId = (string)($o['provider_order_id'] ?? '');
  if ($providerOrderId === '') $providerOrderId = yc_extract_order_id_from_transactions($conn, $tradeOrderId);

  if ($providerOrderId === '') {
    return ['success'=>false,'message'=>'لا يمكن تعديل الطلب لأن provider_order_id غير متوفر.'];
  }

  // بيانات المزود
  $stPr = $conn->prepare("SELECT * FROM providers WHERE id=:id AND is_active=1 LIMIT 1");
  $stPr->execute([':id'=>$providerId]);
  $prov = $stPr->fetch(PDO::FETCH_ASSOC);
  if (!$prov) return ['success'=>false,'message'=>'بيانات المزود غير موجودة.'];

  $apiKey    = (string)($prov['username'] ?? '');
  $apiSecret = (string)($prov['password'] ?? '');
  $apiUrl    = rtrim((string)($prov['api_url'] ?? ''), '/');

  if ($apiKey==='' || $apiSecret==='' || $apiUrl==='') {
    return ['success'=>false,'message'=>'بيانات المزود غير مكتملة.'];
  }

  $headers = [
    "x-mexc-apikey: {$apiKey}",
    "Content-Type: application/json"
  ];

  // filters
  $filters = yc_mexc_get_exchange_filters($apiUrl, $headers, $symbol);
  $tickSize    = $filters['tickSize'] ?? '0';
  $stepSize    = $filters['stepSize'] ?? '0';
  $minNotional = $filters['minNotional'] ?? '0';
  $minQty      = $filters['minQty'] ?? '0';
  $maxQty      = $filters['maxQty'] ?? '0';

  // ✅ في التعديل: لا نرفع الكمية تلقائياً (لتجنب تجاوز الحجز) -> نعمل FLOOR للـ step
  if (yc_dec_cmp($tickSize,'0') > 0) $newPrice = yc_floor_to_step($newPrice, $tickSize, 18);
  if (yc_dec_cmp($stepSize,'0') > 0) $newQtyBase = yc_floor_to_step($newQtyBase, $stepSize, 18);

  if (yc_dec_cmp($minQty,'0') > 0 && yc_dec_cmp($newQtyBase, $minQty) < 0) {
    return ['success'=>false,'message'=>"الكمية أقل من الحد الأدنى (minQty={$minQty}).", 'filters'=>$filters];
  }
  if (yc_dec_cmp($maxQty,'0') > 0 && yc_dec_cmp($newQtyBase, $maxQty) > 0) {
    return ['success'=>false,'message'=>"الكمية أعلى من الحد الأقصى (maxQty={$maxQty}).", 'filters'=>$filters];
  }
  if (yc_dec_cmp($minNotional,'0') > 0) {
    $notional = yc_dec_mul($newQtyBase, $newPrice, 18);
    if (yc_dec_cmp($notional, $minNotional) < 0) {
      return ['success'=>false,'message'=>"قيمة الأمر أقل من الحد الأدنى (minNotional={$minNotional}).", 'filters'=>$filters, 'notional'=>$notional];
    }
  }

  /* ===== 1) GET status ===== */
  $q = [
    'symbol'=>$symbol,
    'orderId'=>$providerOrderId,
    'timestamp'=>(int)round(microtime(true) * 1000),
    'recvWindow'=>5000
  ];
  $qurl = $apiUrl . '/api/v3/order?' . yc_mexc_signed_query($q, $apiSecret);
  $qcall = yc_http('GET', $qurl, $headers, null, 20);
  $qarr = is_string($qcall['resp']) ? json_decode($qcall['resp'], true) : null;

  yc_insert_tx($conn,(int)$tradeOrderId,(int)($o['user_id'] ?? 0),$providerId,'/api/v3/order','GET',$q,($qarr ?: $qcall['resp'] ?: $qcall['err']),$qcall['http'],$providerOrderId,($qcall['ok'] ? 'info':'failed'));

  if (!$qcall['ok'] || !is_array($qarr)) {
    return ['success'=>false,'message'=>'تعذر قراءة حالة الأمر من المزود قبل التعديل.', 'http_code'=>$qcall['http'], 'raw'=>$qcall['resp']];
  }

  $st = strtoupper((string)($qarr['status'] ?? ''));
  $executedQty = yc_clean_decimal_str($qarr['executedQty'] ?? '0', false);

  if ($st === 'FILLED') return ['success'=>false,'message'=>'لا يمكن تعديل الطلب لأنه منفذ (FILLED).', 'provider_status'=>$st];
  if (yc_dec_cmp($executedQty,'0') > 0) return ['success'=>false,'message'=>'لا يمكن تعديل الطلب لأنه نُفّذ جزئياً.', 'executedQty'=>$executedQty, 'provider_status'=>$st];

  if (!in_array($st, ['NEW','PARTIALLY_FILLED','PARTIALLYFILLED'], true)) {
    return ['success'=>false,'message'=>"لا يمكن تعديل الطلب لأن حالته لدى المزود: {$st}", 'provider_status'=>$st];
  }

  /* ===== 2) CANCEL ===== */
  $c = [
    'symbol'=>$symbol,
    'orderId'=>$providerOrderId,
    'timestamp'=>(int)round(microtime(true) * 1000),
    'recvWindow'=>5000
  ];
  $curl = $apiUrl . '/api/v3/order?' . yc_mexc_signed_query($c, $apiSecret);
  $ccall = yc_http('DELETE', $curl, $headers, null, 20);
  $carr = is_string($ccall['resp']) ? json_decode($ccall['resp'], true) : null;

  yc_insert_tx($conn,(int)$tradeOrderId,(int)($o['user_id'] ?? 0),$providerId,'/api/v3/order','DELETE',$c,($carr ?: $ccall['resp'] ?: $ccall['err']),$ccall['http'],$providerOrderId,($ccall['ok'] ? 'info':'failed'));

  if (!$ccall['ok']) {
    return ['success'=>false,'message'=>'فشل إلغاء الطلب القديم لدى المزود.', 'http_code'=>$ccall['http'], 'raw'=>$ccall['resp']];
  }

  // بعض الردود قد لا تكون JSON، نعتبر الإلغاء ناجح إذا HTTP OK
  // ويمكن التحقق لاحقاً، لكن نكمل.

  /* ===== 3) RECREATE new LIMIT ===== */
  $newClientOrderId = 'EBT_UPD_'.$tradeOrderId.'_'.(int)round(microtime(true) * 1000);

  $p = [
    'symbol'     => $symbol,
    'side'       => strtoupper($side),
    'type'       => 'LIMIT',
    'timeInForce'=> 'GTC',
    'price'      => $newPrice,
    'quantity'   => $newQtyBase,
    'timestamp'  => (int)round(microtime(true) * 1000),
    'recvWindow' => 5000,
    'newClientOrderId' => $newClientOrderId
  ];

  $purl = $apiUrl . '/api/v3/order?' . yc_mexc_signed_query($p, $apiSecret);
  $pcall = yc_http('POST', $purl, $headers, json_encode(new stdClass()), 20);
  $parr = is_string($pcall['resp']) ? json_decode($pcall['resp'], true) : null;

  $newProviderOrderId = is_array($parr) ? (string)($parr['orderId'] ?? '') : '';
  $newClientId = is_array($parr) ? (string)($parr['clientOrderId'] ?? $parr['origClientOrderId'] ?? $newClientOrderId) : $newClientOrderId;

  yc_insert_tx($conn,(int)$tradeOrderId,(int)($o['user_id'] ?? 0),$providerId,'/api/v3/order','POST',$p,($parr ?: $pcall['resp'] ?: $pcall['err']),$pcall['http'],($newProviderOrderId !== '' ? $newProviderOrderId : null),($pcall['ok'] ? 'info':'failed'));

  if (!$pcall['ok']) {
    return ['success'=>false,'message'=>'تم إلغاء الطلب القديم لكن فشل إنشاء الطلب الجديد لدى المزود.', 'http_code'=>$pcall['http'], 'raw'=>$pcall['resp']];
  }

  if ($newProviderOrderId === '') {
    return ['success'=>false,'message'=>'تمت محاولة التعديل لكن لم يرجع المزود orderId للطلب الجديد.', 'raw'=>$parr];
  }

  // حدّث IDs في trade_orders (إن وُجدت الأعمدة)
  yc_update_trade_order($conn, $tradeOrderId, 'pending', 'updated_on_provider', $newProviderOrderId, $newClientId);

  return [
    'success'=>true,
    'message'=>'تم تعديل الطلب لدى المزود بنجاح (Cancel + Recreate).',
    'old_provider_order_id'=>$providerOrderId,
    'provider_order_id'=>$newProviderOrderId,
    'client_order_id'=>$newClientId,
    'final_price'=>$newPrice,
    'final_qty_base'=>$newQtyBase,
    'filters_used'=>$filters
  ];
}

/* ================= Direct HTTP Call ================= */
if (!defined('EAZZYBIT_INTERNAL_CALL')) {
  require_once __DIR__ . '/../../config.php';
  if (!isset($conn) || !($conn instanceof PDO)) {
    http_response_code(500);
    echo json_encode(['success'=>false,'message'=>'اتصال قاعدة البيانات غير متوفر.'], JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES);
    exit;
  }

  $in = yc_read_input();
  $tid  = (int)($in['trade_order_id'] ?? 0);
  $pid  = (int)($in['provider_id'] ?? 0);
  $mode = (string)($in['ui_order_mode'] ?? '');

  $res = provider_execute($conn, ['trade_order_id'=>$tid,'provider_id'=>$pid,'ui_order_mode'=>$mode]);
  http_response_code(!empty($res['success']) ? 200 : 400);
  echo json_encode(['success'=>!empty($res['success']), 'message'=>(string)($res['message'] ?? ''), 'result'=>$res], JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES);
}
