<?php
/**
 * providers/mexico.php (EazzyBit)
 * ✅ يقرأ بيانات المزود من providers (username=API_KEY, password=API_SECRET, api_url)
 * ✅ يسجل في transactions
 * ✅ يحدّث orders.status => completed | canceled
 * ✅ يفك الحجز من locked (وعند الإلغاء يرجّع للـ available)
 *
 * الاستخدام:
 * 1) استدعاء داخلي من create_order.php عبر include + provider_execute($conn, ['order_id'=>..,'provider_id'=>..])
 * 2) استدعاء مباشر (HTTP): POST JSON { "order_id": 123, "provider_id": 1 }
 */

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_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 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();
}

function yc_clean_decimal_str($v, bool $allowSign=false): string {
  $s = trim((string)$v);
  if ($s === '') return '0';

  $sign = '';
  if ($allowSign && ($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);
    $b = substr($b, 0, 18);
    $s = $a . ($b !== '' ? '.' . $b : '');
  }

  $s = rtrim(rtrim($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_translate_mexico_msg(string $msg): string {
  $map = [
    'Insufficient balance'          => 'الرصيد غير كافٍ لتنفيذ العملية.',
    'Invalid address'               => 'العنوان غير صالح.',
    'Withdrawal address error'      => 'العنوان غير صالح أو خطأ في عنوان السحب.',
    'Network not supported'         => 'الشبكة غير مدعومة.',
    'Amount too low'                => 'المبلغ أقل من الحد الأدنى.',
    'Signature verification failed' => 'فشل التحقق من التوقيع.',
    'Too many requests'             => 'طلبات كثيرة خلال وقت قصير.',
  ];
  foreach ($map as $en=>$ar) {
    if (stripos($msg, $en) !== false) return $ar;
  }
  return $msg;
}

/**
 * الدالة القياسية التي يستدعيها create_order.php
 * @return array {success(bool), status(completed|canceled), message, txid?, http_code?, raw?}
 */
function provider_execute(PDO $conn, array $ctx): array {
  $orderId    = isset($ctx['order_id']) ? (int)$ctx['order_id'] : 0;
  $providerId = isset($ctx['provider_id']) ? (int)$ctx['provider_id'] : 0;

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

  // تحقق من الجداول الأساسية
  if (!yc_table_exists($conn, 'orders') || !yc_table_exists($conn, 'providers') || !yc_table_exists($conn, 'ledger_entries')) {
    return ['success'=>false, 'status'=>'canceled', 'message'=>'جداول النظام غير مكتملة.'];
  }

  // سنحتاج جدول transactions اختياري
  $hasTransactions = yc_table_exists($conn, 'transactions');

  try {
    $conn->beginTransaction();

    // اقفل الطلب لمنع المعالجة المزدوجة
    $stO = $conn->prepare("SELECT * FROM orders WHERE id=:id FOR UPDATE");
    $stO->execute([':id'=>$orderId]);
    $order = $stO->fetch(PDO::FETCH_ASSOC);

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

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

    $userId    = (int)$order['user_id'];
    $assetCode = strtoupper((string)$order['asset_code']);
    $amountStr = yc_clean_decimal_str($order['amount'] ?? '0', false);
    $feeStr    = yc_clean_decimal_str($order['fee_amount'] ?? '0', false);
    $debitStr  = yc_dec_add($amountStr, $feeStr, 18);
    $target    = (string)($order['target'] ?? '');
    $networkName = (string)($order['route_name'] ?? ''); // مثل TRC20 / ERC20

    // جلب asset_id
    $stA = $conn->prepare("SELECT id FROM assets WHERE code=:c LIMIT 1");
    $stA->execute([':c'=>$assetCode]);
    $assetId = (int)($stA->fetchColumn() ?: 0);
    if ($assetId <= 0) {
      // إلغاء + إرجاع الحجز
      $reason = 'الأصل غير موجود في جدول assets.';
      // refund: locked -= debit, available += debit
      $note1 = "Refund available {$assetCode} (order #{$orderId})";
      $note2 = "Unlock locked {$assetCode} (order #{$orderId})";
      yc_insert_ledger($conn, $userId, $assetId, $debitStr,  'available', 'withdraw', $orderId, 'posted', $note1);
      yc_insert_ledger($conn, $userId, $assetId, yc_dec_sub('0',$debitStr,18), 'locked',    'withdraw', $orderId, 'posted', $note2);

      yc_update_order($conn, $orderId, 'canceled', $reason, null);
      $conn->commit();
      return ['success'=>false, 'status'=>'canceled', 'message'=>$reason];
    }

    // حدد provider_id لو لم يُمرّر
    if ($providerId <= 0 && yc_table_exists($conn, 'provider_networks')) {
      $stNid = $conn->prepare("
        SELECT id FROM yc_crypto_asset_networks
        WHERE asset_code=:ac AND network_name=:nn AND op='withdraw' LIMIT 1
      ");
      $stNid->execute([':ac'=>$assetCode, ':nn'=>$networkName]);
      $nid = (int)($stNid->fetchColumn() ?: 0);

      if ($nid > 0) {
        $stPN = $conn->prepare("SELECT provider_id FROM provider_networks WHERE network_id=:nid AND status='active' LIMIT 1");
        $stPN->execute([':nid'=>$nid]);
        $providerId = (int)($stPN->fetchColumn() ?: 0);
      }
    }

    // جلب بيانات المزود
    $stP = $conn->prepare("SELECT * FROM providers WHERE id=:id AND is_active=1 LIMIT 1");
    $stP->execute([':id'=>$providerId]);
    $provider = $stP->fetch(PDO::FETCH_ASSOC);

    if (!$provider) {
      // لا يوجد مزود: ألغِ وارجع الحجز
      $reason = 'لا يوجد مزود نشط لهذه العملية.';
      yc_insert_ledger($conn, $userId, $assetId, $debitStr,  'available', 'withdraw', $orderId, 'posted', "Refund available {$assetCode} (order #{$orderId})");
      yc_insert_ledger($conn, $userId, $assetId, yc_dec_sub('0',$debitStr,18), 'locked', 'withdraw', $orderId, 'posted', "Unlock locked {$assetCode} (order #{$orderId})");
      yc_update_order($conn, $orderId, 'canceled', $reason, null);

      if ($hasTransactions) {
        yc_insert_tx($conn, $orderId, $userId, $providerId, 'api', null, 'POST',
          json_encode(['error'=>$reason], JSON_UNESCAPED_UNICODE),
          $amountStr, null, null, null, 'failed'
        );
      }

      $conn->commit();
      return ['success'=>false, 'status'=>'canceled', 'message'=>$reason];
    }

    // بيانات API (حسب جدول providers)
    $apiKey    = (string)($provider['username'] ?? ''); // API KEY
    $apiSecret = (string)($provider['password'] ?? ''); // API SECRET
    $apiUrl    = rtrim((string)($provider['api_url'] ?? ''), '/');

    if ($apiKey === '' || $apiSecret === '' || $apiUrl === '') {
      $reason = 'بيانات المزود غير مكتملة (api_url/username/password).';
      // refund
      yc_insert_ledger($conn, $userId, $assetId, $debitStr,  'available', 'withdraw', $orderId, 'posted', "Refund available {$assetCode} (order #{$orderId})");
      yc_insert_ledger($conn, $userId, $assetId, yc_dec_sub('0',$debitStr,18), 'locked', 'withdraw', $orderId, 'posted', "Unlock locked {$assetCode} (order #{$orderId})");
      yc_update_order($conn, $orderId, 'canceled', $reason, null);
      $conn->commit();
      return ['success'=>false, 'status'=>'canceled', 'message'=>$reason];
    }

    /* ================= Call Provider ================= */
    $endpoint  = '/api/v3/capital/withdraw';
    $timestamp = (int)round(microtime(true) * 1000);

    $params = [
      'coin'       => $assetCode,
      'amount'     => $amountStr,
      'address'    => $target,
      'netWork'    => $networkName,
      'walletType' => 'SPOT',
      'timestamp'  => $timestamp,
      'recvWindow' => 5000
    ];

    ksort($params);
    $queryString = http_build_query($params);
    $signature = hash_hmac('sha256', $queryString, $apiSecret);

    // لا نسجل signature في transactions (تسجيل آمن)
    $logRequestData = $params;

    $params['signature'] = $signature;

    $fullUrl = $apiUrl . $endpoint . '?' . http_build_query($params);

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

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $fullUrl);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(new stdClass())); // Body فارغ لتفادي تكرار البيانات
    curl_setopt($ch, CURLOPT_TIMEOUT, 20);

    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $curlErr  = curl_error($ch);
    curl_close($ch);

    // سجل في transactions
    if ($hasTransactions) {
      yc_insert_tx(
        $conn,
        $orderId,
        $userId,
        (int)$provider['id'],
        'api',
        $endpoint,
        'POST',
        json_encode($logRequestData, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES),
        $amountStr,
        $response ?: $curlErr,
        ($httpCode ?: null),
        null,
        $curlErr ? 'failed' : 'info'
      );
    }

    if ($curlErr) {
      // فشل اتصال => canceled + refund
      $reason = 'فشل الاتصال بالمزود: ' . $curlErr;
      yc_insert_ledger($conn, $userId, $assetId, $debitStr,  'available', 'withdraw', $orderId, 'posted', "Refund available {$assetCode} (order #{$orderId})");
      yc_insert_ledger($conn, $userId, $assetId, yc_dec_sub('0',$debitStr,18), 'locked', 'withdraw', $orderId, 'posted', "Unlock locked {$assetCode} (order #{$orderId})");
      yc_update_order($conn, $orderId, 'canceled', $reason, null);
      $conn->commit();
      return ['success'=>false,'status'=>'canceled','message'=>$reason,'http_code'=>$httpCode];
    }

    $respArr = json_decode((string)$response, true);
    if (!is_array($respArr)) {
      $reason = 'استجابة غير صالحة من المزود.';
      yc_insert_ledger($conn, $userId, $assetId, $debitStr,  'available', 'withdraw', $orderId, 'posted', "Refund available {$assetCode} (order #{$orderId})");
      yc_insert_ledger($conn, $userId, $assetId, yc_dec_sub('0',$debitStr,18), 'locked', 'withdraw', $orderId, 'posted', "Unlock locked {$assetCode} (order #{$orderId})");
      yc_update_order($conn, $orderId, 'canceled', $reason, null);
      $conn->commit();
      return ['success'=>false,'status'=>'canceled','message'=>$reason,'http_code'=>$httpCode,'raw'=>$response];
    }

    $code = $respArr['code'] ?? null;
    $msg  = (string)($respArr['msg'] ?? '');
    $txid = (string)($respArr['id'] ?? ($respArr['data']['id'] ?? ''));

    // نجاح مرن
    $isOk = false;
    if ($httpCode >= 200 && $httpCode < 300) {
      if ($code === null) $isOk = true;
      else $isOk = ((int)$code === 200 || (int)$code === 0);
    }

    if (!$isOk) {
      $tmsg = yc_translate_mexico_msg($msg ?: 'خطأ غير معروف من المزود.');
      $reason = 'فشل التنفيذ: ' . $tmsg;

      // canceled + refund
      yc_insert_ledger($conn, $userId, $assetId, $debitStr,  'available', 'withdraw', $orderId, 'posted', "Refund available {$assetCode} (order #{$orderId})");
      yc_insert_ledger($conn, $userId, $assetId, yc_dec_sub('0',$debitStr,18), 'locked', 'withdraw', $orderId, 'posted', "Unlock locked {$assetCode} (order #{$orderId})");
      yc_update_order($conn, $orderId, 'canceled', $reason, null);

      // تحديث transactions إلى failed + txid إن وجد
      if ($hasTransactions && $txid !== '') {
        yc_update_last_tx_txid_status($conn, $orderId, $txid, 'failed', $httpCode);
      } elseif ($hasTransactions) {
        yc_update_last_tx_txid_status($conn, $orderId, null, 'failed', $httpCode);
      }

      $conn->commit();
      return ['success'=>false,'status'=>'canceled','message'=>$tmsg,'http_code'=>$httpCode,'txid'=>$txid,'raw'=>$respArr];
    }

    // ✅ completed: locked -= debit (فك الحجز/إنقاص locked)
    yc_insert_ledger($conn, $userId, $assetId, yc_dec_sub('0',$debitStr,18), 'locked', 'withdraw', $orderId, 'posted', "Withdraw completed {$assetCode} via {$networkName} (order #{$orderId})");

    yc_update_order($conn, $orderId, 'completed', 'تم تنفيذ السحب بنجاح.', ($txid !== '' ? $txid : null));

    if ($hasTransactions) {
      yc_update_last_tx_txid_status($conn, $orderId, ($txid !== '' ? $txid : null), 'success', $httpCode);
    }

    $conn->commit();
    return ['success'=>true,'status'=>'completed','message'=>'تم تنفيذ الطلب بنجاح.','http_code'=>$httpCode,'txid'=>$txid,'raw'=>$respArr];

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

/* ================= DB helpers (ledger/order/tx) ================= */
function yc_insert_ledger(PDO $conn, int $userId, int $assetId, string $amount, string $balanceType, string $refType, int $refId, string $status, string $note): void {
  // بعض الجداول قد لا تحتوي created_at (نراعي ذلك)
  $hasCreated = yc_col_exists($conn, 'ledger_entries', 'created_at');

  $cols = ['user_id','asset_id','amount','balance_type','ref_type','ref_id','status','note'];
  $vals = [':uid',':aid',':amt',':bt',':rt',':rid',':st',':note'];

  if ($hasCreated) { $cols[]='created_at'; $vals[]='NOW()'; }

  $sql = "INSERT INTO ledger_entries (".implode(',',$cols).") VALUES (".implode(',',$vals).")";
  $st = $conn->prepare($sql);
  $st->execute([
    ':uid'=>$userId, ':aid'=>$assetId, ':amt'=>$amount,
    ':bt'=>$balanceType, ':rt'=>$refType, ':rid'=>$refId,
    ':st'=>$status, ':note'=>$note
  ]);
}

function yc_update_order(PDO $conn, int $orderId, string $status, string $reason, ?string $txHash): void {
  $hasReason = yc_col_exists($conn, 'orders', 'status_reason');
  $hasTxHash = yc_col_exists($conn, 'orders', 'tx_hash');

  $set = ["status=:st", "updated_at=NOW()"];
  $params = [':st'=>$status, ':id'=>$orderId];

  if ($hasReason) { $set[]="status_reason=:rs"; $params[':rs']=$reason; }
  if ($hasTxHash) { $set[]="tx_hash=:tx"; $params[':tx']=$txHash; }

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

function yc_insert_tx(PDO $conn, int $orderId, int $userId, int $providerId, string $type, ?string $endpoint, string $reqType, ?string $reqData, string $amount, ?string $respData, ?int $statusCode, ?string $txid, string $status): void {
  $st = $conn->prepare("
    INSERT INTO transactions
      (order_id,user_id,provider_id,type,endpoint,request_type,request_data,amount,response_data,status_code,txid,status,created_at)
    VALUES
      (:oid,:uid,:pid,:tp,:ep,:rt,:rq,:am,:rp,:sc,:tx,:st,NOW())
  ");
  $st->execute([
    ':oid'=>$orderId,
    ':uid'=>$userId ?: null,
    ':pid'=>$providerId ?: null,
    ':tp'=>$type,
    ':ep'=>$endpoint,
    ':rt'=>$reqType,
    ':rq'=>$reqData,
    ':am'=>$amount,
    ':rp'=>$respData,
    ':sc'=>$statusCode,
    ':tx'=>$txid,
    ':st'=>$status
  ]);
}

function yc_update_last_tx_txid_status(PDO $conn, int $orderId, ?string $txid, string $status, ?int $httpCode): void {
  // حدّث آخر سجل API لنفس الطلب (حل عملي وسريع)
  $set = ["status=:st"];
  $params = [':st'=>$status, ':oid'=>$orderId];

  if ($txid !== null) { $set[]="txid=:tx"; $params[':tx']=$txid; }
  if ($httpCode !== null) { $set[]="status_code=:sc"; $params[':sc']=$httpCode; }

  $sql = "UPDATE transactions
          SET ".implode(', ', $set)."
          WHERE order_id=:oid AND type='api'
          ORDER BY id DESC
          LIMIT 1";
  $st = $conn->prepare($sql);
  $st->execute($params);
}

/* ================= Direct HTTP Call ================= */
if (!defined('EAZZYBIT_INTERNAL_CALL')) {
  require_once __DIR__ . '/../../config.php'; // نفس مكان config.php المستخدم عندك
  if (!isset($conn) || !($conn instanceof PDO)) yc_respond(false, 'اتصال قاعدة البيانات غير متوفر.', [], 500);

  $in = yc_read_input();
  $oid = (int)($in['order_id'] ?? 0);
  $pid = (int)($in['provider_id'] ?? 0);

  $res = provider_execute($conn, ['order_id'=>$oid,'provider_id'=>$pid]);

  yc_respond((bool)$res['success'], (string)$res['message'], ['result'=>$res], $res['success'] ? 200 : 400);
}
