<?php
/**
 * /api/get_crypto_deposit_address.php (PDO) — FINAL (One address per network_code per user)
 *
 * ✅ list: يرجّع شبكات الإيداع للأصل + عنوان المستخدم إن وجد (بدون توليد)
 * ✅ create: يولّد عنوان للشبكة المحددة ويحفظه إن لم يكن موجوداً
 * ✅ IMPORTANT: عنوان واحد لكل شبكة لكل مستخدم عبر network_code (مثال: USDT BEP20 = BNB BEP20)
 *
 * ✅ Auth: session user_id أو user_token (وأيضاً user_id + user_token)
 * ✅ إذا لم تُرسل network_id / network_code / network في list => يرجّع الشبكات فقط
 *
 * ✅ Prevent BAD_JSON:
 * - output buffering + clean any stray output
 * - shutdown handler returns JSON even on fatal
 *
 * ✅ Diagnostics:
 * - returns error_id on 500
 * - logs full error to: /api/logs/yc_deposit_api.log (auto-create dir)
 */

declare(strict_types=1);

if (session_status() === PHP_SESSION_NONE) session_start();
if (ob_get_level() === 0) ob_start();

/* ================= DEBUG ================= */
$DEBUG_MODE = false; // اجعله true مؤقتاً فقط عند الحاجة
$GLOBALS['DEBUG_MODE'] = $DEBUG_MODE;
if ($DEBUG_MODE) { ini_set('display_errors', '1'); error_reporting(E_ALL); }
else { ini_set('display_errors', '0'); error_reporting(0); }

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

/* ================= Logging ================= */
function yc_log(string $msg): void {
  $dir = __DIR__ . '/logs';
  if (!is_dir($dir)) { @mkdir($dir, 0755, true); }
  $file = $dir . '/yc_deposit_api.log';
  @error_log('['.date('Y-m-d H:i:s').'] '.$msg.PHP_EOL, 3, $file);
}

/* ================= Emergency JSON on Fatal ================= */
register_shutdown_function(function () {
  $err = error_get_last();
  if (!$err) return;

  $fatalTypes = [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR];
  if (!in_array($err['type'], $fatalTypes, true)) return;

  while (ob_get_level() > 0) { @ob_end_clean(); }

  if (!headers_sent()) header("Content-Type: application/json; charset=UTF-8");
  http_response_code(500);

  $eid = bin2hex(random_bytes(4));
  yc_log("FATAL[$eid] {$err['message']} @ {$err['file']}:{$err['line']}");

  $msg = 'حدث خطأ أثناء معالجة طلب العنوان.';
  if (!empty($GLOBALS['DEBUG_MODE'])) {
    $msg .= ' - ' . ($err['message'] ?? 'FATAL');
  }

  echo json_encode([
    'success' => false,
    'message' => $msg,
    'error_id'=> $eid,
    'fatal'   => true,
  ], JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES);
});

