<?php
/**
 * /api/update_trade_order.php (PDO)
 * ✅ تعديل طلب LIMIT (السعر + الكمية) مع تعديل الحجز في ledger (available <-> locked)
 * ✅ يدعم session user_id أو user_token (وأيضاً user_id+user_token)
 * ✅ يمنع تعديل الأوامر المنفذة/المغلقة/الملغاة
 * ✅ إن كان provider_order_id موجود:
 *    - يسمح بالتعديل فقط إذا كان ملف المزود يحتوي دالة provider_update()
 *    - غير ذلك يرجع خطأ "التعديل غير مدعوم" (حتى لا يحدث عدم تطابق مع المزود)
 *
 * المدخلات المتوقعة من trade.php:
 * - user_token
 * - trade_order_id | order_id | id
 * - price
 * - qty_base
 */

if (session_status() === PHP_SESSION_NONE) session_start();

/* ================= CORS ================= */
$frontendOrigin = 'https://eazzybit.com';
$reqOrigin = $_SERVER['HTTP_ORIGIN'] ?? '';
if ($reqOrigin === $frontendOrigin) {
  header("Access-Control-Allow-Origin: {$frontendOrigin}");
  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; }

/* ================= DEBUG ================= */
$DEBUG_MODE = false;
if ($DEBUG_MODE) { ini_set('display_errors', '1'); error_reporting(E_ALL); }
else { ini_set('display_errors', '0'); error_reporting(0); }

/* ================= Helpers ================= */
function respond(bool $success, string $message, array $extra = [], int $status = 200): void {
  http_response_code($status);
  echo json_encode(array_merge(['success'=>$success,'message'=>$message], $extra), JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES);
  exit;
}

function read_input(): array {
  $method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
  if ($method === 'POST') {
    $raw = file_get_contents('php://input') ?: '';
    $json = json_decode($raw, true);
    if (is_array($json)) return $json;
    return $_POST ?? [];
  }
  return $_GET ?? [];
}

function safe_upper($s): string {
  return strtoupper(preg_replace('/[^A-Z0-9_]/', '', (string)$s));
}

function safe_lower_token($s): string {
  $s = strtolower(trim((string)$s));
  $s = preg_replace('/[^a-z0-9_]/', '', $s);
  return $s;
}

/**
 * ✅ تنظيف آمن للأرقام (نفس create_trade_order.php)
 */
function 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 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 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 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 dec_div(string $a, string $b, int $scale=18): string {
  $b = 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 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 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 column_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();
}

function safe_binding_file(string $f): string {
  $f = trim($f);
  $f = preg_replace('/[^A-Za-z0-9_.-]/', '', $f);
  if ($f === '') return '';
  if (stripos($f, '.php') === false) $f .= '.php';
  if (substr($f, -4) !== '.php') $f .= '.php';
  return $f;
}

/**
 * ✅ resolveUserId (نفس create_trade_order.php)
 */
