<?php
// get_markets.php (PDO) — GET/POST
// ✅ Snapshot JSON للأسواق (fallback / تحميل أولي)
// ✅ مصدر البيانات: Binance server-side + Failover hosts
// ✅ Allow-list من DB (asset_pairs + assets.code [+ assets.icon_url]) — بدون قوائم ثابتة
// ✅ فلترة رموز العميل ضمن المسموح (إن أرسل symbols)
// ✅ كاش ملفي TTL + Stale-if-error
// ✅ Auth: Session أو user_token (user_sessions.user_token)
// ✅ PHP 7.4+

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

/* ================= 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; }

/* ================= 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 log_error(Throwable $e): void {
  $dir = __DIR__ . '/../../logs';
  if (!is_dir($dir)) @mkdir($dir, 0755, true);
  $file = $dir . '/api_errors_' . date('Y-m-d') . '.log';
  $line = "[" . date('Y-m-d H:i:s') . "] " . $e->getMessage() . " | " . $e->getFile() . ":" . $e->getLine() . PHP_EOL;
  @file_put_contents($file, $line, FILE_APPEND | LOCK_EX);
}

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($v): string { return strtoupper(trim((string)$v)); }

function ends_with(string $haystack, string $needle): bool {
  $len = strlen($needle);
  if ($len === 0) return true;
  return substr($haystack, -$len) === $needle;
}

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

/* ================= AUTH ================= */
function resolveUserIdByToken(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;

  $st = $conn->prepare("
    SELECT user_id
    FROM user_sessions
    WHERE user_token = :tok
      AND is_online = 1
    LIMIT 1
  ");
  $st->execute([':tok' => $tok]);
  $row = $st->fetch(PDO::FETCH_ASSOC);

  return $row ? (int)$row['user_id'] : 0;
}

/* ================= HTTP (Binance) ================= */
function http_get_json(string $url, int $timeoutSec = 8): array {
  $ch = curl_init($url);
  curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_CONNECTTIMEOUT => $timeoutSec,
    CURLOPT_TIMEOUT => $timeoutSec,
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_HTTPHEADER => [
      'Accept: application/json',
      'User-Agent: EazzyBit-MarketProxy/1.0'
    ],
  ]);
  $body = curl_exec($ch);
  $err  = curl_error($ch);
  $code = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
  curl_close($ch);

  if ($body === false) throw new RuntimeException("CURL_ERROR: {$err}");
  $json = json_decode($body, true);
  if (!is_array($json)) throw new RuntimeException("BAD_JSON_FROM_SOURCE");
  if ($code < 200 || $code >= 300) {
    $msg = isset($json['msg']) ? (string)$json['msg'] : "HTTP_{$code}";
    throw new RuntimeException("SOURCE_ERROR: {$msg}");
  }
  return $json;
}

function fetch_binance_24hr(array $symbols, int $timeout = 10): array {
  $symbolsJson = json_encode($symbols, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES);
  $path = '/api/v3/ticker/24hr?symbols=' . urlencode($symbolsJson);

  $hosts = [
    'https://api.binance.com',
    'https://api1.binance.com',
    'https://api2.binance.com',
    'https://api3.binance.com',
    'https://data-api.binance.vision',
  ];

  $lastErr = null;
  foreach ($hosts as $h) {
    try {
      return http_get_json($h . $path, $timeout);
    } catch (Throwable $e) {
      $lastErr = $e;
    }
  }
  throw new RuntimeException("MARKET_SOURCE_UNAVAILABLE" . ($lastErr ? (": " . $lastErr->getMessage()) : ""));
}

function fetch_binance_24hr_multi(array $symbols, int $timeout = 10, int $chunkSize = 80): array {
  $symbols = array_values($symbols);
  if (!$symbols) return [];
  if ($chunkSize < 1) $chunkSize = 80;

  $out = [];
  foreach (array_chunk($symbols, $chunkSize) as $chunk) {
    $part = fetch_binance_24hr($chunk, $timeout);
    if (is_array($part)) {
      foreach ($part as $row) $out[] = $row;
    }
  }
  return $out;
}

/* ================= Cache (file) ================= */
function cache_dir(): string {
  $dir = __DIR__ . '/../../cache';
  if (!is_dir($dir)) @mkdir($dir, 0755, true);
  return $dir;
}
function cache_file(string $key): string { return cache_dir() . '/' . $key . '.json'; }

function cache_get(string $key, int $ttlSec): ?array {
  if ($ttlSec <= 0) return null;
  $file = cache_file($key);
  if (!is_file($file)) return null;
  $mt = @filemtime($file);
  if (!$mt || (time() - $mt) > $ttlSec) return null;
  $raw = @file_get_contents($file);
  if (!$raw) return null;
  $json = json_decode($raw, true);
  return is_array($json) ? $json : null;
}