/* ================= Helpers ================= */
function respond(bool $success, string $message, array $extra = [], int $status = 200): void {
  if (ob_get_level() > 0) { @ob_clean(); }
  if (!headers_sent()) header("Content-Type: application/json; charset=UTF-8");
  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_token($s): string { return preg_replace('/[^a-z0-9_]/', '', strtolower(trim((string)$s))); }
function safe_int($v, int $def=0): int { return (int)($v ?? $def); }

function table_exists(PDO $db, string $table): bool {
  $st = $db->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 $db, string $table, string $col): bool {
  $st = $db->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 pick_col(PDO $db, string $table, array $candidates, string $fallback): string {
  foreach ($candidates as $c) { if (column_exists($db, $table, $c)) return $c; }
  return $fallback;
}

/* ===== wallet.env loader (FIXED for cPanel paths) ===== */
function yc_detect_home_dir(): string {
  $candidates = [];

  $envHome = getenv('HOME');
  if (is_string($envHome) && $envHome !== '') $candidates[] = $envHome;

  $srvHome = $_SERVER['HOME'] ?? '';
  if (is_string($srvHome) && $srvHome !== '') $candidates[] = $srvHome;

  $dir = __DIR__;
  if (preg_match('~^(/home/[^/]+)~', $dir, $m)) $candidates[] = $m[1];

  $candidates[] = dirname(__DIR__, 3);

  foreach ($candidates as $h) {
    $h = rtrim((string)$h, '/');
    if ($h !== '' && is_dir($h)) return $h;
  }
  return '/home';
}

function yc_load_wallet_env_inline(): array {
  static $cfg = null;
  if ($cfg !== null) return $cfg;

  $home = yc_detect_home_dir();
  $path = rtrim($home, '/') . '/.secure_env/wallet.env';

  if (!is_readable($path)) throw new RuntimeException('لا يمكن قراءة wallet.env في: ' . $path);
  $cfg = parse_ini_file($path, false, INI_SCANNER_RAW);
  if (!is_array($cfg)) throw new RuntimeException('فشل في قراءة wallet.env');

  return $cfg;
}

/* ✅ Auth resolver */
function resolveUserId(PDO $db, 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;

  $tokenCol = pick_col($db, 'user_sessions', ['user_token','token','access_token'], 'user_token');
  $hasOnline = column_exists($db, 'user_sessions', 'is_online');

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

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

/* ================= DB ================= */
require_once __DIR__ . '/../config.php';

$db = null;
if (isset($conn) && ($conn instanceof PDO)) $db = $conn;
elseif (isset($pdo) && ($pdo instanceof PDO)) $db = $pdo;
if (!$db) respond(false, 'اتصال قاعدة البيانات غير متوفر.', [], 500);

try {
  $in = read_input();

  foreach (['assets','user_sessions','user_deposit_addresses'] as $t) {
    if (!table_exists($db, $t)) respond(false, "جدول {$t} غير موجود.", ['table'=>$t], 500);
  }

  // networks table
  $netsTable = null;
  if (table_exists($db, 'crypto_asset_networks')) $netsTable = 'crypto_asset_networks';
  elseif (table_exists($db, 'yc_crypto_asset_networks')) $netsTable = 'yc_crypto_asset_networks';
  else respond(false, 'جدول شبكات الكريبتو غير موجود.', [], 500);

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

  // asset
  $assetCode = safe_upper($in['asset'] ?? '');
  if ($assetCode === '') respond(false, 'asset مطلوب. مثال: ?asset=USDT', [], 400);

  // action
  $action = safe_token($in['action'] ?? 'list');
  if (!in_array($action, ['list','create'], true)) $action = 'list';

  // network selection
  $networkId = safe_int($in['network_id'] ?? 0, 0);
  $networkCodeReq = safe_upper($in['network_code'] ?? '');
  if ($networkCodeReq === '') $networkCodeReq = safe_upper($in['network'] ?? '');

  // Asset row
  $stA = $db->prepare("SELECT id, code, type FROM assets WHERE code=:c LIMIT 1");
  $stA->execute([':c'=>$assetCode]);
  $asset = $stA->fetch(PDO::FETCH_ASSOC);
  if (!$asset) respond(false, 'الأصل غير موجود في assets.', ['asset'=>$assetCode], 404);

  $assetId = (int)$asset['id'];
  $assetType = strtolower((string)($asset['type'] ?? ''));
  if ($assetType !== 'crypto') respond(false, 'هذا الأصل ليس Crypto حسب جدول assets.', ['asset'=>$assetCode,'type'=>$assetType], 400);

  // columns (networks)
  $colName = pick_col($db, $netsTable, ['network_name','name','name_ar'], 'network_name');
  $colCode = pick_col($db, $netsTable, ['network_code','code','symbol'], 'network_code');
  $colMin  = pick_col($db, $netsTable, ['min_deposit','min_amount','min_deposit_amount'], 'min_deposit');
  $hasActive = column_exists($db, $netsTable, 'is_active');
  $hasSort   = column_exists($db, $netsTable, 'sort_order');

  // columns (addresses)
  $addrTable = 'user_deposit_addresses';
  $addrHasActive = column_exists($db, $addrTable, 'is_active');

  $colAddr  = pick_col($db, $addrTable, ['address'], 'address');
  $colDIdx  = pick_col($db, $addrTable, ['derivation_index'], 'derivation_index');
  $colDPath = pick_col($db, $addrTable, ['derivation_path','path'], 'derivation_path');
  $colMemo  = pick_col($db, $addrTable, ['memo','tag','destination_tag'], 'memo');
  $addrHasMemo = column_exists($db, $addrTable, $colMemo);

  // ✅ شرطنا الأساسي: network_code موجود في جدول العناوين
  $addrNetCodeCol = pick_col($db, $addrTable, ['network_code','net_code'], 'network_code');
  if (!column_exists($db, $addrTable, $addrNetCodeCol)) {
    respond(false, 'يلزم إضافة عمود network_code في user_deposit_addresses لتوحيد العنوان لكل شبكة.', [
      'required_sql' => 'ALTER TABLE user_deposit_addresses ADD COLUMN network_code VARCHAR(32) NOT NULL;'
    ], 500);
  }

  // Networks + LEFT JOIN address BY network_code (NOT network_id)
  $sql = "
    SELECT
      n.id AS id,
      n.{$colName} AS network_name,
      n.{$colCode} AS network_code,
      n.{$colMin}  AS min_deposit,
      d.{$colAddr}  AS address,
      d.{$colDIdx}  AS derivation_index,
      d.{$colDPath} AS derivation_path,
      ".($addrHasMemo ? "d.{$colMemo} AS memo" : "'' AS memo")."
    FROM {$netsTable} n
    LEFT JOIN {$addrTable} d
      ON d.{$addrNetCodeCol} = n.{$colCode}
     AND d.user_id = :uid
     ".($addrHasActive ? " AND d.is_active=1 " : "")."
    WHERE n.asset_id = :aid
    ".($hasActive ? " AND n.is_active=1 " : "")."
  ";
  $sql .= ($hasSort ? " ORDER BY n.sort_order ASC, n.id ASC " : " ORDER BY n.id ASC ");

  $st = $db->prepare($sql);
  $st->execute([':uid'=>$userId, ':aid'=>$assetId]);
  $rows = $st->fetchAll(PDO::FETCH_ASSOC) ?: [];
  if (!$rows) respond(false, 'لا توجد شبكات مضافة لهذا الأصل.', ['asset'=>$assetCode], 404);

  $netOut = array_map(function($r) {
    $addr = (string)($r['address'] ?? '');
    return [
      'id' => (int)$r['id'],
      'network_name' => (string)($r['network_name'] ?? ''),
      'network_code' => safe_upper((string)($r['network_code'] ?? '')),
      'min_deposit'  => (string)($r['min_deposit'] ?? ''),
      'address'      => $addr,
      'memo'         => (string)($r['memo'] ?? ''),
      'has_address'  => ($addr !== ''),
      // ✅ إخفاء معلومات الاشتقاق عن العميل
      'derivation_index' => null,
      'derivation_path'  => ''
    ];
  }, $rows);

  // Resolve selected network
  $hasSelection = ($networkId > 0 || $networkCodeReq !== '');
  $selected = null;

  if ($hasSelection) {
    if ($networkId > 0) {
      foreach ($rows as $r) { if ((int)$r['id'] === $networkId) { $selected = $r; break; } }
    } else {
      foreach ($rows as $r) { if (safe_upper((string)($r['network_code'] ?? '')) === $networkCodeReq) { $selected = $r; break; } }
    }

    if (!$selected) {
      respond(false, 'الشبكة المطلوبة غير موجودة لهذا الأصل.', [
        'asset' => ['id'=>$assetId, 'code'=>$assetCode],
        'networks' => $netOut,
        'selected_network' => null,
        'address' => ['exists'=>false,'is_new'=>false,'address'=>'','memo'=>'']
      ], 404);
    }
  }

  // ===== LIST =====
  if ($action === 'list') {
    if (!$hasSelection) {
      respond(true, 'ok', [
        'asset' => ['id'=>$assetId, 'code'=>$assetCode],
        'networks' => $netOut,
        'selected_network' => null,
        'address' => ['exists'=>false,'is_new'=>false,'address'=>'','memo'=>'']
      ], 200);
    }

    $selAddr = (string)($selected['address'] ?? '');
    $selMemo = (string)($selected['memo'] ?? '');

    respond(true, 'ok', [
      'asset' => ['id'=>$assetId, 'code'=>$assetCode],
      'networks' => $netOut,
      'selected_network' => [
        'id'=>(int)$selected['id'],
        'network_name'=>(string)($selected['network_name'] ?? ''),
        'network_code'=>safe_upper((string)($selected['network_code'] ?? '')),
        'min_deposit'=>(string)($selected['min_deposit'] ?? ''),
      ],
      'address' => [
        'exists' => ($selAddr !== ''),
        'is_new' => false,
        'address' => $selAddr,
        'memo' => $selMemo,
      ]
    ], 200);
  }

  // ===== CREATE =====
  if (!$hasSelection) respond(false, 'يجب تحديد الشبكة لإنشاء عنوان (network_id أو network_code أو network).', [], 400);

  // ✅ لا نحمل hd_wallet_core إلا هنا
  $walletCore = __DIR__ . '/hd_wallet_core.php';
  if (!is_readable($walletCore)) throw new RuntimeException('hd_wallet_core.php غير موجود/غير قابل للقراءة: '.$walletCore);
  require_once $walletCore; // eth_wallet()

  // ✅ تأكيد وجود GMP (مهم لكود الاشتقاق)
  if (!function_exists('gmp_init')) {
    throw new RuntimeException('PHP extension GMP غير مفعّل على السيرفر (مطلوب لتوليد HD Wallet).');
  }

  $selNetId   = (int)$selected['id'];
  $selNetCode = safe_upper((string)($selected['network_code'] ?? ''));
  if ($selNetCode === '') respond(false, 'network_code غير متوفر لهذه الشبكة.', [], 500);

  // إذا موجود عنوان مسبقاً عبر JOIN
  $selAddr = (string)($selected['address'] ?? '');
  if ($selAddr !== '') {
    respond(true, 'ok', [
      'asset' => ['id'=>$assetId, 'code'=>$assetCode],
      'networks' => $netOut,
      'selected_network' => [
        'id'=>$selNetId,
        'network_name'=>(string)($selected['network_name'] ?? ''),
        'network_code'=>$selNetCode,
        'min_deposit'=>(string)($selected['min_deposit'] ?? ''),
      ],
      'address' => [
        'exists'=>true,'is_new'=>false,'address'=>$selAddr,
        'memo'=>(string)($selected['memo'] ?? ''),
      ]
    ], 200);
  }

  $db->beginTransaction();

  // 1) Check existing by (user_id + network_code) FOR UPDATE
  $sqlCheck = "SELECT id, {$colAddr} AS address, {$colDIdx} AS derivation_index, {$colDPath} AS derivation_path
               FROM {$addrTable}
               WHERE user_id=:uid AND {$addrNetCodeCol}=:ncode ".($addrHasActive ? " AND is_active=1 " : "")."
               LIMIT 1 FOR UPDATE";
  $stC = $db->prepare($sqlCheck);
  $stC->execute([':uid'=>$userId, ':ncode'=>$selNetCode]);
  $ex = $stC->fetch(PDO::FETCH_ASSOC);

  if ($ex && !empty($ex['address'])) {
    $db->commit();
    respond(true, 'ok', [
      'asset' => ['id'=>$assetId, 'code'=>$assetCode],
      'selected_network' => [
        'id'=>$selNetId,
        'network_name'=>(string)($selected['network_name'] ?? ''),
        'network_code'=>$selNetCode,
        'min_deposit'=>(string)($selected['min_deposit'] ?? ''),
      ],
      'address' => [
        'exists'=>true,'is_new'=>false,'address'=>(string)$ex['address'],
        'memo'=>'',
      ]
    ], 200);
  }

  // 2) Next derivation index (start at 3)
  if (!column_exists($db, $addrTable, $colDIdx)) throw new RuntimeException("عمود {$colDIdx} غير موجود.");

  $START_INDEX = 3;

  $stMax = $db->query("SELECT {$colDIdx} AS di FROM {$addrTable} ORDER BY {$colDIdx} DESC LIMIT 1 FOR UPDATE");
  $mr = $stMax ? $stMax->fetch(PDO::FETCH_ASSOC) : null;
  $nextIndex = ($mr && $mr['di'] !== null) ? ((int)$mr['di'] + 1) : $START_INDEX;
  if ($nextIndex < $START_INDEX) $nextIndex = $START_INDEX;

  // 3) Wallet config + generate
  $walletCfg = yc_load_wallet_env_inline();
  $mnemonic  = trim((string)($walletCfg['WALLET_MNEMONIC'] ?? ''));
  $basePath  = trim((string)($walletCfg['WALLET_DERIVATION_PATH'] ?? "m/44'/60'/0'/0"));
  if ($mnemonic === '') throw new RuntimeException('WALLET_MNEMONIC غير مهيأ داخل wallet.env');

  $wallet = null;
  try {
    $rf = new ReflectionFunction('eth_wallet');
    $wallet = ($rf->getNumberOfParameters() >= 3)
      ? eth_wallet($mnemonic, $nextIndex, $basePath)
      : eth_wallet($mnemonic, $nextIndex);
  } catch (Throwable $e) {
    $wallet = eth_wallet($mnemonic, $nextIndex);
  }

  $address = (string)($wallet['address'] ?? '');
  $dPath   = (string)($wallet['path'] ?? '');
  if ($address === '' || $dPath === '') throw new RuntimeException('فشل توليد عنوان من eth_wallet().');

  // 4) Insert (store both network_code + (اختياري) network_id للتتبع فقط)
  $fields = ['user_id', $addrNetCodeCol, $colDIdx, $colDPath, $colAddr];
  $values = [':uid', ':ncode', ':didx', ':dpath', ':addr'];
  $bind   = [':uid'=>$userId, ':ncode'=>$selNetCode, ':didx'=>$nextIndex, ':dpath'=>$dPath, ':addr'=>$address];

  // optional: if network_id column exists in table, store selected net id (للتتبع فقط)
  if (column_exists($db, $addrTable, 'network_id')) {
    $fields[] = 'network_id';
    $values[] = ':nid';
    $bind[':nid'] = $selNetId;
  }

  if ($addrHasActive) { $fields[]='is_active'; $values[]='1'; }
  if (column_exists($db, $addrTable, 'created_at')) { $fields[]='created_at'; $values[]='CURRENT_TIMESTAMP'; }
  if (column_exists($db, $addrTable, 'updated_at')) { $fields[]='updated_at'; $values[]='CURRENT_TIMESTAMP'; }

  $sqlIns = "INSERT INTO {$addrTable} (".implode(',', $fields).") VALUES (".implode(',', $values).")";
  $stI = $db->prepare($sqlIns);
  $stI->execute($bind);

  $db->commit();

  // reload networks output (so UI sees address for this asset)
  $st2 = $db->prepare($sql);
  $st2->execute([':uid'=>$userId, ':aid'=>$assetId]);
  $rows2 = $st2->fetchAll(PDO::FETCH_ASSOC) ?: [];
  $netOut2 = array_map(function($r) {
    $addr = (string)($r['address'] ?? '');
    return [
      'id' => (int)$r['id'],
      'network_name' => (string)($r['network_name'] ?? ''),
      'network_code' => safe_upper((string)($r['network_code'] ?? '')),
      'min_deposit'  => (string)($r['min_deposit'] ?? ''),
      'address'      => $addr,
      'memo'         => (string)($r['memo'] ?? ''),
      'has_address'  => ($addr !== ''),
      'derivation_index' => null,
      'derivation_path'  => ''
    ];
  }, $rows2);

  respond(true, 'ok', [
    'asset' => ['id'=>$assetId, 'code'=>$assetCode],
    'networks' => $netOut2,
    'selected_network' => [
      'id'=>$selNetId,
      'network_name'=>(string)($selected['network_name'] ?? ''),
      'network_code'=>$selNetCode,
      'min_deposit'=>(string)($selected['min_deposit'] ?? ''),
    ],
    'address' => [
      'exists'=>true,'is_new'=>true,'address'=>$address,'memo'=>''
    ]
  ], 200);

} catch (Throwable $e) {
  if (isset($db) && ($db instanceof PDO) && $db->inTransaction()) $db->rollBack();

  $eid = bin2hex(random_bytes(4));
  yc_log("EX[$eid] ".$e->getMessage()." | URI=".($_SERVER['REQUEST_URI'] ?? '')." | IP=".($_SERVER['REMOTE_ADDR'] ?? ''));

  $msg = 'حدث خطأ أثناء معالجة طلب العنوان.';
  if (!empty($GLOBALS['DEBUG_MODE'])) $msg .= ' - ' . $e->getMessage();

  respond(false, $msg, [
    'error_id' => $eid
  ], 500);
}