function resolveUserId(PDO $conn, array $in): int {
  $sid = isset($_SESSION['user_id']) ? (int)$_SESSION['user_id'] : 0;
  if ($sid > 0) return $sid;

  $tok = trim((string)($in['user_token'] ?? ''));
  if ($tok === '') return 0;

  $uid = isset($in['user_id']) ? (int)$in['user_id'] : 0;

  if ($uid > 0) {
    $st = $conn->prepare("
      SELECT user_id
      FROM user_sessions
      WHERE user_id=:uid AND user_token=:tok AND is_online=1
      LIMIT 1
    ");
    $st->execute([':uid'=>$uid, ':tok'=>$tok]);
    $row = $st->fetch(PDO::FETCH_ASSOC);
    return $row ? (int)$row['user_id'] : 0;
  }

  $st = $conn->prepare("
    SELECT user_id
    FROM user_sessions
    WHERE user_token=:tok AND is_online=1
    ORDER BY id DESC
    LIMIT 1
  ");
  $st->execute([':tok'=>$tok]);
  $row = $st->fetch(PDO::FETCH_ASSOC);
  return $row ? (int)$row['user_id'] : 0;
}

/* ================= DB ================= */
require_once __DIR__ . '/../config.php';
if (!isset($conn) || !($conn instanceof PDO)) respond(false, 'اتصال قاعدة البيانات غير متوفر.', [], 500);

try {
  $in = read_input();

  foreach (['users','trade_orders','asset_pairs','assets','ledger_entries','user_sessions','providers'] as $t) {
    if (!table_exists($conn, $t)) respond(false, "جدول {$t} غير موجود.", [], 500);
  }

  $userId = resolveUserId($conn, $in);
  if ($userId <= 0) respond(false, 'غير مصرح. الرجاء تسجيل الدخول.', ['unauthorized'=>true], 401);

  /* ================= Input ================= */
  $orderId = (int)($in['trade_order_id'] ?? ($in['order_id'] ?? ($in['id'] ?? 0)));
  if ($orderId <= 0) respond(false, 'trade_order_id مطلوب.', [], 400);

  $newPrice   = clean_decimal_str($in['price'] ?? '0', false);
  $newQtyBase = clean_decimal_str($in['qty_base'] ?? '0', false);

  if (dec_cmp($newPrice,'0') <= 0) respond(false, 'price مطلوب.', [], 400);
  if (dec_cmp($newQtyBase,'0') <= 0) respond(false, 'qty_base مطلوب.', [], 400);

  /* ================= Load Order (FOR UPDATE) ================= */
  $conn->beginTransaction();

  $stO = $conn->prepare("SELECT * FROM trade_orders WHERE id=:id AND user_id=:uid LIMIT 1 FOR UPDATE");
  $stO->execute([':id'=>$orderId, ':uid'=>$userId]);
  $ord = $stO->fetch(PDO::FETCH_ASSOC);
  if (!$ord) {
    $conn->rollBack();
    respond(false, 'الطلب غير موجود.', [], 404);
  }

  if (!column_exists($conn, 'trade_orders', 'status')) {
    $conn->rollBack();
    respond(false, 'عمود status غير موجود في trade_orders.', [], 500);
  }

  $status = strtolower(trim((string)($ord['status'] ?? '')));
  $blocked = ['filled','closed','completed','done','settled','executed','canceled','cancelled','rejected','failed'];
  if (in_array($status, $blocked, true)) {
    $conn->rollBack();
    respond(false, 'لا يمكن تعديل هذا الطلب لأن حالته نهائية.', ['status'=>$status], 400);
  }

  $side = strtolower(trim((string)($ord['side'] ?? 'buy')));
  if (!in_array($side, ['buy','sell'], true)) $side = 'buy';

  $orderType = strtolower(trim((string)($ord['order_type'] ?? 'limit')));
  if ($orderType !== 'limit') {
    $conn->rollBack();
    respond(false, 'يمكن تعديل أوامر LIMIT فقط.', ['order_type'=>$orderType], 400);
  }

  $pairId = (int)($ord['pair_id'] ?? 0);
  if ($pairId <= 0) {
    $conn->rollBack();
    respond(false, 'pair_id غير صالح.', [], 500);
  }

  // pair + provider
  $stP = $conn->prepare("
    SELECT id, base_asset_id, quote_asset_id,
           COALESCE(taker_fee_pct,0) AS taker_fee_pct,
           COALESCE(maker_fee_pct,0) AS maker_fee_pct,
           COALESCE(provider_id,0)   AS provider_id,
           tv_symbol
    FROM asset_pairs
    WHERE id=:id
    LIMIT 1
  ");
  $stP->execute([':id'=>$pairId]);
  $pair = $stP->fetch(PDO::FETCH_ASSOC);
  if (!$pair) {
    $conn->rollBack();
    respond(false, 'الزوج غير موجود.', [], 500);
  }

  $symbol = safe_upper($ord['symbol'] ?? ($pair['tv_symbol'] ?? ''));
  $baseAssetId  = (int)$pair['base_asset_id'];
  $quoteAssetId = (int)$pair['quote_asset_id'];
  $providerId   = (int)$pair['provider_id'];

  // fee_rate: من الطلب أولاً، وإلا من الزوج (maker_fee_pct)
  $feeRate = clean_decimal_str($ord['fee_rate'] ?? ($pair['maker_fee_pct'] ?? '0'), false);
  if ($feeRate === '0') $feeRate = '0.001';

  // old hold info
  $oldHoldAssetId = (int)($ord['hold_asset_id'] ?? 0);
  $oldHoldAmount  = clean_decimal_str($ord['hold_amount'] ?? '0', false);

  if ($oldHoldAssetId <= 0 || dec_cmp($oldHoldAmount,'0') <= 0) {
    $conn->rollBack();
    respond(false, 'بيانات الحجز غير صالحة داخل الطلب.', [], 500);
  }

  /* ================= Recompute new qty_quote + fee + hold ================= */
  $newQtyQuote = dec_mul($newQtyBase, $newPrice, 18);
  if (dec_cmp($newQtyQuote,'0') <= 0) {
    $conn->rollBack();
    respond(false, 'qty_quote غير صالح بعد الحساب.', [], 400);
  }

  $newFeeAmount = dec_mul($newQtyQuote, $feeRate, 18);

  if ($side === 'buy') {
    $newHoldAssetId = $quoteAssetId;
    $newHoldAmount  = dec_add($newQtyQuote, $newFeeAmount, 18);
  } else {
    $newHoldAssetId = $baseAssetId;
    $newHoldAmount  = $newQtyBase; // بيع: الحجز = qty_base
  }

  if ($newHoldAssetId !== $oldHoldAssetId) {
    $conn->rollBack();
    respond(false, 'لا يمكن تغيير أصل الحجز أثناء التعديل.', [], 400);
  }

  if (dec_cmp($newHoldAmount,'0') <= 0) {
    $conn->rollBack();
    respond(false, 'hold_amount غير صالح.', [], 400);
  }

  // delta = new - old
  $delta = dec_sub($newHoldAmount, $oldHoldAmount, 18);

  /* ================= If delta > 0: check extra available ================= */
  $needExtra = (dec_cmp($delta,'0') > 0);

  if ($needExtra) {
    $hasLedgerStatus = column_exists($conn, 'ledger_entries', 'status');
    $sqlAvail = "
      SELECT COALESCE(SUM(
        CASE WHEN balance_type='available' ".($hasLedgerStatus ? "AND status='posted'" : "")." THEN amount ELSE 0 END
      ), 0) AS available
      FROM ledger_entries
      WHERE user_id=:uid AND asset_id=:aid
    ";
    $stA = $conn->prepare($sqlAvail);
    $stA->execute([':uid'=>$userId, ':aid'=>$oldHoldAssetId]);
    $availStr = clean_decimal_str($stA->fetchColumn() ?? '0', true);

    if (dec_cmp($availStr, $delta) < 0) {
      $conn->rollBack();
      respond(false, 'لا يوجد رصيد كافي لإتمام التعديل.', [
        'available'=>$availStr,
        'required_extra'=>$delta
      ], 400);
    }
  }

  /* ================= Provider hook (only if provider_order_id exists) ================= */
  $hasProviderOrderId = column_exists($conn, 'trade_orders', 'provider_order_id') && !empty($ord['provider_order_id']);
  $providerOrderId = $hasProviderOrderId ? (string)$ord['provider_order_id'] : '';

  $providerName = '';
  $bindingFile  = '';

  if ($providerId <= 0) {
    // fallback لأي مزود نشط
    $stPr0 = $conn->query("SELECT id FROM providers WHERE is_active=1 AND binding_file IS NOT NULL AND binding_file<>'' ORDER BY id ASC LIMIT 1");
    $providerId = (int)($stPr0 ? $stPr0->fetchColumn() : 0);
  }

  if ($providerId > 0) {
    $stPr = $conn->prepare("SELECT id,name,binding_file FROM providers WHERE id=:id AND is_active=1 LIMIT 1");
    $stPr->execute([':id'=>$providerId]);
    $prow = $stPr->fetch(PDO::FETCH_ASSOC);
    if ($prow) {
      $providerName = (string)$prow['name'];
      $bindingFile  = safe_binding_file((string)($prow['binding_file'] ?? ''));
    }
  }

  $providerUpdateResult = null;

  if ($hasProviderOrderId) {
    if ($bindingFile === '') {
      $conn->rollBack();
      respond(false, 'لا يمكن تعديل الطلب لأن ملف المزود غير معروف.', [], 400);
    }

    $providerPath = __DIR__ . '/providers/' . $bindingFile;
    if (!is_file($providerPath)) {
      $conn->rollBack();
      respond(false, "ملف المزود غير موجود: {$bindingFile}", [], 500);
    }

    define('EAZZYBIT_INTERNAL_CALL', 1);
    require_once $providerPath;

    // ✅ شرط أمان: لا نعدل طلب مزود بدون دعم صريح
    if (!function_exists('provider_update')) {
      $conn->rollBack();
      respond(false, 'التعديل غير مدعوم لهذا المزود حالياً. قم بإلغاء الطلب ثم إنشاء طلب جديد.', [
        'provider'=>['id'=>$providerId,'name'=>$providerName,'binding_file'=>$bindingFile]
      ], 400);
    }

    // ✅ نفّذ تحديث المزود أولاً، وإذا نجح نكمل تحديث قاعدة البيانات + ledger
    $providerUpdateResult = provider_update($conn, [
      'trade_order_id'    => $orderId,
      'provider_id'       => $providerId,
      'provider_order_id' => $providerOrderId,
      'symbol'            => $symbol,
      'side'              => $side,
      'new_price'         => $newPrice,
      'new_qty_base'      => $newQtyBase,
      'new_qty_quote'     => $newQtyQuote
    ]);

    if (empty($providerUpdateResult['success'])) {
      $conn->rollBack();
      respond(false, (string)($providerUpdateResult['message'] ?? 'فشل تحديث الطلب لدى المزود.'), [
        'provider'=>['id'=>$providerId,'name'=>$providerName,'binding_file'=>$bindingFile],
        'execution'=>$providerUpdateResult
      ], 400);
    }
  }

  /* ================= Ledger delta (available/locked) ================= */
  // إذا delta = 0 لا نضيف قيود
  if (dec_cmp($delta,'0') !== 0) {
    $hasLedgerStatus  = column_exists($conn, 'ledger_entries', 'status');
    $hasLedgerCreated = column_exists($conn, 'ledger_entries', 'created_at');

    $note = "Trade update {$symbol} ({$side}) (trade_order #{$orderId})";
    // available gets -delta, locked gets +delta (حتى لو delta سالب تشتغل صح)
    $amtAvail = dec_sub('0', $delta, 18);
    $amtLock  = $delta;

    $lcols = ['user_id','asset_id','amount','balance_type','ref_type','ref_id','note'];
    $lvals = [':uid',':aid',':amt',':bt',':rt',':rid',':note'];
    $lparBase = [
      ':uid'=>$userId,
      ':aid'=>$oldHoldAssetId,
      ':rt'=>'trade_update',
      ':rid'=>$orderId,
      ':note'=>$note
    ];

    if ($hasLedgerStatus) { $lcols[]='status'; $lvals[]=':st'; $lparBase[':st']='posted'; }
    if ($hasLedgerCreated) { $lcols[]='created_at'; $lvals[]='NOW()'; }

    $sqlL = "INSERT INTO ledger_entries (".implode(',',$lcols).") VALUES (".implode(',',$lvals).")";
    $stL = $conn->prepare($sqlL);

    // available -= delta
    $stL->execute($lparBase + [':amt'=>$amtAvail, ':bt'=>'available']);
    // locked += delta
    $stL->execute($lparBase + [':amt'=>$amtLock,  ':bt'=>'locked']);
  }

  /* ================= Update trade_orders ================= */
  $set = [];
  $par = [':id'=>$orderId, ':uid'=>$userId];

  $updMap = [
    'price'        => $newPrice,
    'client_price' => $newPrice,
    'qty_base'     => $newQtyBase,
    'qty_quote'    => $newQtyQuote,
    'fee_amount'   => $newFeeAmount,
    'hold_amount'  => $newHoldAmount,
    'status_reason'=> 'updated'
  ];

  foreach ($updMap as $c=>$v) {
    if (column_exists($conn,'trade_orders',$c)) {
      $set[] = "`{$c}` = :{$c}";
      $par[":{$c}"] = $v;
    }
  }

  // نترك status كما هو، لكن لو تحب ترجعها pending بعد التعديل:
  // if (column_exists($conn,'trade_orders','status')) { $set[]="status='pending'"; }

  if (column_exists($conn,'trade_orders','updated_at')) {
    $set[] = "updated_at = NOW()";
  }

  if (empty($set)) {
    $conn->rollBack();
    respond(false, 'لا توجد أعمدة قابلة للتحديث داخل trade_orders.', [], 500);
  }

  $sqlU = "UPDATE trade_orders SET ".implode(', ', $set)." WHERE id=:id AND user_id=:uid LIMIT 1";
  $stU = $conn->prepare($sqlU);
  $stU->execute($par);

  $conn->commit();

  // fetch final
  $stF = $conn->prepare("SELECT * FROM trade_orders WHERE id=:id AND user_id=:uid LIMIT 1");
  $stF->execute([':id'=>$orderId, ':uid'=>$userId]);
  $final = $stF->fetch(PDO::FETCH_ASSOC) ?: null;

  respond(true, 'تم تعديل الطلب بنجاح.', [
    'trade_order_id' => $orderId,
    'order' => $final,
    'delta_hold' => $delta,
    'provider' => ['id'=>$providerId,'name'=>$providerName,'binding_file'=>$bindingFile],
    'execution' => $providerUpdateResult
  ], 200);

} catch (Throwable $e) {
  if (isset($conn) && ($conn instanceof PDO) && $conn->inTransaction()) $conn->rollBack();
  $msg = 'خطأ في الخادم';
  if (!empty($GLOBALS['DEBUG_MODE'])) $msg .= ' - ' . $e->getMessage();
  respond(false, $msg, [], 500);
}
