<?php
// admin/security_alerts_lib.php
// تنبيهات أمن لوحة الإدارة: تسجيل محاولات الدخول غير المصرّح وعرضها في مركز التنبيهات.

if (!function_exists('ua_security_add_col')) {
  function ua_security_add_col(mysqli $conn, string $table, string $col, string $ddl): void {
    $tableEsc = $conn->real_escape_string($table);
    $colEsc = $conn->real_escape_string($col);
    $rs = @$conn->query("SHOW COLUMNS FROM `{$tableEsc}` LIKE '{$colEsc}'");
    if (!$rs || $rs->num_rows < 1) {
      @ $conn->query("ALTER TABLE `{$tableEsc}` ADD COLUMN {$ddl}");
    }
  }
}

if (!function_exists('ua_security_alerts_ensure_schema')) {
  function ua_security_alerts_ensure_schema(mysqli $conn): void {
    @mysqli_report(MYSQLI_REPORT_OFF);
    @ $conn->query("CREATE TABLE IF NOT EXISTS admin_security_alerts (
      id INT UNSIGNED NOT NULL AUTO_INCREMENT,
      alert_key VARCHAR(190) NOT NULL,
      alert_type VARCHAR(60) NOT NULL DEFAULT 'login_failed',
      severity VARCHAR(20) NOT NULL DEFAULT 'warn',
      title VARCHAR(191) NOT NULL,
      message TEXT NULL,
      username VARCHAR(191) NULL,
      ip_address VARCHAR(45) NULL,
      user_agent VARCHAR(500) NULL,
      request_uri VARCHAR(500) NULL,
      is_open TINYINT(1) NOT NULL DEFAULT 1,
      first_seen_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
      last_seen_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
      hit_count INT NOT NULL DEFAULT 1,
      meta_json LONGTEXT NULL,
      created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
      updated_at DATETIME NULL,
      PRIMARY KEY(id),
      UNIQUE KEY uniq_alert_key (alert_key),
      KEY idx_open_seen (is_open, last_seen_at),
      KEY idx_ip_seen (ip_address, last_seen_at),
      KEY idx_type_seen (alert_type, last_seen_at)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");

    ua_security_add_col($conn, 'admin_security_alerts', 'alert_key', "`alert_key` VARCHAR(190) NOT NULL DEFAULT ''");
    ua_security_add_col($conn, 'admin_security_alerts', 'alert_type', "`alert_type` VARCHAR(60) NOT NULL DEFAULT 'login_failed'");
    ua_security_add_col($conn, 'admin_security_alerts', 'severity', "`severity` VARCHAR(20) NOT NULL DEFAULT 'warn'");
    ua_security_add_col($conn, 'admin_security_alerts', 'title', "`title` VARCHAR(191) NOT NULL DEFAULT 'تنبيه أمان'");
    ua_security_add_col($conn, 'admin_security_alerts', 'message', "`message` TEXT NULL");
    ua_security_add_col($conn, 'admin_security_alerts', 'username', "`username` VARCHAR(191) NULL");
    ua_security_add_col($conn, 'admin_security_alerts', 'ip_address', "`ip_address` VARCHAR(45) NULL");
    ua_security_add_col($conn, 'admin_security_alerts', 'user_agent', "`user_agent` VARCHAR(500) NULL");
    ua_security_add_col($conn, 'admin_security_alerts', 'request_uri', "`request_uri` VARCHAR(500) NULL");
    ua_security_add_col($conn, 'admin_security_alerts', 'is_open', "`is_open` TINYINT(1) NOT NULL DEFAULT 1");
    ua_security_add_col($conn, 'admin_security_alerts', 'first_seen_at', "`first_seen_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP");
    ua_security_add_col($conn, 'admin_security_alerts', 'last_seen_at', "`last_seen_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP");
    ua_security_add_col($conn, 'admin_security_alerts', 'hit_count', "`hit_count` INT NOT NULL DEFAULT 1");
    ua_security_add_col($conn, 'admin_security_alerts', 'meta_json', "`meta_json` LONGTEXT NULL");
    ua_security_add_col($conn, 'admin_security_alerts', 'created_at', "`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP");
    ua_security_add_col($conn, 'admin_security_alerts', 'updated_at', "`updated_at` DATETIME NULL");
  }
}

if (!function_exists('ua_security_client_ip')) {
  function ua_security_client_ip(): string {
    $keys = ['HTTP_CF_CONNECTING_IP', 'HTTP_X_REAL_IP', 'HTTP_X_FORWARDED_FOR', 'REMOTE_ADDR'];
    foreach ($keys as $k) {
      $raw = trim((string)($_SERVER[$k] ?? ''));
      if ($raw === '') continue;
      $first = trim(explode(',', $raw)[0]);
      if ($first !== '') return mb_substr($first, 0, 45, 'UTF-8');
    }
    return '0.0.0.0';
  }
}

if (!function_exists('ua_security_short')) {
  function ua_security_short(string $value, int $max): string {
    $value = trim($value);
    if ($value === '') return '';
    return mb_substr($value, 0, $max, 'UTF-8');
  }
}

if (!function_exists('ua_security_record_failed_login')) {
  function ua_security_record_failed_login(mysqli $conn, string $username, string $reason = 'invalid_credentials'): void {
    try {
      ua_security_alerts_ensure_schema($conn);
      $username = ua_security_short($username, 191);
      $ip = ua_security_client_ip();
      $ua = ua_security_short((string)($_SERVER['HTTP_USER_AGENT'] ?? ''), 500);
      $uri = ua_security_short((string)($_SERVER['REQUEST_URI'] ?? ''), 500);
      $type = $reason === 'rate_limited' ? 'login_rate_limited' : 'login_failed';
      $severity = $reason === 'rate_limited' ? 'danger' : 'warn';
      $title = $reason === 'rate_limited' ? 'حظر مؤقت لمحاولات دخول' : 'محاولة دخول غير مصرّح';
      $shownUser = $username !== '' ? $username : 'غير محدد';
      $message = 'تم رفض محاولة دخول إلى لوحة الإدارة باسم: ' . $shownUser . ' من IP: ' . $ip;
      if ($reason === 'rate_limited') $message .= ' بعد تجاوز عدد المحاولات المسموح.';
      $meta = json_encode([
        'reason' => $reason,
        'method' => (string)($_SERVER['REQUEST_METHOD'] ?? ''),
        'referer' => ua_security_short((string)($_SERVER['HTTP_REFERER'] ?? ''), 500),
      ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
      $key = hash('sha256', 'admin-login-alert|' . mb_strtolower($username, 'UTF-8') . '|' . $ip . '|' . $type);

      $stmt = @$conn->prepare("INSERT INTO admin_security_alerts
        (alert_key, alert_type, severity, title, message, username, ip_address, user_agent, request_uri, is_open, first_seen_at, last_seen_at, hit_count, meta_json, created_at)
        VALUES (?,?,?,?,?,?,?,?,?,1,NOW(),NOW(),1,?,NOW())
        ON DUPLICATE KEY UPDATE
          alert_type=VALUES(alert_type),
          severity=IF(hit_count + 1 >= 3, 'danger', VALUES(severity)),
          title=VALUES(title),
          message=VALUES(message),
          username=VALUES(username),
          ip_address=VALUES(ip_address),
          user_agent=VALUES(user_agent),
          request_uri=VALUES(request_uri),
          is_open=1,
          last_seen_at=NOW(),
          hit_count=hit_count+1,
          meta_json=VALUES(meta_json),
          updated_at=NOW()");
      if (!$stmt) return;
      $stmt->bind_param('ssssssssss', $key, $type, $severity, $title, $message, $username, $ip, $ua, $uri, $meta);
      @$stmt->execute();
      $stmt->close();
    } catch (Throwable $e) {
      // لا نسمح لفشل التسجيل الأمني أن يعطل الدخول أو النظام.
    }
  }
}

if (!function_exists('ua_security_record_event')) {
  function ua_security_record_event(mysqli $conn, string $alertKey, string $alertType, string $severity, string $title, string $message, array $opts = []): bool {
    try {
      ua_security_alerts_ensure_schema($conn);
      $alertType = ua_security_short($alertType !== '' ? $alertType : 'security_event', 60);
      $severity = in_array($severity, ['info','warn','danger'], true) ? $severity : 'warn';
      $title = ua_security_short($title !== '' ? $title : 'تنبيه أمان', 191);
      $message = trim($message);
      if ($message === '') $message = $title;
      $username = ua_security_short((string)($opts['username'] ?? ($_SESSION['admin_username'] ?? $_SESSION['admin_user'] ?? '')), 191);
      $ip = ua_security_short((string)($opts['ip_address'] ?? ua_security_client_ip()), 45);
      $ua = ua_security_short((string)($opts['user_agent'] ?? ($_SERVER['HTTP_USER_AGENT'] ?? '')), 500);
      $uri = ua_security_short((string)($opts['request_uri'] ?? ($_SERVER['REQUEST_URI'] ?? '')), 500);
      $firstSeen = trim((string)($opts['first_seen_at'] ?? date('Y-m-d H:i:s')));
      $lastSeen = trim((string)($opts['last_seen_at'] ?? $firstSeen));
      $meta = $opts['meta'] ?? [];
      if (!is_array($meta)) $meta = ['value' => $meta];
      $metaJson = json_encode($meta, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
      if (!is_string($metaJson)) $metaJson = '{}';
      $alertKey = trim($alertKey);
      if ($alertKey === '') {
        $alertKey = hash('sha256', 'security-event|' . $alertType . '|' . microtime(true) . '|' . random_int(1, PHP_INT_MAX));
      } else {
        $alertKey = ua_security_short($alertKey, 190);
      }

      $stmt = @$conn->prepare("INSERT INTO admin_security_alerts
        (alert_key, alert_type, severity, title, message, username, ip_address, user_agent, request_uri, is_open, first_seen_at, last_seen_at, hit_count, meta_json, created_at)
        VALUES (?,?,?,?,?,?,?,?,?,1,?,?,1,?,NOW())
        ON DUPLICATE KEY UPDATE
          alert_type=VALUES(alert_type),
          severity=VALUES(severity),
          title=VALUES(title),
          message=VALUES(message),
          username=VALUES(username),
          ip_address=VALUES(ip_address),
          user_agent=VALUES(user_agent),
          request_uri=VALUES(request_uri),
          is_open=1,
          last_seen_at=VALUES(last_seen_at),
          hit_count=hit_count+1,
          meta_json=VALUES(meta_json),
          updated_at=NOW()");
      if (!$stmt) return false;
      $stmt->bind_param('ssssssssssss', $alertKey, $alertType, $severity, $title, $message, $username, $ip, $ua, $uri, $firstSeen, $lastSeen, $metaJson);
      $ok = @$stmt->execute();
      $stmt->close();
      return (bool)$ok;
    } catch (Throwable $e) {
      return false;
    }
  }
}

if (!function_exists('ua_security_login_attempts_ensure_schema')) {
  function ua_security_login_attempts_ensure_schema(mysqli $conn): void {
    @mysqli_report(MYSQLI_REPORT_OFF);
    @ $conn->query("CREATE TABLE IF NOT EXISTS admin_login_attempts (
      id INT UNSIGNED NOT NULL AUTO_INCREMENT,
      username VARCHAR(191) NULL,
      ip_address VARCHAR(45) NOT NULL,
      result VARCHAR(20) NOT NULL DEFAULT 'failed',
      reason VARCHAR(80) NULL,
      user_agent VARCHAR(500) NULL,
      created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
      PRIMARY KEY(id),
      KEY idx_ip_created (ip_address, created_at),
      KEY idx_user_created (username, created_at),
      KEY idx_result_created (result, created_at)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
  }
}

if (!function_exists('ua_security_record_login_attempt')) {
  function ua_security_record_login_attempt(mysqli $conn, string $username, bool $success, string $reason = ''): void {
    try {
      ua_security_login_attempts_ensure_schema($conn);
      $username = ua_security_short($username, 191);
      $ip = ua_security_client_ip();
      $result = $success ? 'success' : 'failed';
      $reason = ua_security_short($reason, 80);
      $ua = ua_security_short((string)($_SERVER['HTTP_USER_AGENT'] ?? ''), 500);
      $stmt = @$conn->prepare("INSERT INTO admin_login_attempts (username, ip_address, result, reason, user_agent, created_at) VALUES (?,?,?,?,?,NOW())");
      if ($stmt) {
        $stmt->bind_param('sssss', $username, $ip, $result, $reason, $ua);
        @$stmt->execute();
        $stmt->close();
      }
      @ $conn->query("DELETE FROM admin_login_attempts WHERE created_at < DATE_SUB(NOW(), INTERVAL 30 DAY)");
    } catch (Throwable $e) {
      return;
    }
  }
}

if (!function_exists('ua_security_db_rate_limited')) {
  function ua_security_db_rate_limited(mysqli $conn, string $username, int $maxFails = 6, int $windowSeconds = 300): bool {
    try {
      ua_security_login_attempts_ensure_schema($conn);
      $maxFails = max(3, min(50, $maxFails));
      $windowSeconds = max(60, min(3600, $windowSeconds));
      $ip = $conn->real_escape_string(ua_security_client_ip());
      $username = $conn->real_escape_string(ua_security_short($username, 191));
      $where = "result='failed' AND created_at >= DATE_SUB(NOW(), INTERVAL {$windowSeconds} SECOND) AND (ip_address='{$ip}'";
      if ($username !== '') $where .= " OR username='{$username}'";
      $where .= ")";
      $rs = @$conn->query("SELECT COUNT(*) AS c FROM admin_login_attempts WHERE {$where}");
      $r = $rs ? $rs->fetch_assoc() : null;
      return ((int)($r['c'] ?? 0)) >= $maxFails;
    } catch (Throwable $e) {
      return false;
    }
  }
}

if (!function_exists('ua_security_recent_alerts')) {
  function ua_security_recent_alerts(mysqli $conn, int $limit = 10): array {
    try {
      ua_security_alerts_ensure_schema($conn);
      $limit = max(1, min(50, $limit));
      $rows = [];
      $rs = @$conn->query("SELECT * FROM admin_security_alerts WHERE is_open=1 ORDER BY last_seen_at DESC, id DESC LIMIT " . (int)$limit);
      if ($rs) {
        while ($r = $rs->fetch_assoc()) {
          $hits = (int)($r['hit_count'] ?? 1);
          $message = (string)($r['message'] ?? '');
          if ($hits > 1) $message .= ' - عدد المحاولات: ' . $hits;
          $r['message'] = $message;
          $r['href'] = 'security_monitor.php?type=admin&id=' . (int)($r['id'] ?? 0);
          if ($hits >= 3) $r['severity'] = 'danger';
          $rows[] = $r;
        }
      }
      return $rows;
    } catch (Throwable $e) {
      return [];
    }
  }
}

if (!function_exists('ua_security_close_alert')) {
  function ua_security_close_alert(mysqli $conn, int $id): bool {
    if ($id <= 0) return false;
    ua_security_alerts_ensure_schema($conn);
    return (bool)@ $conn->query("UPDATE admin_security_alerts SET is_open=0, updated_at=NOW() WHERE id=" . (int)$id . " LIMIT 1");
  }
}

if (!function_exists('ua_security_close_all_alerts')) {
  function ua_security_close_all_alerts(mysqli $conn): bool {
    ua_security_alerts_ensure_schema($conn);
    return (bool)@ $conn->query("UPDATE admin_security_alerts SET is_open=0, updated_at=NOW() WHERE is_open=1");
  }
}


if (!function_exists('ua_security_alerts_list')) {
  function ua_security_alerts_list(mysqli $conn, int $limit = 200, ?bool $openOnly = null): array {
    try {
      ua_security_alerts_ensure_schema($conn);
      $limit = max(1, min(1000, $limit));
      $where = '';
      if ($openOnly === true) $where = ' WHERE is_open=1';
      elseif ($openOnly === false) $where = '';
      $rows = [];
      $rs = @$conn->query("SELECT * FROM admin_security_alerts" . $where . " ORDER BY is_open DESC, last_seen_at DESC, id DESC LIMIT " . (int)$limit);
      if ($rs) while ($r = $rs->fetch_assoc()) $rows[] = $r;
      return $rows;
    } catch (Throwable $e) {
      return [];
    }
  }
}
