<?php
/* ============================================================
   منصة خبرة — أدوات مشتركة
   ردود JSON · رموز CSRF · التحقق من المدخلات · حارس الدخول
   ============================================================ */

declare(strict_types=1);

/* ---------- ردود JSON ---------- */
function exp_json($data, int $code = 200): void {
  http_response_code($code);
  header('Content-Type: application/json; charset=utf-8');
  header('Cache-Control: no-store, no-cache, must-revalidate');
  echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
  exit;
}
function exp_fail(string $msg, int $code = 400): void { exp_json(['ok' => false, 'error' => $msg], $code); }

/* ---------- قراءة جسم الطلب ---------- */
function exp_body(): array {
  $raw = file_get_contents('php://input');
  if ($raw === false || $raw === '') return [];
  $d = json_decode($raw, true);
  return is_array($d) ? $d : [];
}

/* ---------- إلزام طريقة الطلب ---------- */
function exp_require_post(): void {
  if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') exp_fail('طريقة الطلب غير مسموحة', 405);
}

/* ============================================================
   رمز الحماية CSRF
   يمنع موقعًا خبيثًا من إرسال طلبات باسم المستخدم المسجّل
   ============================================================ */
function exp_csrf_token(): string {
  if (empty($_SESSION['csrf'])) {
    $_SESSION['csrf'] = bin2hex(random_bytes(32));
  }
  return $_SESSION['csrf'];
}
function exp_check_csrf(): void {
  $sent = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? ($_POST['csrf'] ?? '');
  $have = $_SESSION['csrf'] ?? '';
  if ($have === '' || !is_string($sent) || !hash_equals($have, $sent)) {
    exp_fail('رمز الحماية غير صالح — أعد تحميل الصفحة', 419);
  }
}

/* ---------- التحقق من أن الطلب من نفس الموقع ---------- */
function exp_check_origin(): void {
  $origin = $_SERVER['HTTP_ORIGIN'] ?? '';
  if ($origin === '') return;                       // بعض المتصفحات لا ترسله
  $host = $_SERVER['HTTP_HOST'] ?? '';
  $ok = parse_url($origin, PHP_URL_HOST) === $host;
  if (!$ok) exp_fail('طلب من مصدر غير موثوق', 403);
}

/* ============================================================
   حارس الدخول
   ============================================================ */
function exp_user(): ?array {
  if (empty($_SESSION['uid'])) return null;
  return [
    'id'   => (int)$_SESSION['uid'],
    'user' => (string)($_SESSION['uname'] ?? ''),
    'name' => (string)($_SESSION['dname'] ?? ''),
  ];
}
function exp_require_login(): array {
  $u = exp_user();
  if (!$u) exp_fail('الدخول مطلوب', 401);
  return $u;
}

/* ============================================================
   تنظيف المدخلات
   ============================================================ */
/* الأيقونة: إيموجي قصير أو مسار صورة مرفوعة */
function exp_icon($v): string {
  $s = trim((string)(is_scalar($v) ? $v : ""));
  if ($s === "") return "";
  if (preg_match("#^uploads/[A-Za-z0-9._-]{1,120}$#", $s)) return $s;
  if (strpos($s, "data:image/") === 0) return mb_substr($s, 0, 400000);
  return mb_substr($s, 0, 16, "UTF-8");
}

function exp_str($v, int $max = 500): string {
  if (!is_scalar($v)) return '';
  $s = trim((string)$v);
  $s = str_replace(["\0", "\r"], '', $s);
  return mb_substr($s, 0, $max, 'UTF-8');
}
function exp_id($v): string {
  $s = strtolower(trim((string)(is_scalar($v) ? $v : '')));
  $s = preg_replace('/[^a-z0-9_-]+/', '-', $s) ?? '';
  $s = trim($s, '-');
  return $s !== '' ? mb_substr($s, 0, 64) : '';
}
function exp_num($v, float $min = 0, float $max = 1): float {
  $n = is_numeric($v) ? (float)$v : 0.0;
  return max($min, min($max, $n));
}
function exp_color($v): string {
  $s = strtoupper(trim((string)(is_scalar($v) ? $v : '')));
  return preg_match('/^#[0-9A-F]{6}$/', $s) ? $s : '#00AAA4';
}
/* يقبل مسار ملف داخل uploads أو صورة مدمجة (توافق مع البيانات القديمة) */
function exp_photo($v): string {
  $s = trim((string)(is_scalar($v) ? $v : ''));
  if ($s === '') return '';
  if (preg_match('#^uploads/[A-Za-z0-9._-]{1,120}$#', $s)) return $s;
  if (strpos($s, 'data:image/') === 0) return mb_substr($s, 0, 2000000);
  return '';
}
function exp_url($v): string {
  $s = trim((string)(is_scalar($v) ? $v : ''));
  if ($s === '') return '';
  if (!preg_match('#^https?://#i', $s)) return '';
  return filter_var($s, FILTER_VALIDATE_URL) ? mb_substr($s, 0, 500) : '';
}

/* ============================================================
   تسجيل محاولات الدخول (الحماية من التخمين)
   ============================================================ */
function exp_client_ip(): string {
  $ip = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
  return filter_var($ip, FILTER_VALIDATE_IP) ? $ip : '0.0.0.0';
}

function exp_login_blocked(PDO $db, array $CONFIG): int {
  $max  = (int)($CONFIG['security']['max_login_attempts'] ?? 5);
  $mins = (int)($CONFIG['security']['lockout_minutes'] ?? 15);

  $st = $db->prepare(
    "SELECT COUNT(*) AS n, MAX(created_at) AS last
       FROM exp_login_attempts
      WHERE ip = ? AND success = 0 AND created_at > (NOW() - INTERVAL ? MINUTE)"
  );
  $st->execute([exp_client_ip(), $mins]);
  $r = $st->fetch();

  if (!$r || (int)$r['n'] < $max) return 0;

  $last = strtotime((string)$r['last']);
  $left = ($last + $mins * 60) - time();
  return $left > 0 ? (int)ceil($left / 60) : 0;
}

function exp_log_attempt(PDO $db, string $user, bool $success): void {
  $st = $db->prepare(
    "INSERT INTO exp_login_attempts (ip, username, success) VALUES (?, ?, ?)"
  );
  $st->execute([exp_client_ip(), mb_substr($user, 0, 64), $success ? 1 : 0]);

  // تنظيف السجلات الأقدم من يوم — مرة كل ~20 محاولة بدل كل مرة
  if (random_int(1, 20) === 1) {
    $db->exec("DELETE FROM exp_login_attempts WHERE created_at < (NOW() - INTERVAL 1 DAY)");
  }
}