function cache_get_stale(string $key, int $maxAgeSec): ?array {
  if ($maxAgeSec <= 0) return null;
  $file = cache_file($key);
  if (!is_file($file)) return null;
  $mt = @filemtime($file);
  if (!$mt || (time() - $mt) > $maxAgeSec) return null;
  $raw = @file_get_contents($file);
  if (!$raw) return null;
  $json = json_decode($raw, true);
  return is_array($json) ? $json : null;
}

function cache_set(string $key, array $value): void {
  $file = cache_file($key);
  @file_put_contents($file, json_encode($value, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES), LOCK_EX);
}

/* ================= Allowed symbols from DB =================
   asset_pairs: base_asset_id, quote_asset_id, tv_symbol
   assets: code (+ optional: icon_url, is_active)
   - نجرب استعلام “كامل” ثم نسقط لاستعلامات أبسط إذا الأعمدة غير موجودة.
============================================================== */
function allowed_pairs_from_db(PDO $conn, string $quote, int $limit = 200): array {
  $quote = safe_upper($quote ?: 'USDT');
  $limit = (int)$limit;
  if ($limit < 1) $limit = 1;
  if ($limit > 500) $limit = 500;

  $queries = [
    // 1) كامل (icon_url + is_active)
    "
      SELECT
        ap.tv_symbol,
        b.code AS base_code,
        q.code AS quote_code,
        b.icon_url AS base_icon_url
      FROM asset_pairs ap
      JOIN assets b ON b.id = ap.base_asset_id AND b.is_active = 1
      JOIN assets q ON q.id = ap.quote_asset_id AND q.is_active = 1
      WHERE UPPER(q.code) = :quote
      ORDER BY ap.base_asset_id ASC
      LIMIT {$limit}
    ",
    // 2) بدون is_active (لكن مع icon_url)
    "
      SELECT
        ap.tv_symbol,
        b.code AS base_code,
        q.code AS quote_code,
        b.icon_url AS base_icon_url
      FROM asset_pairs ap
      JOIN assets b ON b.id = ap.base_asset_id
      JOIN assets q ON q.id = ap.quote_asset_id
      WHERE UPPER(q.code) = :quote
      ORDER BY ap.base_asset_id ASC
      LIMIT {$limit}
    ",
    // 3) بدون icon_url وبدون is_active
    "
      SELECT
        ap.tv_symbol,
        b.code AS base_code,
        q.code AS quote_code
      FROM asset_pairs ap
      JOIN assets b ON b.id = ap.base_asset_id
      JOIN assets q ON q.id = ap.quote_asset_id
      WHERE UPPER(q.code) = :quote
      ORDER BY ap.base_asset_id ASC
      LIMIT {$limit}
    ",
  ];

  $rows = [];
  $lastErr = null;

  foreach ($queries as $sql) {
    try {
      $st = $conn->prepare($sql);
      $st->execute([':quote' => $quote]);
      $rows = $st->fetchAll(PDO::FETCH_ASSOC);
      $lastErr = null;
      break;
    } catch (Throwable $e) {
      $lastErr = $e;
      continue;
    }
  }

  if ($lastErr) throw $lastErr;

  $symbols = [];
  $meta = []; // symbol => [base, quote, icon_url]

  foreach ($rows as $row) {
    $tv = safe_upper($row['tv_symbol'] ?? '');
    $bc = safe_upper($row['base_code'] ?? '');
    $qc = safe_upper($row['quote_code'] ?? '');
    $icon = trim((string)($row['base_icon_url'] ?? ''));

    $sym = $tv !== '' ? $tv : (($bc !== '' && $qc !== '') ? ($bc . $qc) : '');
    $sym = preg_replace('/[^A-Z0-9_]/', '', $sym);

    if ($sym === '') continue;
    if (!ends_with($sym, $quote)) continue;

    $symbols[] = $sym;
    $meta[$sym] = [
      'base' => $bc !== '' ? $bc : $sym,
      'quote' => $qc !== '' ? $qc : $quote,
      'icon_url' => ($icon !== '' ? $icon : null),
    ];
  }

  $symbols = array_values(array_unique($symbols));
  if (count($symbols) > 200) $symbols = array_slice($symbols, 0, 200);

  $meta2 = [];
  foreach ($symbols as $s) $meta2[$s] = $meta[$s] ?? ['base'=>null,'quote'=>$quote,'icon_url'=>null];

  return ['symbols' => $symbols, 'meta' => $meta2];
}

/* ================= Main ================= */
try {
  $in = read_input();

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

  $quote = safe_upper($in['quote'] ?? 'USDT');
  if ($quote === '') $quote = 'USDT';

  // cache_ttl (ثواني) — افتراضي 5s
  $ttlSec = isset($in['cache_ttl']) ? (int)$in['cache_ttl'] : 5;
  if ($ttlSec < 0) $ttlSec = 0;
  if ($ttlSec > 300) $ttlSec = 300;

  // stale_if_error: افتراضي 60s
  $staleIfError = isset($in['stale_if_error']) ? (int)$in['stale_if_error'] : 60;
  if ($staleIfError < 0) $staleIfError = 0;
  if ($staleIfError > 600) $staleIfError = 600;

  // ✅ Allow-list من قاعدة البيانات
  $allow = allowed_pairs_from_db($conn, $quote, 200);
  $ALLOWED = $allow['symbols'];
  $META = $allow['meta'];

  if (!$ALLOWED) {
    respond(true, 'لا توجد أزواج سوق مفعّلة في قاعدة البيانات لهذا الـ quote.', [
      'source' => 'db',
      'user_id' => $userId,
      'count' => 0,
      'items' => [],
      'ts' => time(),
      'note' => 'empty_allowlist'
    ], 200);
  }

  // symbols: JSON array أو CSV (اختياري)
  $symbolsRaw = $in['symbols'] ?? null;
  $symbols = [];
  if (is_array($symbolsRaw)) {
    $symbols = $symbolsRaw;
  } else {
    $try = json_decode((string)$symbolsRaw, true);
    if (is_array($try)) $symbols = $try;
    else {
      $csv = trim((string)$symbolsRaw);
      if ($csv !== '') $symbols = array_map('trim', explode(',', $csv));
    }
  }

  // تنظيف الرموز
  $clean = [];
  foreach ($symbols as $s) {
    $sym = preg_replace('/[^A-Z0-9_]/', '', safe_upper($s));
    if ($sym !== '') $clean[] = $sym;
  }
  $clean = array_values(array_unique($clean));
  if (count($clean) > 200) $clean = array_slice($clean, 0, 200);

  // إذا أرسل العميل symbols: فلترة ضمن المسموح
  // إذا لم يرسل: استخدم DB مباشرة
  $final = $clean
    ? array_values(array_filter($clean, fn($x)=>in_array($x, $ALLOWED, true)))
    : $ALLOWED;

  if (!$final) {
    respond(true, 'لا توجد رموز مسموحة بعد الفلترة.', [
      'source' => 'db',
      'user_id' => $userId,
      'count' => 0,
      'items' => [],
      'ts' => time(),
      'note' => 'filtered_empty'
    ], 200);
  }

  // كاش
  $cacheKey = 'markets_v5_' . md5($quote . '|' . implode(',', $final));

  $cached = cache_get($cacheKey, $ttlSec);
  if (is_array($cached) && isset($cached['items']) && is_array($cached['items'])) {
    respond(true, 'تم جلب الأسواق (كاش).', [
      'source' => 'cache',
      'user_id' => $userId,
      'count' => count($cached['items']),
      'items' => $cached['items'],
      'ts' => $cached['ts'] ?? time(),
    ]);
  }

  // مصدر السوق
  try {
    $data = fetch_binance_24hr_multi($final, 10, 80);
  } catch (Throwable $srcE) {
    $stale = cache_get_stale($cacheKey, $staleIfError);
    if (is_array($stale) && isset($stale['items']) && is_array($stale['items'])) {
      respond(true, 'تم جلب الأسواق (Stale Cache).', [
        'source' => 'stale_cache',
        'user_id' => $userId,
        'count' => count($stale['items']),
        'items' => $stale['items'],
        'ts' => $stale['ts'] ?? time(),
        'note' => 'source_unavailable'
      ]);
    }
    throw $srcE;
  }

  $items = [];
  foreach ($data as $x) {
    $sym = safe_upper($x['symbol'] ?? '');
    if ($sym === '') continue;

    $base = $sym;
    $q = $quote;
    $iconUrl = null;

    if (isset($META[$sym])) {
      $base = (string)($META[$sym]['base'] ?? $base);
      $q = (string)($META[$sym]['quote'] ?? $q);
      $iconUrl = $META[$sym]['icon_url'] ?? null;
    } else {
      // fallback parsing
      if ($quote && ends_with($sym, $quote)) {
        $base = substr($sym, 0, -strlen($quote));
        $q = $quote;
      } elseif (ends_with($sym, 'USDT')) {
        $base = substr($sym, 0, -4);
        $q = 'USDT';
      }
    }

    $items[] = [
      'symbol' => $sym,
      'base' => safe_upper($base),
      'quote' => safe_upper($q),
      'icon_url' => $iconUrl, // ✅ جديد
      'lastPrice' => (float)($x['lastPrice'] ?? 0),
      'priceChangePercent' => (float)($x['priceChangePercent'] ?? 0),
      'quoteVolume' => (float)($x['quoteVolume'] ?? 0),
    ];
  }

  // ترتيب ثابت (مفيد للكاش/التطابق)
  usort($items, fn($a,$b)=>strcmp($a['symbol'], $b['symbol']));

  $payload = ['ts' => time(), 'items' => $items];
  cache_set($cacheKey, $payload);

  respond(true, 'تم جلب الأسواق بنجاح.', [
    'source' => 'binance',
    'user_id' => $userId,
    'count' => count($items),
    'items' => $items,
    'ts' => $payload['ts'],
  ]);

} catch (Throwable $e) {
  log_error($e);
  $msg = 'خطأ في الخادم';
  if ($DEBUG_MODE) $msg .= ' - ' . $e->getMessage();
  respond(false, $msg, [], 500);
}
