<?php
@ini_set('display_errors','0');
error_reporting(E_ALL & ~E_WARNING & ~E_NOTICE);
set_error_handler(function($severity,$message,$file,$line){
  return in_array($severity, [E_WARNING,E_NOTICE,E_USER_WARNING,E_USER_NOTICE,E_DEPRECATED,E_USER_DEPRECATED], true);
});
$page_title = 'قائمة المشتركين';

$UA_FORCE_DESIGN_V2 = true;
require_once __DIR__ . '/auth.php';
require_once __DIR__ . '/db_bootstrap.php';
require_once __DIR__ . '/quota_lib.php';
require_once __DIR__ . '/mikrotik_lib.php';
require_once __DIR__ . '/ppp_active_cache_lib.php';
require_once __DIR__ . '/subscriber_status_lib.php';
require_once __DIR__ . '/settings_store.php';
require_once __DIR__ . '/billing_account_lib.php';
require_once __DIR__ . '/subscriber_history_lib.php';
require_once __DIR__ . '/subscriber_plan_schema.php';
require_once __DIR__ . '/ua_unified_stats_lib.php';
require_once __DIR__ . '/stats_snapshot_lib.php';
require_once __DIR__ . '/receiver_subscriber_match_lib.php';
require_once __DIR__ . '/subscriber_custom_print_lib.php';

if (function_exists('ensure_db')) ensure_db(true);
$conn = $GLOBALS['conn'] ?? null;
if (!($conn instanceof mysqli)) { http_response_code(500); die('Database connection error'); }
if (function_exists('subscriber_plan_ensure_schema')) { @subscriber_plan_ensure_schema($conn); }
if (function_exists('billing_accounts_migrate')) { billing_accounts_migrate($conn); }

/** أدوات فحص سكيمة */
if (!function_exists('col_exists')) {
function col_exists(mysqli $conn, string $table, string $col): bool {
  static $cache = [];
  $key = $table.'|'.$col;
  if (array_key_exists($key, $cache)) return $cache[$key];
  $t = $conn->real_escape_string($table);
  $c = $conn->real_escape_string($col);
  $sql = "SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
          WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='$t' AND COLUMN_NAME='$c' LIMIT 1";
  $rs = $conn->query($sql);
  return $cache[$key] = ($rs && $rs->num_rows > 0);
}
}

function time_ago($datetime) {
  if (!$datetime) return '—';
  $ts = strtotime($datetime);
  if (!$ts || $ts < 946684800) return '—';
  $diff = time() - $ts;
  if ($diff < 60) return "الآن";
  if ($diff < 3600) return "منذ " . floor($diff/60) . " دقيقة";
  if ($diff < 86400) return "منذ " . floor($diff/3600) . " ساعة";
  if ($diff < 604800) return "منذ " . floor($diff/86400) . " يوم";
  return date("Y-m-d H:i", $ts);
}

/** توحيد اسم المستخدم (إزالة realm) */
function normalize_username(string $u): string {
  $u = trim($u);
  $u = preg_replace('/@.*/', '', $u);
  return trim($u);
}


function ua_search_normalize_ar(string $v): string {
  $v = str_replace("\xC2\xA0", ' ', $v);
  $v = preg_replace('/[\x{064B}-\x{065F}\x{0670}\x{0640}]/u', '', $v);
  $v = strtr($v, ['أ'=>'ا','إ'=>'ا','آ'=>'ا','ٱ'=>'ا','ى'=>'ي','ئ'=>'ي','ؤ'=>'و','ة'=>'ه']);
  $v = preg_replace('/\s+/u', ' ', $v);
  return trim(mb_strtolower($v, 'UTF-8'));
}
function ua_sql_like_escape(mysqli $conn, string $v): string {
  return $conn->real_escape_string($v);
}


function ua_subscribers_sync_gate(string $name, int $ttl = 15): bool {
  $path = rtrim(sys_get_temp_dir(), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'ua_' . preg_replace('/[^a-z0-9_\-]+/i','_', $name) . '.lock';
  $now = time();
  if (is_file($path) && (($now - (int)@filemtime($path)) < $ttl)) return false;
  @touch($path);
  return true;
}

function ua_monthly_usage_batch(mysqli $conn, array $rows, string $cycleMonth): array {
  $map = [];
  if (empty($rows) || !function_exists('q_get_monthly_used_bytes')) return $map;
  $byId = [];
  $byUser = [];
  foreach ($rows as $row) {
    $sid = (int)($row['id'] ?? 0);
    $u = trim((string)($row['ppp_username'] ?? ''));
    if ($u === '') $u = trim((string)($row['username'] ?? ''));
    $u = preg_replace('/@.*/', '', $u);
    if ($sid > 0) $byId[$sid] = true;
    if ($u !== '') $byUser[$u] = true;
  }
  if (col_exists($conn, 'ppp_usage_monthly', 'subscriber_id') && col_exists($conn, 'ppp_usage_monthly', 'month_key') && !empty($byId)) {
    $ids = implode(',', array_map('intval', array_keys($byId)));
    $m = $conn->real_escape_string($cycleMonth);
    $q = @$conn->query("SELECT subscriber_id, SUM(used_bytes) used_bytes FROM ppp_usage_monthly WHERE month_key='$m' AND subscriber_id IN ($ids) GROUP BY subscriber_id");
    if ($q) while ($r = $q->fetch_assoc()) $map['id:' . (int)$r['subscriber_id']] = (int)($r['used_bytes'] ?? 0);
  }
  if (col_exists($conn, 'ppp_usage_monthly', 'username') && col_exists($conn, 'ppp_usage_monthly', 'cycle_month') && !empty($byUser)) {
    $in = "'" . implode("','", array_map([$conn, 'real_escape_string'], array_keys($byUser))) . "'";
    $m = $conn->real_escape_string($cycleMonth);
    $q = @$conn->query("SELECT username, SUM(used_bytes) used_bytes FROM ppp_usage_monthly WHERE cycle_month='$m' AND username IN ($in) GROUP BY username");
    if ($q) while ($r = $q->fetch_assoc()) $map['user:' . trim((string)$r['username'])] = (int)($r['used_bytes'] ?? 0);
  }
  return $map;
}

/** استخراج usernames من mt_ppp_active() مهما كان شكل المفاتيح */
function mt_active_usernames(mysqli $conn): array {
  if (function_exists('ua_ppp_cache_active_usernames')) {
    return ua_ppp_cache_active_usernames($conn, 10);
  }
  $rows = mt_ppp_active($conn);
  if (!is_array($rows)) return [];
  $set = [];
  foreach ($rows as $r) {
    if (!is_array($r)) continue;

    $u = '';
    foreach (['name','NAME','user','USER','username','USERNAME'] as $k) {
      if (isset($r[$k]) && trim((string)$r[$k]) !== '') { $u = (string)$r[$k]; break; }
    }
    $u = normalize_username($u);
    if ($u !== '') $set[$u] = true;
  }
  return array_keys($set);
}

/**
 * ✅ مزامنة is_online من المايكروتك
 * - تحدّث last_seen_at للمتصلين
 * - تحدّث last_disconnect_at للي كانوا online وصاروا offline
 */
function sync_subscribers_online(mysqli $conn): array {
  $now = date('Y-m-d H:i:s');

  $hasDel   = col_exists($conn,'subscribers','is_deleted');
  $hasOn    = col_exists($conn,'subscribers','is_online');
  $hasSeen  = col_exists($conn,'subscribers','last_seen_at');
  $hasDisc  = col_exists($conn,'subscribers','last_disconnect_at');
  $hasPppU  = col_exists($conn,'subscribers','ppp_username');

  if (!$hasOn) return ['ok'=>false,'active'=>0,'message'=>'عمود is_online غير موجود'];

  if (function_exists('sh_ensure_schema')) {
    sh_ensure_schema($conn);
  }

  $userExpr = $hasPppU ? "SUBSTRING_INDEX(COALESCE(NULLIF(ppp_username,''), username),'@',1)" : "SUBSTRING_INDEX(username,'@',1)";

  $activeUsers = mt_active_usernames($conn);
  $activeCount = count($activeUsers);

  $delCond = $hasDel ? " AND COALESCE(is_deleted,0)=0 " : "";
  $wentOffline = [];
  $wentOnline  = [];

  if ($activeCount > 0) {
    $in = "'" . implode("','", array_map([$conn,'real_escape_string'], $activeUsers)) . "'";

    $qOff = @$conn->query("SELECT id FROM subscribers WHERE is_online=1 $delCond AND $userExpr NOT IN ($in)");
    if ($qOff) while ($r = $qOff->fetch_assoc()) $wentOffline[] = (int)$r['id'];

    $qOn = @$conn->query("SELECT id FROM subscribers WHERE COALESCE(is_online,0)=0 $delCond AND $userExpr IN ($in)");
    if ($qOn) while ($r = $qOn->fetch_assoc()) $wentOnline[] = (int)$r['id'];

    $sqlOff = "UPDATE subscribers
               SET is_online=0" .
               ($hasDisc ? ", last_disconnect_at='$now'" : "") . "
               WHERE is_online=1 $delCond
                 AND $userExpr NOT IN ($in)";
    @$conn->query($sqlOff);

    $sqlOn = "UPDATE subscribers
              SET is_online=1" .
              ($hasSeen ? ", last_seen_at='$now'" : "") . "
              WHERE 1=1 $delCond
                AND $userExpr IN ($in)";
    @$conn->query($sqlOn);

  } else {
    $qOffAll = @$conn->query("SELECT id FROM subscribers WHERE is_online=1 $delCond");
    if ($qOffAll) while ($r = $qOffAll->fetch_assoc()) $wentOffline[] = (int)$r['id'];

    $sqlOffAll = "UPDATE subscribers
                  SET is_online=0" .
                  ($hasDisc ? ", last_disconnect_at='$now'" : "") . "
                  WHERE is_online=1 $delCond";
    @$conn->query($sqlOffAll);
  }

  if (function_exists('sh_log_online_state')) {
    foreach ($wentOnline as $sid) {
      sh_log_online_state($conn, $sid, true, $now, 'تم تسجيل اتصال المشترك');
    }
    foreach ($wentOffline as $sid) {
      sh_log_online_state($conn, $sid, false, $now, 'تم تسجيل فصل/انقطاع المشترك');
    }
  }

  return ['ok'=>true,'active'=>$activeCount,'went_online'=>count($wentOnline),'went_offline'=>count($wentOffline)];
}

/* ✅ إقلاع الصفحة من مصدر واحد لكل عدادات المشتركين */
$sync = ['ok'=>true,'active'=>0,'message'=>'app_stats_snapshot'];
// Opening the subscriber workspace must never wait for a RouterOS refresh.
// Compose the first paint from the last atomic caches and local database; the
// existing seven-second live-status poll refreshes the same counters afterward.
$unifiedStats = function_exists('ua_unified_stats') ? ua_unified_stats($conn, 10, false) : [];
$footerCounts = [
  'ok' => (bool)($unifiedStats['ok'] ?? true),
  'ts' => time(),
  'total' => (int)($unifiedStats['total'] ?? 0),
  'online' => (int)($unifiedStats['online'] ?? 0),
  'offline' => (int)($unifiedStats['offline'] ?? 0),
  'disabled' => (int)($unifiedStats['disabled'] ?? 0),
  'unsafe_speed' => (int)($unifiedStats['unsafe_speed'] ?? 0),
  'open_all_mode' => (int)($unifiedStats['open_all_mode'] ?? 0),
  'source_online' => (string)($unifiedStats['online_source'] ?? 'unified'),
  'source_disabled' => (string)($unifiedStats['disabled_source'] ?? 'unified'),
];
$mtRealityMap = function_exists('mt_ppp_secret_reality_map_cached') ? mt_ppp_secret_reality_map_cached($conn, 60, false) : [];
$mtDisabledMap = [];
foreach ($mtRealityMap as $mtU => $mtRow) { $mtDisabledMap[$mtU] = !empty($mtRow['disabled']); }
$pppStatusCache = function_exists('ua_ppp_cache_snapshot') ? ua_ppp_cache_snapshot(86400) : [];
$pppActiveRowMap = function_exists('sub_status_ppp_active_map') ? sub_status_ppp_active_map($pppStatusCache) : [];
$mtActive = (int)($footerCounts['online'] ?? 0);
$current_month = date('Y-m');
$cycle_month   = $current_month;
$flash_success = (string)($_SESSION['flash_success'] ?? '');
$flash_warning = (string)($_SESSION['flash_warning'] ?? '');
unset($_SESSION['flash_success'], $_SESSION['flash_warning']);
$openAllMode = function_exists('setting_get') ? ((string)setting_get($conn, 'ppp_open_all_mode', '0') === '1') : false;
$openAllAction = $openAllMode ? 'restore' : 'open';
$openAllLabel  = $openAllMode ? 'إعادة القيود' : 'سرعة مفتوحة';
$openAllTitle  = $openAllMode ? 'إعادة جميع الحسابات إلى السرعات الأصلية' : 'فتح جميع السرعات دون قيود';
$openAllIcon   = $openAllMode ? 'fa-undo' : 'fa-bolt';
$openAllGo     = 'subscribers_speed_toggle_all.php?action=' . $openAllAction;

// ✅ تحديث الاستهلاك الشهري من counters الحية بشكل مؤقت
// Monthly usage sync is intentionally kept out of the initial page render.
// Run cron/update_ppp_cache.php or a dedicated quota worker for live MikroTik counters.

$filter = $_GET['filter'] ?? 'all';
$allowedFilters = ['all','online','offline','disabled','limited'];
if (!in_array($filter, $allowedFilters, true)) $filter = 'all';
$q = trim((string)($_GET['q'] ?? ''));
$requestedViewMode = trim((string)($_GET['view'] ?? ''));
$allowedViewModes = ['all', 'pages'];
if (in_array($requestedViewMode, $allowedViewModes, true)) {
  $viewMode = $requestedViewMode;
  $_SESSION['subscribers_view_mode'] = $viewMode;
} else {
  $viewMode = (string)($_SESSION['subscribers_view_mode'] ?? 'all');
  if (!in_array($viewMode, $allowedViewModes, true)) $viewMode = 'all';
}
$sort = trim((string)($_GET['sort'] ?? ''));
$allowedSorts = ['alpha','id','speed','sector'];
if (!in_array($sort, $allowedSorts, true)) $sort = '';
$sort = $viewMode === 'pages' ? $sort : '';
if ($viewMode === 'pages' && $sort === '') $sort = 'id';
$perPageOptions = [50, 100, 200];
$perPage = (int)($_GET['per_page'] ?? 50);
if (!in_array($perPage, $perPageOptions, true)) $perPage = 50;
$currentPage = max(1, (int)($_GET['page'] ?? 1));
$currentPage = $viewMode === 'pages' ? $currentPage : 1;
$qs = '';
if ($q !== '') $qs .= '&q=' . urlencode($q);
if ($sort !== '') $qs .= '&sort=' . urlencode($sort);
$qs .= '&view=' . urlencode($viewMode);
if ($viewMode === 'pages' && $perPage !== 50) $qs .= '&per_page=' . $perPage;

$whereParts = [];
if (col_exists($conn,'subscribers','is_deleted')) $whereParts[] = "COALESCE(is_deleted,0)=0";
$hasPppDisabled = col_exists($conn,'subscribers','ppp_is_disabled');
$hasMtDisabled  = col_exists($conn,'subscribers','mt_disabled');
$hasIsCut       = col_exists($conn,'subscribers','is_cut');
$hasSpeedState  = col_exists($conn,'subscribers','speed_state');
$hasStatusCol   = col_exists($conn,'subscribers','status');
$hasLastCut     = col_exists($conn,'subscribers','ppp_last_cut_at');
$hasLastSeen    = col_exists($conn,'subscribers','last_seen_at');
$hasLastDisc    = col_exists($conn,'subscribers','last_disconnect_at');
$hasPppUserCol  = col_exists($conn,'subscribers','ppp_username');
$disabledConds = [];
if ($hasPppDisabled) $disabledConds[] = "COALESCE(ppp_is_disabled,0)=1";
if ($hasMtDisabled) $disabledConds[] = "COALESCE(mt_disabled,0)=1";
if ($hasIsCut) $disabledConds[] = "COALESCE(is_cut,0)=1";
if ($hasSpeedState) $disabledConds[] = "UPPER(COALESCE(speed_state,''))='CUT'";
if ($hasStatusCol) $disabledConds[] = "UPPER(COALESCE(status,'')) IN ('CUT','DISABLED','STOPPED')";
$disabledWhere = $disabledConds ? '(' . implode(' OR ', $disabledConds) . ')' : '0=1';
$limitedConds = [];
if ($hasSpeedState) $limitedConds[] = "UPPER(COALESCE(speed_state,'')) IN ('LIMIT','LIMITED','REDUCED','THROTTLED')";
if (col_exists($conn,'subscribers','current_speed') && col_exists($conn,'subscribers','base_speed')) {
  $limitedConds[] = "(COALESCE(current_speed,'')<>'' AND COALESCE(base_speed,'')<>'' AND COALESCE(current_speed,'')<>COALESCE(base_speed,''))";
}
$limitedWhere = $limitedConds ? '(' . implode(' OR ', $limitedConds) . ')' : '0=1';
if ($filter === 'online') {
  $whereParts[] = "COALESCE(is_online,0)=1";
} elseif ($filter === 'offline') {
  $whereParts[] = "COALESCE(is_online,0)=0";
} elseif ($filter === 'disabled') {
  $whereParts[] = $disabledWhere;
} elseif ($filter === 'limited') {
  $whereParts[] = $limitedWhere;
}
if ($q !== '') {
  // بحث محسن للأسماء العربية المستوردة من Excel: يدعم الاسم الثلاثي، الفراغات المكررة/NBSP، والبحث بجزء من كل كلمة.
  $qClean = preg_replace('/\s+/u', ' ', str_replace("\xC2\xA0", ' ', $q));
  $qe = ua_sql_like_escape($conn, $qClean);
  $like = "%".$qe."%";
  $conds = [];
  if (ctype_digit($qClean)) $conds[] = "id=".(int)$qClean;
  $conds[] = "username LIKE '$like'";
  if (col_exists($conn,'subscribers','ppp_username')) $conds[] = "ppp_username LIKE '$like'";
  if (col_exists($conn,'subscribers','full_name')) {
    $conds[] = "full_name LIKE '$like'";
    $noSpace = ua_sql_like_escape($conn, preg_replace('/\s+/u', '', $qClean));
    if ($noSpace !== '') {
      $conds[] = "REPLACE(REPLACE(REPLACE(full_name,' ',''), CHAR(160), ''), CHAR(9), '') LIKE '%$noSpace%'";
    }
    $tokens = preg_split('/\s+/u', $qClean, -1, PREG_SPLIT_NO_EMPTY);
    $tokenConds = [];
    foreach ($tokens as $tok) {
      $tok = trim($tok);
      if ($tok === '') continue;
      $te = ua_sql_like_escape($conn, $tok);
      $tokenConds[] = "full_name LIKE '%$te%'";
    }
    if (count($tokenConds) > 1) $conds[] = "(" . implode(' AND ', $tokenConds) . ")";
  }
  if (col_exists($conn,'subscribers','phone')) $conds[] = "phone LIKE '$like'";
  $whereParts[] = "(".implode(" OR ", $conds).")";
}

$where = $whereParts ? ("WHERE ".implode(" AND ", $whereParts)) : "";
$uaAndroidSubscribers = ((string)($_GET['ua_android'] ?? '') === '1')
  || stripos((string)($_SERVER['HTTP_USER_AGENT'] ?? ''), 'NetworkCustomersAndroid/') !== false;
$countRs = $conn->query("SELECT COUNT(*) c FROM subscribers s $where");
$totalRows = ($countRs && ($countRow = $countRs->fetch_assoc())) ? (int)($countRow['c'] ?? 0) : 0;
$totalPages = $viewMode === 'pages' ? max(1, (int)ceil($totalRows / $perPage)) : 1;
$currentPage = min($currentPage, $totalPages);
$pageOffset = $viewMode === 'pages' ? (($currentPage - 1) * $perPage) : 0;
$pageFrom = $totalRows > 0 ? $pageOffset + 1 : 0;
$pageTo = $viewMode === 'pages' ? min($totalRows, $pageOffset + $perPage) : $totalRows;
$subscriberPageUrl = static function(int $page) use ($filter, $q, $sort, $perPage, $viewMode): string {
  $params = ['filter' => $filter, 'view' => $viewMode];
  if ($q !== '') $params['q'] = $q;
  if ($sort !== '') $params['sort'] = $sort;
  if ($viewMode === 'pages' && $perPage !== 50) $params['per_page'] = $perPage;
  if ($page > 1) $params['page'] = $page;
  return 'subscribers.php?' . http_build_query($params, '', '&', PHP_QUERY_RFC3986);
};

/** بالأعلى: نعرض فقط المتصلون حسب قاعدة البيانات (بعد المزامنة) */
$online = 0;
try {
  $w = col_exists($conn,'subscribers','is_deleted') ? "WHERE COALESCE(is_deleted,0)=0" : "";
  $rs = $conn->query("SELECT SUM(CASE WHEN COALESCE(is_online,0)=1 THEN 1 ELSE 0 END) AS online FROM subscribers $w");
  if ($rs && ($row=$rs->fetch_assoc())) $online = (int)$row['online'];
} catch(Throwable $e){}

$lastUpdate = !empty($footerCounts['ts']) ? date('Y-m-d H:i:s', (int)$footerCounts['ts']) : date('Y-m-d H:i:s');

$hasPppDisabled = col_exists($conn,'subscribers','ppp_is_disabled');
$hasLastCut     = col_exists($conn,'subscribers','ppp_last_cut_at');
$hasLastSeen    = col_exists($conn,'subscribers','last_seen_at');
$hasPppUserCol  = col_exists($conn,'subscribers','ppp_username');

$unsafeSpeedCount = (int)($footerCounts['unsafe_speed'] ?? 0);
$unsafeCutExtra = 0;
$unsafeUsersMap = [];
$GLOBALS['ua_cut_subs_extra'] = $unsafeCutExtra;
$GLOBALS['ua_unsafe_speed_count'] = $unsafeSpeedCount;


$onlineSeenExpr = $hasLastSeen
  ? "CASE WHEN s.last_seen_at IS NOT NULL AND s.last_seen_at <> '0000-00-00 00:00:00' AND s.last_seen_at >= '2000-01-01 00:00:00' THEN UNIX_TIMESTAMP(s.last_seen_at) ELSE 2147483647 END"
  : "s.id";
if ($hasLastSeen && $hasLastDisc) {
  $offlineSeenExpr = "GREATEST(
    CASE WHEN s.last_seen_at IS NOT NULL AND s.last_seen_at <> '0000-00-00 00:00:00' AND s.last_seen_at >= '2000-01-01 00:00:00' THEN UNIX_TIMESTAMP(s.last_seen_at) ELSE 0 END,
    CASE WHEN s.last_disconnect_at IS NOT NULL AND s.last_disconnect_at <> '0000-00-00 00:00:00' AND s.last_disconnect_at >= '2000-01-01 00:00:00' THEN UNIX_TIMESTAMP(s.last_disconnect_at) ELSE 0 END
  )";
} elseif ($hasLastSeen) {
  $offlineSeenExpr = "CASE WHEN s.last_seen_at IS NOT NULL AND s.last_seen_at <> '0000-00-00 00:00:00' AND s.last_seen_at >= '2000-01-01 00:00:00' THEN UNIX_TIMESTAMP(s.last_seen_at) ELSE 0 END";
} elseif ($hasLastDisc) {
  $offlineSeenExpr = "CASE WHEN s.last_disconnect_at IS NOT NULL AND s.last_disconnect_at <> '0000-00-00 00:00:00' AND s.last_disconnect_at >= '2000-01-01 00:00:00' THEN UNIX_TIMESTAMP(s.last_disconnect_at) ELSE 0 END";
} else {
  $offlineSeenExpr = "s.id";
}
$sortOrder = "ORDER BY
  CASE WHEN COALESCE(s.is_online,0)=1 THEN 0 ELSE 1 END ASC,
  CASE WHEN COALESCE(s.is_online,0)=1 THEN {$onlineSeenExpr} ELSE 0 END ASC,
  CASE WHEN COALESCE(s.is_online,0)=0 THEN {$offlineSeenExpr} ELSE 0 END DESC,
  CASE WHEN COALESCE(s.is_online,0)=1 THEN s.id ELSE 0 END ASC,
  CASE WHEN COALESCE(s.is_online,0)=0 THEN s.id ELSE 0 END DESC";

$querySortOrder = $sortOrder;
if ($sort === 'alpha') {
  $querySortOrder = "ORDER BY CASE WHEN TRIM(COALESCE(s.full_name,''))='' THEN 1 ELSE 0 END ASC, s.full_name ASC, s.id DESC";
} elseif ($sort === 'id') {
  $querySortOrder = "ORDER BY s.id ASC";
}
$sortNeedsFullSet = in_array($sort, ['speed', 'sector'], true);
$queryPagination = (!$uaAndroidSubscribers && $viewMode === 'pages' && !$sortNeedsFullSet)
  ? " LIMIT {$perPage} OFFSET {$pageOffset}"
  : '';
$result = $conn->query("SELECT * FROM subscribers s $where $querySortOrder$queryPagination");

$subscribersRows = [];
if ($result && $result->num_rows > 0) {
  while ($sub = $result->fetch_assoc()) { $subscribersRows[] = $sub; }
}

$pendingInvoiceBySubscriber = [];
if (!empty($subscribersRows) && function_exists('table_exists') && table_exists($conn, 'invoices')) {
  $subscriberIdsForInvoices = [];
  foreach ($subscribersRows as $rowForInvoice) {
    $sidForInvoice = (int)($rowForInvoice['id'] ?? 0);
    if ($sidForInvoice > 0) $subscriberIdsForInvoices[$sidForInvoice] = true;
  }

  if ($subscriberIdsForInvoices) {
    $invoiceSidList = implode(',', array_map('intval', array_keys($subscriberIdsForInvoices)));
    $invoiceUnpaidChecks = [];
    if (col_exists($conn, 'invoices', 'payment_status')) {
      $invoiceUnpaidChecks[] = "LOWER(COALESCE(i.payment_status,'unpaid')) <> 'paid'";
    }
    if (col_exists($conn, 'invoices', 'status')) {
      $invoiceUnpaidChecks[] = "(i.status IS NULL OR (i.status <> CONVERT(0xD985D8AFD981D988D8B9 USING utf8mb4) AND LOWER(i.status) <> 'paid'))";
    }
    if (!$invoiceUnpaidChecks) $invoiceUnpaidChecks[] = '1=1';
    if (col_exists($conn, 'invoices', 'remaining_amount')) {
      $invoiceUnpaidChecks[] = "COALESCE(i.remaining_amount,i.amount,0) > 0";
    }
    if (col_exists($conn, 'invoices', 'due_date')) {
      $fallbackDue = col_exists($conn, 'invoices', 'created_at') ? 'DATE(i.created_at)' : 'CURDATE()';
      $invoiceUnpaidChecks[] = "COALESCE(i.due_date,{$fallbackDue}) <= CURDATE()";
    }

    $invoiceSelect = ['i.id', 'i.subscriber_id', 'i.amount'];
    foreach (['remaining_amount','due_date','created_at','cycle_month','cycle_label'] as $invoiceCol) {
      if (col_exists($conn, 'invoices', $invoiceCol)) $invoiceSelect[] = 'i.' . $invoiceCol;
    }
    $invoiceOrder = col_exists($conn, 'invoices', 'due_date')
      ? "ORDER BY (i.due_date IS NULL), i.due_date ASC, i.id ASC"
      : "ORDER BY i.id ASC";
    $invoiceSql = "SELECT " . implode(',', $invoiceSelect) . "
      FROM invoices i
      WHERE i.subscriber_id IN ($invoiceSidList)
        AND (" . implode(' AND ', $invoiceUnpaidChecks) . ")
      $invoiceOrder";
    $invoiceRs = @ $conn->query($invoiceSql);
    if ($invoiceRs) {
      while ($invoiceRow = $invoiceRs->fetch_assoc()) {
        $sidForInvoice = (int)($invoiceRow['subscriber_id'] ?? 0);
        if ($sidForInvoice > 0 && !isset($pendingInvoiceBySubscriber[$sidForInvoice])) {
          $pendingInvoiceBySubscriber[$sidForInvoice] = $invoiceRow;
        }
      }
    }
  }
}

$rxMatchSnapshot = function_exists('ua_receiver_match_snapshot') ? ua_receiver_match_snapshot($conn, 300, true) : [];
$rxMatchBySubscriber = function_exists('ua_receiver_match_by_subscriber') ? ua_receiver_match_by_subscriber($rxMatchSnapshot) : [];
$sectorNameMap = [];
try {
  if (function_exists('ua_rx_table_exists') && ua_rx_table_exists($conn, 'sectors')) {
    $rsSec = @$conn->query("SELECT id, name FROM sectors");
    if ($rsSec) while ($sr = $rsSec->fetch_assoc()) $sectorNameMap[(int)$sr['id']] = (string)($sr['name'] ?? '');
  }
} catch(Throwable $e) {}

$usageBatch = ua_monthly_usage_batch($conn, $subscribersRows, $cycle_month);
foreach ($subscribersRows as &$sub) {
  $ppp_user = $hasPppUserCol ? (string)($sub['ppp_username'] ?? '') : '';
  $uJoin = trim($ppp_user) !== '' ? $ppp_user : (string)($sub['username'] ?? '');
  $uJoin = preg_replace('/@.*/','', trim($uJoin));
  $id = (int)($sub['id'] ?? 0);
  $sub['_used_bytes'] = (int)($usageBatch['id:' . $id] ?? ($usageBatch['user:' . $uJoin] ?? 0));

  $lastSeenTs = 0;
  $lastDiscTs = 0;
  $mtLogoutTs = 0;
  $lastSeenRaw = $hasLastSeen ? trim((string)($sub['last_seen_at'] ?? '')) : '';
  if ($lastSeenRaw !== '' && $lastSeenRaw !== '0000-00-00 00:00:00') {
    $ts = strtotime($lastSeenRaw);
    if ($ts && $ts >= 946684800) $lastSeenTs = (int)$ts;
  }
  $lastDiscRaw = $hasLastDisc ? trim((string)($sub['last_disconnect_at'] ?? '')) : '';
  if ($lastDiscRaw !== '' && $lastDiscRaw !== '0000-00-00 00:00:00') {
    $ts = strtotime($lastDiscRaw);
    if ($ts && $ts >= 946684800) $lastDiscTs = (int)$ts;
  }
  $mtReality = $uJoin !== '' && isset($mtRealityMap[$uJoin]) ? $mtRealityMap[$uJoin] : null;
  $mtLogoutRaw = is_array($mtReality) ? trim((string)($mtReality['last_logged_out'] ?? '')) : '';
  if ($mtLogoutRaw !== '') {
    $ts = strtotime($mtLogoutRaw);
    if ($ts && $ts >= 946684800) $mtLogoutTs = (int)$ts;
  }
  $sub['_online_sort_ts'] = $lastSeenTs > 0 ? $lastSeenTs : PHP_INT_MAX;
  $sub['_offline_sort_ts'] = max($lastSeenTs, $lastDiscTs, $mtLogoutTs);

  $speedCandidates = [
    (string)($sub['current_speed'] ?? ''),
    (string)($sub['base_speed'] ?? ''),
    (string)($sub['ppp_profile'] ?? ''),
    (string)($sub['pppoe_profile'] ?? ''),
    (string)($sub['mt_profile'] ?? ''),
  ];
  $sub['_speed_sort'] = '';
  foreach ($speedCandidates as $speedCandidate) {
    $speedCandidate = trim($speedCandidate);
    if ($speedCandidate !== '') { $sub['_speed_sort'] = $speedCandidate; break; }
  }

  $autoSectorMatch = $rxMatchBySubscriber[$id] ?? null;
  $autoSectorName = trim((string)($sub['airmax_device_name'] ?? ''));
  if ($autoSectorName === '' && is_array($autoSectorMatch)) {
    $autoSectorName = trim((string)($autoSectorMatch['device_name'] ?? ''));
  }
  $manualSectorId = (int)($sub['sector_id'] ?? 0);
  $manualSectorName = $manualSectorId > 0 ? trim((string)($sectorNameMap[$manualSectorId] ?? '')) : '';
  $sub['_sector_sort'] = $autoSectorName !== '' ? $autoSectorName : $manualSectorName;
}
unset($sub);

if (!empty($subscribersRows)) {
  if ($sort === '') {
    usort($subscribersRows, static function(array $a, array $b): int {
      $ao = ((int)($a['is_online'] ?? 0) === 1) ? 1 : 0;
      $bo = ((int)($b['is_online'] ?? 0) === 1) ? 1 : 0;
      if ($ao !== $bo) return $bo <=> $ao;
      if ($ao === 1) {
        $cmp = ((int)($a['_online_sort_ts'] ?? PHP_INT_MAX)) <=> ((int)($b['_online_sort_ts'] ?? PHP_INT_MAX));
        if ($cmp !== 0) return $cmp;
        return ((int)($a['id'] ?? 0)) <=> ((int)($b['id'] ?? 0));
      }
      $cmp = ((int)($b['_offline_sort_ts'] ?? 0)) <=> ((int)($a['_offline_sort_ts'] ?? 0));
      if ($cmp !== 0) return $cmp;
      return ((int)($b['id'] ?? 0)) <=> ((int)($a['id'] ?? 0));
    });
  } elseif ($sort === 'alpha') {
    usort($subscribersRows, static function(array $a, array $b): int {
      $an = trim((string)($a['full_name'] ?? ''));
      $bn = trim((string)($b['full_name'] ?? ''));
      if ($an === '' && $bn !== '') return 1;
      if ($bn === '' && $an !== '') return -1;
      return strcasecmp($an, $bn);
    });
  } elseif ($sort === 'id') {
    usort($subscribersRows, static function(array $a, array $b): int {
      return ((int)($a['id'] ?? 0)) <=> ((int)($b['id'] ?? 0));
    });
  } elseif ($sort === 'speed') {
    usort($subscribersRows, static function(array $a, array $b): int {
      $as = trim((string)($a['_speed_sort'] ?? ''));
      $bs = trim((string)($b['_speed_sort'] ?? ''));
      if ($as === '' && $bs !== '') return 1;
      if ($bs === '' && $as !== '') return -1;
      $cmp = strnatcasecmp($as, $bs);
      if ($cmp !== 0) return $cmp;
      return ((int)($a['id'] ?? 0)) <=> ((int)($b['id'] ?? 0));
    });
  } elseif ($sort === 'sector') {
    usort($subscribersRows, static function(array $a, array $b): int {
      $as = trim((string)($a['_sector_sort'] ?? ''));
      $bs = trim((string)($b['_sector_sort'] ?? ''));
      if ($as === '' && $bs !== '') return 1;
      if ($bs === '' && $as !== '') return -1;
      $cmp = strnatcasecmp($as, $bs);
      if ($cmp !== 0) return $cmp;
      return ((int)($a['id'] ?? 0)) <=> ((int)($b['id'] ?? 0));
    });
  }
}

// Keep the full prepared set for the dedicated Android renderer, which owns
// its pagination. Desktop renders only the requested window so theme/style
// recalculation is bounded instead of growing with the entire subscriber base.
if (!$uaAndroidSubscribers && $viewMode === 'pages' && count($subscribersRows) > $perPage) {
  $subscribersRows = array_slice($subscribersRows, $pageOffset, $perPage);
}

/*
 * Android has a dedicated subscriber experience.  Keep the desktop page below
 * untouched: the mobile renderer reuses the prepared data, but owns its markup,
 * pagination and styling instead of trying to compress the desktop table.
 */
if ($uaAndroidSubscribers) {
  require __DIR__ . '/android_subscribers.php';
  exit;
}

$UA_PAGE_CSS = ['assets/css/ua_subscribers_page.css'];
require_once __DIR__ . '/header.php';
require_once __DIR__ . '/sidebar.php';
?>
<div class="content-wrapper ua-manual-page ua-sub-main-page subs-page-shell">
<section class="content p-3">
  <div class="subsx-body ua-page-v2" data-ua-viewport-fill data-ua-viewport-bottom-gap="18">

      <?php if ($flash_success !== '' || $flash_warning !== ''): ?>
        <div class="subs-flash-stack">
          <?php if ($flash_success !== ''): ?>
            <div class="alert alert-success ua-alert mb-0"><?= htmlspecialchars($flash_success, ENT_QUOTES, 'UTF-8') ?></div>
          <?php endif; ?>
          <?php if ($flash_warning !== ''): ?>
            <div class="alert alert-warning ua-alert mb-0"><?= htmlspecialchars($flash_warning, ENT_QUOTES, 'UTF-8') ?></div>
          <?php endif; ?>
        </div>
      <?php endif; ?>

      <div class="subsx-head ua-page-head">
        <?php
          $subsTotalForGauge = max(0, (int)($footerCounts['total'] ?? count($subscribersRows)));
          $subsOfflineForGauge = max(0, $subsTotalForGauge - (int)$mtActive);
          $subsDisabledForGauge = max(0, (int)($footerCounts['disabled'] ?? 0));
          $subsGaugeBase = max(1, $subsTotalForGauge);
          $subsGaugeGreenEnd = max(0, min(360, round(((int)$mtActive / $subsGaugeBase) * 360)));
          $subsGaugeBlueEnd = max($subsGaugeGreenEnd, min(360, $subsGaugeGreenEnd + round(((int)$unsafeSpeedCount / $subsGaugeBase) * 360)));
        ?>
        <div class="subsx-gauge" id="subsCenterGauge" data-green-end="<?= (int)$subsGaugeGreenEnd ?>" data-blue-end="<?= (int)$subsGaugeBlueEnd ?>" title="المتصلين: <?= (int)$mtActive ?> | المفصولين: <?= (int)$subsDisabledForGauge ?> | بلا تقييد: <?= (int)$unsafeSpeedCount ?>">
          <div class="subsx-gauge__stats">
            <div><b id="mtActiveCount"><?= (int)$mtActive ?></b><span>متصل</span><i class="is-green"></i></div>
            <div><b id="subsOfflineGaugeCount"><?= (int)$subsOfflineForGauge ?></b><span>غير متصل</span><i class="is-red"></i></div>
            <div><b id="subsDisabledGaugeCount"><?= (int)$subsDisabledForGauge ?></b><span>مفصول</span><i class="is-orange"></i></div>
            <div><b id="unsafeSpeedCount"><?= (int)$unsafeSpeedCount ?></b><span>غير مقيد</span><i class="is-blue"></i></div>
          </div>
          <div class="subsx-gauge__donut" aria-hidden="true"></div>
          <div class="subsx-gauge__center">
            <b id="subsTotalGaugeCount"><?= (int)$subsTotalForGauge ?></b>
            <span>مشترك</span>
          </div>
        </div>

          <div class="subsx-title ua-page-title">
            <h4 class="subsx-page-title">قائمة المشتركين</h4>
            <div class="subsx-meta">
              <span class="subsx-meta-chip">شهر الاستهلاك: <b><?= htmlspecialchars($current_month) ?></b></span>
              <span class="subsx-meta-chip">آخر تحديث: <b id="lastUpdateTxt"><?= htmlspecialchars($lastUpdate) ?></b></span>
              <button type="button" class="subsx-autorefresh-note" id="subsRefreshNote" title="تحديث الآن"><i class="fa fa-refresh" aria-hidden="true"></i> تحديث تلقائي</button>
            </div>
        <div class="subsx-topline ua-page-toolbar">
          <div class="subsx-tools">
            <div class="subsx-actions ua-page-actions">
            <a href="#" class="btn btn-secondary js-secure-modal-link ua-icon-action" data-go="subscribers_history_clear.php" data-title="تصفير سجلات المشتركين" title="تصفير سجلات المشتركين" aria-label="تصفير سجلات المشتركين"><i class="fa fa-eraser" aria-hidden="true"></i><span class="ua-visually-hidden">تصفير سجلات المشتركين</span></a>
            <?php if (!function_exists('has_perm') || has_perm('can_mikrotik_sync')): ?>
            <a href="sync_mikrotik.php?embed=1&ua_modal=1&ua_sub_modal=1" class="btn btn-info js-page-modal-link ua-icon-action" data-title="مزامنة مع MikroTik" title="مزامنة مع MikroTik" aria-label="مزامنة مع MikroTik"><i class="fa fa-refresh" aria-hidden="true"></i><span class="ua-visually-hidden">مزامنة مع MikroTik</span></a>
            <?php endif; ?>
            <a href="subscribers_import_excel.php?embed=1" class="btn btn-warning js-page-modal-link ua-icon-action" data-title="استيراد من Excel" title="استيراد من Excel" aria-label="استيراد من Excel"><i class="fa fa-file-excel-o" aria-hidden="true"></i><span class="ua-visually-hidden">استيراد من Excel</span></a>
            <a href="subscribers_import_id_cards.php?embed=1" class="btn btn-warning js-page-modal-link ua-icon-action" data-title="استيراد البطاقات الشخصية" title="استيراد البطاقات الشخصية" aria-label="استيراد البطاقات الشخصية"><i class="fa fa-id-card-o" aria-hidden="true"></i><span class="ua-visually-hidden">استيراد البطاقات الشخصية</span></a>
            <a href="subscribers_add.php?embed=1" class="btn btn-success js-page-modal-link ua-icon-action" data-title="إضافة مشترك" title="إضافة مشترك" aria-label="إضافة مشترك"><i class="fa fa-plus" aria-hidden="true"></i><span class="ua-visually-hidden">إضافة مشترك</span></a>
            <a href="subscribers_archive.php?embed=1" class="btn btn-secondary js-page-modal-link ua-icon-action" data-title="أرشيف المشتركين" title="أرشيف المشتركين" aria-label="أرشيف المشتركين"><i class="fa fa-archive" aria-hidden="true"></i><span class="ua-visually-hidden">أرشيف المشتركين</span></a>

            <details class="ua-head-menu ua-status-menu">
              <summary class="btn btn-secondary ua-icon-action <?= $filter !== 'all' ? 'ua-filter-active-icon' : '' ?>" title="عرض / تصفية المشتركين" aria-label="عرض / تصفية المشتركين">
                <i class="fa fa-users" aria-hidden="true"></i><span class="ua-visually-hidden">عرض / تصفية المشتركين</span>
              </summary>
              <div class="ua-head-dropdown">
                <a class="ua-head-item js-subs-filter <?= $filter==='all'?'is-active':'' ?>" data-ua-keep-back="1" data-filter="all" href="subscribers.php?filter=all<?= $qs ?>" title="عرض الكل" aria-label="عرض كل المشتركين"><i class="fa fa-list" aria-hidden="true"></i><span>عرض الكل</span></a>
                <a class="ua-head-item js-subs-filter <?= $filter==='online'?'is-active':'' ?>" data-ua-keep-back="1" data-filter="online" href="subscribers.php?filter=online<?= $qs ?>" title="المتصلين" aria-label="عرض المشتركين المتصلين"><i class="fa fa-wifi" aria-hidden="true"></i><span>المتصلين</span></a>
                <a class="ua-head-item js-subs-filter <?= $filter==='offline'?'is-active':'' ?>" data-ua-keep-back="1" data-filter="offline" href="subscribers.php?filter=offline<?= $qs ?>" title="غير المتصلين" aria-label="عرض المشتركين غير المتصلين"><i class="fa fa-toggle-off" aria-hidden="true"></i><span>غير المتصلين</span></a>
                <a class="ua-head-item js-subs-filter <?= $filter==='disabled'?'is-active':'' ?>" data-ua-keep-back="1" data-filter="disabled" href="subscribers.php?filter=disabled<?= $qs ?>" title="المفصولين" aria-label="عرض المشتركين المفصولين فقط"><i class="fa fa-ban" aria-hidden="true"></i><span>المفصولين</span></a>
                <a class="ua-head-item js-subs-filter <?= $filter==='limited'?'is-active':'' ?>" data-ua-keep-back="1" data-filter="limited" href="subscribers.php?filter=limited<?= $qs ?>" title="المخفضين" aria-label="عرض المشتركين المخفضين فقط"><i class="fa fa-tachometer" aria-hidden="true"></i><span>المخفضين</span></a>
              </div>
            </details>

            <form method="get" class="subsx-view-controls" id="subsViewControls">
              <input type="hidden" name="filter" value="<?= htmlspecialchars($filter, ENT_QUOTES, 'UTF-8') ?>">
              <?php if ($q !== ''): ?><input type="hidden" name="q" value="<?= htmlspecialchars($q, ENT_QUOTES, 'UTF-8') ?>"><?php endif; ?>
              <?php if ($viewMode === 'pages' && $perPage !== 50): ?><input type="hidden" name="per_page" value="<?= $perPage ?>"><?php endif; ?>
              <label for="subsViewMode">
                <span>طريقة العرض</span>
                <select id="subsViewMode" name="view" onchange="this.form.submit()">
                  <option value="all" <?= $viewMode === 'all' ? 'selected' : '' ?>>كامل المشتركين</option>
                  <option value="pages" <?= $viewMode === 'pages' ? 'selected' : '' ?>>عرض صفحات</option>
                </select>
              </label>
              <?php if ($viewMode === 'pages'): ?>
                <label for="subsPageSort">
                  <span>الفرز حسب</span>
                  <select id="subsPageSort" name="sort" onchange="this.form.submit()">
                    <option value="id" <?= $sort === 'id' ? 'selected' : '' ?>>المعرّف</option>
                    <option value="alpha" <?= $sort === 'alpha' ? 'selected' : '' ?>>أبجدي</option>
                    <option value="speed" <?= $sort === 'speed' ? 'selected' : '' ?>>السرعة</option>
                    <option value="sector" <?= $sort === 'sector' ? 'selected' : '' ?>>القطاع</option>
                  </select>
                </label>
              <?php endif; ?>
            </form>

            <details class="ua-head-menu ua-bulk-menu">
              <summary class="btn btn-secondary btn-bulk-mode ua-icon-action" title="التحديد الجماعي" aria-label="التحديد الجماعي">
                <i class="fa fa-check-square-o" aria-hidden="true"></i><span class="ua-visually-hidden">التحديد</span>
              </summary>
              <div class="ua-head-dropdown">
                <button type="button" id="bulkModeBtn" class="ua-head-item ua-head-button" title="تفعيل التحديد" aria-label="تفعيل التحديد الجماعي"><i class="fa fa-check-square-o" aria-hidden="true"></i><span id="bulkModeLabel">تفعيل التحديد</span></button>
                <button type="button" id="selectAllRowsHead" class="ua-head-item ua-head-button" title="تحديد الكل" aria-label="تحديد كل الصفوف الظاهرة"><i class="fa fa-check-square" aria-hidden="true"></i><span>تحديد الكل</span></button>
                <button type="button" id="clearAllRowsHead" class="ua-head-item ua-head-button" title="إلغاء التحديد" aria-label="إلغاء تحديد كل الصفوف"><i class="fa fa-square-o" aria-hidden="true"></i><span>إلغاء التحديد</span></button>
                <button type="button" id="showSelectedRowsHead" class="ua-head-item ua-head-button" title="عرض المحددين" aria-label="عرض المشتركين المحددين فقط" aria-pressed="false"><i class="fa fa-filter" aria-hidden="true"></i><span id="showSelectedRowsLabel">عرض المحددين</span></button>
                <button type="button" class="ua-head-item ua-head-button ua-bulk-action" data-selection-item="1" data-bulk-action="cut" title="فصل المحددين" aria-label="فصل المشتركين المحددين"><i class="fa fa-power-off" aria-hidden="true"></i><span>فصل المحددين</span></button>
                <button type="button" class="ua-head-item ua-head-button ua-bulk-action" data-selection-item="1" data-bulk-action="restore" title="استعادة المحددين" aria-label="استعادة المشتركين المحددين"><i class="fa fa-undo" aria-hidden="true"></i><span>استعادة المحددين</span></button>
                <button type="button" class="ua-head-item ua-head-button ua-bulk-action" data-selection-item="1" data-bulk-action="cancel" title="إلغاء اشتراك" aria-label="إلغاء اشتراك المحددين"><i class="fa fa-ban" aria-hidden="true"></i><span>إلغاء اشتراك</span></button>
                <button type="button" class="ua-head-item ua-head-button ua-bulk-action" data-selection-item="1" data-bulk-action="open_speed" title="سرعة مفتوحة" aria-label="تطبيق سرعة مفتوحة على المحددين"><i class="fa fa-tachometer" aria-hidden="true"></i><span>سرعة مفتوحة</span></button>
                <button type="button" id="bulkContractsBtn" class="ua-head-item ua-head-button" data-selection-item="1" title="طباعة العقود" aria-label="طباعة عقود المشتركين المحددين"><i class="fa fa-file-word-o" aria-hidden="true"></i><span>طباعة العقود</span></button>
                <button type="button" id="bulkSubscriberInfoBtn" class="ua-head-item ua-head-button" data-selection-item="1" title="تصدير معلومات المشتركين" aria-label="تصدير معلومات المشتركين المحددين"><i class="fa fa-id-card-o" aria-hidden="true"></i><span>تصدير المعلومات</span></button>
                <button type="button" id="bulkCustomPrintBtn" class="ua-head-item ua-head-button" data-selection-item="1" title="الطباعة المخصصة للمشتركين المحددين" aria-label="اختيار أعمدة وطباعة المشتركين المحددين"><i class="fa fa-print" aria-hidden="true"></i><span>الطباعة المخصصة</span></button>
                <?php if (!function_exists('has_perm') || has_perm('can_manage_messages')): ?>
                <button type="button" id="bulkSubscriberMessageBtn" class="ua-head-item ua-head-button" data-selection-item="1" title="إرسال رسالة للمشتركين المحددين" aria-label="إرسال رسالة للمشتركين المحددين"><i class="fa fa-envelope" aria-hidden="true"></i><span>إرسال رسالة</span></button>
                <?php endif; ?>
              </div>
            </details>
            </div>

            <div class="subsx-search-line ua-search-control">
              <div class="subsx-search-row">
                <div class="subsx-search-wrap">
                  <form method="get" class="ua-search-form">
                    <input type="hidden" name="filter" value="<?= htmlspecialchars($filter, ENT_QUOTES, 'UTF-8') ?>">
                    <input type="hidden" name="view" value="<?= htmlspecialchars($viewMode, ENT_QUOTES, 'UTF-8') ?>">
                    <?php if ($viewMode === 'pages' && $perPage !== 50): ?>
                      <input type="hidden" name="per_page" value="<?= $perPage ?>">
                    <?php endif; ?>
                    <?php if ($viewMode === 'pages' && $sort !== ''): ?>
                      <input type="hidden" name="sort" value="<?= htmlspecialchars($sort, ENT_QUOTES, 'UTF-8') ?>">
                    <?php endif; ?>

                    <div class="subsx-search-input-wrap">
                      <input
                        type="text"
                        name="q"
                        value="<?= htmlspecialchars($q, ENT_QUOTES, 'UTF-8') ?>"
                        id="subsSearchInput" data-ua-subscriber-search autocomplete="off" class="form-control ua-search-input"
                        placeholder="ID / اسم المستخدم / PPPoE / الاسم"
                      >
                      <button type="submit" class="search-icon-btn" aria-label="بحث" title="بحث">
                        <i class="fa fa-search"></i>
                      </button>
                    </div>
                  </form>
                </div>
                <span class="ua-selected-count-inline" id="selectedCount" aria-hidden="true">0 محدد</span>

              </div>
            </div>
          </div>
            </div>
          </div>
        </div>

      <div id="bulkToolbar" class="ua-bulk-toolbar" aria-hidden="true"></div>


      <div id="uaJobPanel" class="ua-job-panel">
        <div><b id="uaJobTitle">مهمة التنفيذ</b></div>
        <div class="ua-job-bar"><div id="uaJobFill" class="ua-job-fill"></div></div>
        <div id="uaJobText" class="ua-job-small">بانتظار بدء التنفيذ...</div>
      </div>
      <div id="uaBulkModalBackdrop" class="ua-bulk-modal-backdrop" role="dialog" aria-modal="true">
        <div class="ua-bulk-modal">
          <h5 id="uaBulkModalTitle">تأكيد العملية</h5>
          <p id="uaBulkModalText"></p>
          <div class="ua-bulk-modal-actions">
            <button type="button" class="btn btn-success" id="uaBulkModalOk">تنفيذ</button>
            <button type="button" class="btn btn-secondary" id="uaBulkModalCancel">إلغاء</button>
          </div>
        </div>
      </div>
      <div id="uaCustomPrintBackdrop" class="ua-custom-print-backdrop" role="dialog" aria-modal="true" aria-labelledby="uaCustomPrintTitle" hidden>
        <form class="ua-custom-print-dialog" id="uaCustomPrintForm" method="get" action="subscriber_custom_print.php">
          <input type="hidden" name="ids" id="uaCustomPrintIds" value="">
          <header>
            <div><i class="fa fa-print" aria-hidden="true"></i><span><b id="uaCustomPrintTitle">الطباعة المخصصة</b><small>اختر الأعمدة التي تريد إدراجها في الجدول</small></span></div>
            <button type="button" id="uaCustomPrintClose" aria-label="إغلاق"><i class="fa fa-times"></i></button>
          </header>
          <div class="ua-custom-print-columns">
            <?php foreach (scp_column_catalog() as $columnKey => $columnLabel): ?>
            <label><input type="checkbox" name="columns[]" value="<?= h($columnKey) ?>"><span><i class="fa fa-check"></i><?= h($columnLabel) ?></span></label>
            <?php endforeach; ?>
          </div>
          <section class="ua-custom-print-progress" id="uaCustomPrintProgress" hidden aria-live="polite" aria-busy="true">
            <div><span><i class="fa fa-cog fa-spin"></i><b>جاري تجهيز المعاينة</b></span><strong id="uaCustomPrintProgressCount">0 مشترك</strong></div>
            <div class="ua-custom-print-progress-track"><span></span></div>
            <p>يتم جمع البيانات وتجهيز الجدول، يرجى إبقاء الصفحة مفتوحة.</p>
          </section>
          <p id="uaCustomPrintError" class="ua-custom-print-error" aria-live="polite"></p>
          <footer>
            <button type="submit" class="btn btn-success"><i class="fa fa-table"></i> متابعة إلى المعاينة</button>
            <button type="button" class="btn btn-secondary" id="uaCustomPrintCancel">إلغاء</button>
          </footer>
        </form>
      </div>
      <div id="uaToastStack" class="ua-feedback-stack ua-toast-stack"></div>

      <div id="uaSecureModalBackdrop" class="ua-secure-backdrop" role="dialog" aria-modal="true">
        <div class="ua-secure-modal ua-auth-mode">
          <div class="ua-secure-head">
            <div class="ua-secure-title" id="uaSecureTitle">عملية محمية</div>
            <button type="button" class="ua-secure-close" id="uaSecureClose">إغلاق</button>
          </div>
          <div class="ua-secure-body">
            <form id="uaSecureAuth" class="ua-secure-auth">
              <div class="ua-secure-card">
                <label for="uaSecurePassword">كلمة مرور السوبر أدمن</label>
                <input type="password" id="uaSecurePassword" autocomplete="current-password" placeholder="أدخل كلمة المرور">
                <button type="submit" class="btn btn-warning w-100">متابعة</button>
                <div id="uaSecureError" class="ua-secure-error"></div>
              </div>
            </form>
            <iframe id="uaSecureFrame" class="ua-secure-frame" title="نافذة آمنة"></iframe>
            <div id="uaSecureHtml" class="ua-secure-html"></div>
          </div>
        </div>
      </div>


      <div class="subsx-table-card ua-table-wrap" data-ua-scroll-region>
        <table class="subsx-table ua-table" dir="rtl">
          <colgroup class="subs-table-columns">
            <col class="subs-col-select">
            <col class="subs-col-id">
            <col class="subs-col-name">
            <col class="subs-col-profile">
            <col class="subs-col-sector">
            <col class="subs-col-status">
            <col class="subs-col-last-seen">
            <col class="subs-col-last-cut">
            <col class="subs-col-usage">
            <col class="subs-col-actions">
          </colgroup>
          <thead>
            <tr>
              <th class="bulk-select-col ua-u-w-42"><input type="checkbox" id="masterCheckbox" class="bulk-master-checkbox" title="تحديد الكل"></th>
              <th>#</th>
              <th>الاسم</th>
              <th>PPPoE Profile</th>
              <th>القطاع</th>
              <th>الحالة</th>
              <th>آخر ظهور</th>
              <th>آخر فصل</th>
              <th>الاستهلاك</th>
              <th>إجراءات</th>
            </tr>
          </thead>
          <tbody id="subscribersTableBody">

          <?php if (!empty($subscribersRows)): ?>
            <?php foreach($subscribersRows as $sub): ?>
              <?php
@ini_set('display_errors','0');
error_reporting(E_ALL & ~E_WARNING & ~E_NOTICE);
                $id = (int)($sub['id'] ?? 0);
                $isOnline = ((int)($sub['is_online'] ?? 0) === 1);

                $ppp_user = $hasPppUserCol ? (string)($sub['ppp_username'] ?? '') : '';
                $uJoin = trim($ppp_user) !== '' ? $ppp_user : (string)($sub['username'] ?? '');
                $uJoin = preg_replace('/@.*/','', trim($uJoin));

                $mtReality = ($uJoin !== '' && isset($mtRealityMap[$uJoin]) && is_array($mtRealityMap[$uJoin])) ? $mtRealityMap[$uJoin] : [];
                $mtLastLogout = trim((string)($mtReality['last_logged_out'] ?? ''));
                $mtLastLogoutTs = $mtLastLogout !== '' ? strtotime($mtLastLogout) : false;

                $ls_raw = $hasLastSeen ? ($sub['last_seen_at'] ?? null) : null;
                // إذا الحساب غير متصل، تاريخ MikroTik الحقيقي من /ppp secret هو المرجع الأوثق.
                if (!$isOnline && $mtLastLogoutTs && $mtLastLogoutTs >= 946684800) {
                  $localLsTs = $ls_raw ? strtotime((string)$ls_raw) : false;
                  if (!$localLsTs || $mtLastLogoutTs >= $localLsTs) $ls_raw = date('Y-m-d H:i:s', $mtLastLogoutTs);
                }
                $ls_ts = $ls_raw ? strtotime($ls_raw) : false;
                $ls_valid = ($ls_ts && $ls_ts >= 946684800);

                $lastCut = '';
                if ($hasLastCut) $lastCut = (string)($sub['ppp_last_cut_at'] ?? '');
                if (!$lastCut) $lastCut = (string)($sub['last_disconnect_at'] ?? '');
                // العمود المعروض هنا يجب أن يعكس آخر خروج حقيقي من MikroTik عند توفره، لا تاريخ محلي قديم.
                if ($mtLastLogoutTs && $mtLastLogoutTs >= 946684800) {
                  $localCutTs = $lastCut ? strtotime($lastCut) : false;
                  if (!$localCutTs || $mtLastLogoutTs >= $localCutTs) $lastCut = date('Y-m-d H:i:s', $mtLastLogoutTs);
                }

                $ppp_user = $hasPppUserCol ? (string)($sub['ppp_username'] ?? '') : '';
                $uJoin = trim($ppp_user) !== '' ? $ppp_user : (string)($sub['username'] ?? '');
                $uJoin = preg_replace('/@.*/','', trim($uJoin));
                $used_bytes = array_key_exists('_used_bytes', $sub)
                  ? (int)($sub['_used_bytes'] ?? 0)
                  : (($uJoin !== '' && function_exists('q_get_monthly_used_bytes')) ? (int)q_get_monthly_used_bytes($conn, $uJoin, $cycle_month, $id) : 0);
                $used_gb = $used_bytes / 1073741824;

                $isDisabled = false;
                if ($hasPppDisabled && (int)($sub['ppp_is_disabled'] ?? 0) === 1) $isDisabled = true;
                if (!$isDisabled && isset($sub['mt_disabled']) && (int)($sub['mt_disabled'] ?? 0) === 1) $isDisabled = true;
                if (!$isDisabled && strtoupper((string)($sub['speed_state'] ?? '')) === 'CUT') $isDisabled = true;
                if ($uJoin !== '' && array_key_exists($uJoin, $mtDisabledMap)) $isDisabled = (bool)$mtDisabledMap[$uJoin];
                $billingPaused = (int)($sub['billing_paused'] ?? 0) === 1;
                if ($billingPaused) $isDisabled = true;
                $activeRow = ($uJoin !== '' && isset($pppActiveRowMap[$uJoin]) && is_array($pppActiveRowMap[$uJoin])) ? $pppActiveRowMap[$uJoin] : null;
                $statusInfo = $billingPaused
                  ? ['key'=>'service-paused', 'class'=>'is-stopped', 'label'=>'متوقف مؤقتاً']
                  : sub_status_info($isOnline, $isDisabled, $sub, $activeRow);
                $statusKey = (string)($statusInfo['key'] ?? ($isOnline ? 'online' : 'offline'));
                $statusClass = (string)($statusInfo['class'] ?? ($isOnline ? 'is-online' : 'is-offline'));
                $statusLabel = (string)($statusInfo['label'] ?? ($isOnline ? 'متصل' : 'غير متصل'));

                $go_view = 'subscribers_view.php?id=' . $id;
                $go_edit = 'subscribers_edit.php?id=' . $id;
                $go_cancel = 'subscribers_cancel.php?id=' . $id;
                $go_history = 'subscribers_history.php?id=' . $id;
                $go_router = 'subscriber_router_setup.php?subscriber_id=' . $id;
                $go_credit = 'subscriber_credit.php?id=' . $id;
                $go_billing_pause = 'subscriber_billing_pause.php?id=' . $id;
                $pendingInvoice = $pendingInvoiceBySubscriber[$id] ?? null;
                $pendingInvoiceId = is_array($pendingInvoice) ? (int)($pendingInvoice['id'] ?? 0) : 0;
                $pendingInvoiceAmount = is_array($pendingInvoice) ? (float)($pendingInvoice['remaining_amount'] ?? $pendingInvoice['amount'] ?? 0) : 0.0;
                $go_pay = $pendingInvoiceId > 0 ? ('pay_invoice.php?id=' . $pendingInvoiceId) : '#';

                $displayName = trim((string)($sub['full_name'] ?? ''));
                $displayPppUser = trim((string)($sub['ppp_username'] ?? ''));
                if ($displayName === '' || preg_match('/^SUB#\d+(?:\s+.*)?$/u', $displayName) || preg_match('/^SUB#AUTO\b/iu', $displayName)) {
                  $displayName = $displayPppUser !== '' ? $displayPppUser : (string)($sub['username'] ?? '');
                }
                $autoMatch = $rxMatchBySubscriber[$id] ?? [];
                $autoReceiverName = trim((string)($sub['airmax_device_name'] ?? ''));
                if ($autoReceiverName === '' && is_array($autoMatch)) $autoReceiverName = trim((string)($autoMatch['device_name'] ?? ''));
                $manualSectorName = '';
                $manualSectorId = (int)($sub['sector_id'] ?? 0);
                if ($manualSectorId > 0 && isset($sectorNameMap[$manualSectorId])) $manualSectorName = trim((string)$sectorNameMap[$manualSectorId]);
                $sectorLabel = $autoReceiverName !== '' ? $autoReceiverName : ($manualSectorName !== '' ? $manualSectorName : '—');
                $sectorAuto = $autoReceiverName !== '';
                $sectorTitle = $sectorAuto ? ('تمت المطابقة تلقائياً عبر MAC: ' . (string)($sub['airmax_matched_mac'] ?? ($autoMatch['mac'] ?? ''))) : ($manualSectorName !== '' ? 'قطاع محدد ببيانات المشترك' : 'لا توجد مطابقة بعد');
                $rowSearchText = ua_search_normalize_ar($id . ' ' . $displayName . ' ' . ((string)($sub['username'] ?? '')) . ' ' . ((string)($sub['ppp_username'] ?? '')) . ' ' . ((string)($sub['phone'] ?? '')) . ' ' . $sectorLabel);
              ?>
              <tr data-subscriber-row="<?= $id ?>" data-online="<?= $isOnline ? '1' : '0' ?>" data-disabled="<?= $isDisabled ? '1' : '0' ?>" data-service-paused="<?= $billingPaused ? '1' : '0' ?>" data-status-key="<?= htmlspecialchars($statusKey, ENT_QUOTES, 'UTF-8') ?>" data-online-sort="<?= $ls_valid ? (int)$ls_ts : PHP_INT_MAX ?>" data-offline-sort="<?= $ls_valid ? (int)$ls_ts : 0 ?>" data-search="<?= htmlspecialchars($rowSearchText, ENT_QUOTES, 'UTF-8') ?>">
                <td class="bulk-select-col"><input type="checkbox" class="row-selector" value="<?= $id ?>" data-open-speed-go="subscribers_speed_toggle_selected.php?action=open&ids=<?= $id ?>"></td>
                <td><?= $id ?></td>
                <td>
                  <div class="subs-name-cell">
                    <span class="subs-name-main"><?= htmlspecialchars($displayName, ENT_QUOTES, 'UTF-8') ?></span>
                  </div>
                </td>

                <td class="subs-profile-cell" dir="ltr"><?= htmlspecialchars(trim((string)($sub['ppp_profile'] ?? $sub['pppoe_profile'] ?? $sub['mt_profile'] ?? '')) ?: '—', ENT_QUOTES, 'UTF-8') ?></td>

                <td class="subs-sector-cell js-sector-cell" data-subscriber-id="<?= $id ?>" title="<?= htmlspecialchars($sectorTitle, ENT_QUOTES, 'UTF-8') ?>">
                  <span class="subs-sector-badge <?= $sectorLabel === '—' ? 'is-empty' : ($sectorAuto ? 'is-auto' : 'is-manual') ?>"><?= htmlspecialchars($sectorLabel, ENT_QUOTES, 'UTF-8') ?></span>
                </td>

                <td>
                  <span class="subs-status-pill <?= htmlspecialchars($statusClass, ENT_QUOTES, 'UTF-8') ?>" title="<?= htmlspecialchars($statusLabel, ENT_QUOTES, 'UTF-8') ?>" aria-label="<?= htmlspecialchars($statusLabel, ENT_QUOTES, 'UTF-8') ?>">
                    <span class="subs-status-dot js-online-indicator" data-subscriber-id="<?= $id ?>" data-online="<?= $isOnline ? '1' : '0' ?>" data-status-key="<?= htmlspecialchars($statusKey, ENT_QUOTES, 'UTF-8') ?>" title="<?= htmlspecialchars($statusLabel, ENT_QUOTES, 'UTF-8') ?>"></span>
                    <span><?= htmlspecialchars($statusLabel, ENT_QUOTES, 'UTF-8') ?></span>
                  </span>
                </td>

                <td>
                  <?php $lastSeenFull = $ls_valid ? date('Y-m-d H:i:s', $ls_ts) : ''; ?>
                  <span class="js-lastseen-label" data-subscriber-id="<?= $id ?>" data-online="<?= $isOnline ? '1' : '0' ?>" data-full="<?= htmlspecialchars($lastSeenFull, ENT_QUOTES, 'UTF-8') ?>">
                    <?php if ($isOnline): ?>
                      <span class="subs-now-badge">الآن</span>
                    <?php else: ?>
                      <span class="subs-muted-cell"><?= time_ago($ls_raw) ?></span>
                    <?php endif; ?>
                  </span>
                  <?php if (!$isOnline && $ls_valid): ?>
                    <a href="#"
                       class="lastseen-toggle js-lastseen-toggle"
                       data-subscriber-id="<?= $id ?>"
                       data-full="<?= htmlspecialchars($lastSeenFull, ENT_QUOTES, 'UTF-8') ?>"
                       title="عرض التاريخ بالتفصيل">?</a>
                  <?php endif; ?>
                </td>

                <td class="js-lastcut-cell subs-muted-cell subs-ltr-date-cell" data-subscriber-id="<?= $id ?>">
                  <?php
                    $cut_ts = $lastCut ? strtotime($lastCut) : false;
                    echo ($cut_ts && $cut_ts >= 946684800) ? date('Y-m-d H:i', $cut_ts) : '—';
                  ?>
                </td>

                <td class="js-usage-cell subs-usage-cell" data-subscriber-id="<?= $id ?>" data-used-bytes="<?= (int)$used_bytes ?>"><?= number_format($used_gb, 2) ?> GB</td>

                <td class="text-nowrap subs-row-actions ua-row-actions">
                  <div class="subs-actions-group">
                  <a class="btn btn-sm btn-primary js-secure-modal-link" href="#" data-go="<?= htmlspecialchars($go_view, ENT_QUOTES, 'UTF-8') ?>" data-title="عرض المشترك" title="عرض" aria-label="عرض المشترك <?= htmlspecialchars($displayName, ENT_QUOTES, 'UTF-8') ?>"><i class="fa fa-eye" aria-hidden="true"></i></a>
                  <a class="btn btn-sm btn-primary js-secure-modal-link" href="#" data-go="<?= htmlspecialchars($go_edit, ENT_QUOTES, 'UTF-8') ?>" data-title="تعديل المشترك" title="تعديل" aria-label="تعديل المشترك <?= htmlspecialchars($displayName, ENT_QUOTES, 'UTF-8') ?>"><i class="fa fa-pencil" aria-hidden="true"></i></a>
                  <?php if ($pendingInvoiceId > 0): ?>
                    <a class="btn btn-sm btn-success js-secure-modal-link" href="#" data-go="<?= htmlspecialchars($go_pay, ENT_QUOTES, 'UTF-8') ?>" data-title="تسديد فاتورة" title="تسديد فاتورة #<?= (int)$pendingInvoiceId ?><?= $pendingInvoiceAmount > 0 ? ' - ' . htmlspecialchars(number_format($pendingInvoiceAmount, 2), ENT_QUOTES, 'UTF-8') : '' ?>" aria-label="تسديد فاتورة للمشترك <?= htmlspecialchars($displayName, ENT_QUOTES, 'UTF-8') ?>"><i class="fa fa-credit-card" aria-hidden="true"></i></a>
                  <?php else: ?>
                    <span class="btn btn-sm btn-secondary disabled ua-subscriber-pay-disabled" title="لا توجد فاتورة مستحقة" aria-label="لا توجد فاتورة مستحقة للمشترك <?= htmlspecialchars($displayName, ENT_QUOTES, 'UTF-8') ?>"><i class="fa fa-credit-card" aria-hidden="true"></i></span>
                  <?php endif; ?>
                  <a class="btn btn-sm btn-info js-secure-modal-link" href="#" data-go="<?= htmlspecialchars($go_credit, ENT_QUOTES, 'UTF-8') ?>" data-title="الرصيد المقدم" title="رصيد مقدم" aria-label="إدارة الرصيد المقدم للمشترك <?= htmlspecialchars($displayName, ENT_QUOTES, 'UTF-8') ?>"><i class="fa fa-plus-circle" aria-hidden="true"></i></a>
                  <a class="btn btn-sm <?= $billingPaused ? 'btn-success' : 'btn-warning' ?> js-secure-modal-link" href="#" data-go="<?= htmlspecialchars($go_billing_pause, ENT_QUOTES, 'UTF-8') ?>" data-title="<?= $billingPaused ? 'إعادة تفعيل المشترك' : 'إيقاف المشترك مؤقتاً' ?>" title="<?= $billingPaused ? 'إعادة تفعيل المشترك' : 'إيقاف المشترك مؤقتاً' ?>" aria-label="<?= $billingPaused ? 'إعادة تفعيل المشترك ' : 'إيقاف المشترك مؤقتاً ' ?><?= htmlspecialchars($displayName, ENT_QUOTES, 'UTF-8') ?>"><i class="fa <?= $billingPaused ? 'fa-play' : 'fa-pause' ?>" aria-hidden="true"></i></a>
                  <a class="btn btn-sm btn-secondary js-secure-modal-link" href="#" data-go="<?= htmlspecialchars($go_history, ENT_QUOTES, 'UTF-8') ?>" data-title="سجل المشترك" title="السجل" aria-label="سجل المشترك <?= htmlspecialchars($displayName, ENT_QUOTES, 'UTF-8') ?>"><i class="fa fa-file-text-o" aria-hidden="true"></i></a>
                  <a class="btn btn-sm btn-info js-page-modal-link js-router-modal-link" href="<?= htmlspecialchars($go_router, ENT_QUOTES, 'UTF-8') ?>" data-title="إعداد الراوتر" title="إعداد الراوتر" aria-label="إعداد راوتر المشترك <?= htmlspecialchars($displayName, ENT_QUOTES, 'UTF-8') ?>"><svg class="ua-router-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 11V5m14 6V5"/><rect x="3" y="11" width="18" height="8" rx="2"/><circle cx="7" cy="15" r="1"/><circle cx="11" cy="15" r="1"/><path d="M15 15h3"/></svg></a>
                  <a class="btn btn-sm btn-danger js-cancel-btn js-secure-modal-link" data-bulk-action="cancel" href="#" data-go="<?= htmlspecialchars($go_cancel, ENT_QUOTES, 'UTF-8') ?>" data-title="إلغاء اشتراك" title="إلغاء اشتراك" aria-label="إلغاء اشتراك <?= htmlspecialchars($displayName, ENT_QUOTES, 'UTF-8') ?>"><i class="fa fa-ban" aria-hidden="true"></i></a>

                  <?php
                    $pppAction = $isDisabled ? 'restore' : 'cut';
                    $go_ppp = 'subscribers_ppp_toggle.php?id=' . $id . '&a=' . $pppAction;
                  ?>
                  <?php if ($billingPaused): ?>
                  <span class="btn btn-sm btn-secondary disabled" title="أعد تفعيل المشترك أولاً" aria-label="حساب المشترك <?= htmlspecialchars($displayName, ENT_QUOTES, 'UTF-8') ?> متوقف مؤقتاً">
                    <i class="fa fa-lock" aria-hidden="true"></i>
                  </span>
                  <?php else: ?>
                  <a class="btn btn-sm <?= $isDisabled ? 'btn-success' : 'btn-warning' ?> js-ppp-toggle-btn js-secure-modal-link"
                     data-subscriber-id="<?= $id ?>"
                     data-cut-go="subscribers_ppp_toggle.php?id=<?= $id ?>&a=cut"
                     data-restore-go="subscribers_ppp_toggle.php?id=<?= $id ?>&a=restore"
                     href="#" data-go="<?= htmlspecialchars($go_ppp, ENT_QUOTES, 'UTF-8') ?>" data-title="<?= $isDisabled ? 'استعادة' : 'فصل' ?>"
                     title="<?= $isDisabled ? 'استعادة' : 'فصل' ?>" aria-label="<?= $isDisabled ? 'استعادة المشترك ' : 'فصل المشترك ' ?><?= htmlspecialchars($displayName, ENT_QUOTES, 'UTF-8') ?>">
                    <i class="fa <?= $isDisabled ? 'fa-wifi' : 'fa-chain-broken' ?>" aria-hidden="true"></i>
                  </a>
                  <?php endif; ?>
                  </div>
                </td>
              </tr>
            <?php endforeach; ?>
          <?php else: ?>
            <tr><td colspan="9" class="text-center">لا يوجد بيانات</td></tr>
          <?php endif; ?>

          </tbody>
        </table>
      </div>
      <?php if ($viewMode === 'pages'): ?>
      <nav class="subsx-pagination" aria-label="صفحات المشتركين">
        <div class="subsx-pagination-summary">
          عرض <b><?= $pageFrom ?></b>–<b><?= $pageTo ?></b> من <b><?= $totalRows ?></b>
        </div>
        <div class="subsx-pagination-pages">
          <?php if ($currentPage > 1): ?>
            <a class="subsx-page-link" data-ua-keep-back="1" href="<?= htmlspecialchars($subscriberPageUrl($currentPage - 1), ENT_QUOTES, 'UTF-8') ?>" aria-label="الصفحة السابقة"><i class="fa fa-chevron-right" aria-hidden="true"></i></a>
          <?php endif; ?>
          <?php
            $pageNumbers = [1, $totalPages, $currentPage - 2, $currentPage - 1, $currentPage, $currentPage + 1, $currentPage + 2];
            $pageNumbers = array_values(array_unique(array_filter($pageNumbers, static fn($n) => $n >= 1 && $n <= $totalPages)));
            sort($pageNumbers);
            $previousPageNumber = 0;
          ?>
          <?php foreach ($pageNumbers as $pageNumber): ?>
            <?php if ($previousPageNumber > 0 && $pageNumber > $previousPageNumber + 1): ?><span class="subsx-page-gap">…</span><?php endif; ?>
            <a class="subsx-page-link <?= $pageNumber === $currentPage ? 'is-active' : '' ?>" data-ua-keep-back="1" href="<?= htmlspecialchars($subscriberPageUrl($pageNumber), ENT_QUOTES, 'UTF-8') ?>" <?= $pageNumber === $currentPage ? 'aria-current="page"' : '' ?>><?= $pageNumber ?></a>
            <?php $previousPageNumber = $pageNumber; ?>
          <?php endforeach; ?>
          <?php if ($currentPage < $totalPages): ?>
            <a class="subsx-page-link" data-ua-keep-back="1" href="<?= htmlspecialchars($subscriberPageUrl($currentPage + 1), ENT_QUOTES, 'UTF-8') ?>" aria-label="الصفحة التالية"><i class="fa fa-chevron-left" aria-hidden="true"></i></a>
          <?php endif; ?>
        </div>
        <form method="get" class="subsx-per-page-form">
          <input type="hidden" name="filter" value="<?= htmlspecialchars($filter, ENT_QUOTES, 'UTF-8') ?>">
          <input type="hidden" name="view" value="pages">
          <?php if ($q !== ''): ?><input type="hidden" name="q" value="<?= htmlspecialchars($q, ENT_QUOTES, 'UTF-8') ?>"><?php endif; ?>
          <?php if ($sort !== ''): ?><input type="hidden" name="sort" value="<?= htmlspecialchars($sort, ENT_QUOTES, 'UTF-8') ?>"><?php endif; ?>
          <label for="subsPerPage">عدد الصفوف</label>
          <select id="subsPerPage" name="per_page" onchange="this.form.submit()">
            <?php foreach ($perPageOptions as $option): ?>
              <option value="<?= $option ?>" <?= $option === $perPage ? 'selected' : '' ?>><?= $option ?></option>
            <?php endforeach; ?>
          </select>
        </form>
      </nav>
      <?php else: ?>
      <div class="subsx-all-summary" role="status">
        <i class="fa fa-list" aria-hidden="true"></i>
        عرض كامل المشتركين: <b><?= $totalRows ?></b> مشترك
      </div>
      <?php endif; ?>
  </div>
</section>
</div>

<script>
(function () {
  const AUTO_REFRESH_MS = 7000;
  const LIVE_STATUS_LEASE_MS = Math.max(5000, AUTO_REFRESH_MS - 1000);
  let qualityRequestInFlight = false;

  try {
    const cachedTop = JSON.parse(sessionStorage.getItem('ua_subscribers_top_v1') || 'null');
    if (cachedTop && typeof cachedTop === 'object') {
      const activeBox = document.getElementById('mtActiveCount');
      const updateBox = document.getElementById('lastUpdateTxt');
      if (activeBox) {
        const cachedOnline = parseInt(cachedTop.online || '0', 10);
        if (Number.isFinite(cachedOnline) && cachedOnline >= 0) activeBox.textContent = String(cachedOnline);
      }
      if (updateBox && cachedTop.updated_at) {
        updateBox.textContent = String(cachedTop.updated_at);
      }
    }
  } catch(e) {}

  function qualityClassForStatus(status) {
    switch ((status || '').trim()) {
      case 'ممتاز': return 'quality-text-excellent';
      case 'جيد جداً': return 'quality-text-vgood';
      case 'جيد': return 'quality-text-good';
      case 'متوسط': return 'quality-text-medium';
      case 'سيء': return 'quality-text-bad';
      default: return '';
    }
  }

  document.addEventListener('click', function (e) {
    const lastSeenBtn = e.target.closest('.lastseen-toggle');
    if (lastSeenBtn) {
      e.preventDefault();
      const d = lastSeenBtn.getAttribute('data-full') || '';
      if (d) alert('التاريخ الكامل: ' + d);
      return;
    }

    const btn = e.target.closest('.js-quality-btn');
    if (!btn) return;

    e.preventDefault();
    const id = btn.getAttribute('data-id');
    const isOnline = btn.getAttribute('data-online') === '1';
    const resultBox = document.getElementById('quality-result-' + id);
    if (!resultBox) return;

    if (!isOnline) {
      resultBox.className = 'quality-result';
      resultBox.textContent = 'المشترك غير متصل';
      return;
    }

    qualityRequestInFlight = true;
    btn.disabled = true;
    const oldText = btn.textContent;
    btn.textContent = 'جاري الفحص...';
    resultBox.className = 'quality-result';
    resultBox.textContent = '...';

    fetch('api/subscriber_connection_quality.php?id=' + encodeURIComponent(id), {
      credentials: 'same-origin',
      headers: { 'X-Requested-With': 'XMLHttpRequest' }
    })
    .then(async (res) => {
      let data = {};
      try { data = await res.json(); } catch (_) {}
      if (!res.ok || !data.ok) {
        throw new Error((data && data.message) ? data.message : 'فشل الفحص');
      }
      return data;
    })
    .then((data) => {
      if (!data.online) {
        resultBox.className = 'quality-result';
        resultBox.textContent = 'المشترك غير متصل';
        return;
      }
      const cls = qualityClassForStatus(data.status_text);
      resultBox.className = 'quality-result ' + cls;
      const ping = (typeof data.avg_ms !== 'undefined') ? (' — Ping ' + data.avg_ms + 'ms') : '';
      const loss = (typeof data.loss_percent !== 'undefined') ? (' — Loss ' + data.loss_percent + '%') : '';
      resultBox.textContent = (data.status_text || '—') + ping + loss;
    })
    .catch((err) => {
      resultBox.className = 'quality-result quality-text-bad';
      resultBox.textContent = err.message || 'تعذر الفحص';
    })
    .finally(() => {
      qualityRequestInFlight = false;
      btn.disabled = !isOnline;
      btn.textContent = oldText;
    });
  });


  let bgRefreshInFlight = false;
  let bgRefreshAbortController = null;
  let liveStatusRevision = '';
  let liveApplyGeneration = 0;
  let subscriberLastInteractionAt = 0;
  const liveStatusUrl = 'api/subscribers_live_status.php?filter=<?= rawurlencode($filter) ?>&q=<?= rawurlencode($q) ?>';
  let currentClientFilter = '<?= in_array($filter, ['online','offline','disabled','limited'], true) ? $filter : 'all' ?>';
  const subsServerSort = '<?= htmlspecialchars($sort, ENT_QUOTES, 'UTF-8') ?>';
  const subsSearchInput = document.getElementById('subsSearchInput');
  const subsSearchForm = subsSearchInput ? subsSearchInput.closest('form') : null;
  const liveStatusLeaseKey = 'ua_subscribers_live_status_lease_v1';
  const subscriberScrollStoreKey = 'ua_subscribers_scroll_state_v2';
  try {
    if ('scrollRestoration' in history) history.scrollRestoration = 'manual';
  } catch(_e) {}

  function markSubscriberInteraction() {
    subscriberLastInteractionAt = Date.now();
    liveApplyGeneration++;
    if (bgRefreshAbortController) {
      try { bgRefreshAbortController.abort(); } catch(_e) {}
    }
  }
  document.addEventListener('pointerdown', markSubscriberInteraction, true);
  document.addEventListener('submit', markSubscriberInteraction, true);
  document.addEventListener('keydown', function(e) {
    if (e.key === 'Tab' || e.key === 'Shift' || e.key === 'Control' || e.key === 'Alt' || e.key === 'Meta') return;
    markSubscriberInteraction();
  }, true);

  function uaSubscriberScrollElements() {
    const out = [];
    const add = function(el) {
      if (!el || out.indexOf(el) !== -1) return;
      out.push(el);
    };
    add(document.scrollingElement || document.documentElement || document.body);
    [
      document.documentElement,
      document.body,
      document.querySelector('.ua-page'),
      document.querySelector('.ua-main'),
      document.querySelector('.content-wrapper'),
      document.querySelector('.subs-list-wrap'),
      document.querySelector('.table-responsive'),
      document.getElementById('uaFixContentBody')
    ].forEach(add);
    document.querySelectorAll('[data-ua-scrollbox], .ua-scrollbox, .ua-page-body').forEach(add);
    return out.filter(Boolean);
  }

  function uaCaptureSubscriberScroll() {
    const items = [];
    uaSubscriberScrollElements().forEach(function(el, index) {
      items.push({
        index: index,
        top: Math.max(0, el.scrollTop || 0),
        left: Math.max(0, el.scrollLeft || 0)
      });
    });
    return {
      winX: Math.max(0, window.scrollX || window.pageXOffset || 0),
      winY: Math.max(0, window.scrollY || window.pageYOffset || 0),
      url: location.pathname + location.search,
      storedAt: Date.now(),
      items: items
    };
  }

  function uaRestoreSubscriberScroll(state) {
    if (!state) return;
    if (state.url && state.url !== (location.pathname + location.search)) return;
    const apply = function() {
      const els = uaSubscriberScrollElements();
      (state.items || []).forEach(function(item) {
        const el = els[item.index];
        if (!el) return;
        el.scrollTop = Math.max(0, parseInt(item.top || 0, 10) || 0);
        el.scrollLeft = Math.max(0, parseInt(item.left || 0, 10) || 0);
      });
      window.scrollTo(Math.max(0, parseInt(state.winX || 0, 10) || 0), Math.max(0, parseInt(state.winY || 0, 10) || 0));
    };
    [0, 30, 90, 180, 360, 720].forEach(function(delay) {
      setTimeout(function(){ requestAnimationFrame(apply); }, delay);
    });
  }

  function uaStoreSubscriberScroll(state) {
    if (!state) return state;
    try {
      sessionStorage.setItem(subscriberScrollStoreKey, JSON.stringify(state));
    } catch(_e) {}
    return state;
  }

  function uaLoadSubscriberScroll() {
    try {
      const raw = sessionStorage.getItem(subscriberScrollStoreKey);
      if (!raw) return null;
      const state = JSON.parse(raw);
      const age = Date.now() - (parseInt(state.storedAt || 0, 10) || 0);
      if (age < 0 || age > 30000) {
        sessionStorage.removeItem(subscriberScrollStoreKey);
        return null;
      }
      return state;
    } catch(_e) {
      try { sessionStorage.removeItem(subscriberScrollStoreKey); } catch(_x) {}
      return null;
    }
  }

  window.uaCaptureSubscriberScroll = uaCaptureSubscriberScroll;
  window.uaRestoreSubscriberScroll = uaRestoreSubscriberScroll;
  window.uaSubscriberLastScroll = null;
  window.uaRememberSubscriberScroll = function() {
    window.uaSubscriberLastScroll = uaCaptureSubscriberScroll();
    return uaStoreSubscriberScroll(window.uaSubscriberLastScroll);
  };
  window.uaRestoreLastSubscriberScroll = function() {
    uaRestoreSubscriberScroll(window.uaSubscriberLastScroll);
  };

  const savedSubscriberScroll = uaLoadSubscriberScroll();
  if (savedSubscriberScroll) {
    window.uaSubscriberLastScroll = savedSubscriberScroll;
    uaRestoreSubscriberScroll(savedSubscriberScroll);
    setTimeout(function() {
      try { sessionStorage.removeItem(subscriberScrollStoreKey); } catch(_e) {}
    }, 2200);
  }

  document.addEventListener('click', function(e) {
    const target = e.target && e.target.closest ? e.target.closest('button,input[type="submit"],a[href],summary') : null;
    if (target) window.uaRememberSubscriberScroll();
  }, true);
  document.addEventListener('submit', function() {
    window.uaRememberSubscriberScroll();
  }, true);
  window.addEventListener('pagehide', function() {
    window.uaRememberSubscriberScroll();
  });
  window.addEventListener('beforeunload', function() {
    window.uaRememberSubscriberScroll();
  });

  function acquireLiveStatusLease() {
    try {
      const now = Date.now();
      const raw = localStorage.getItem(liveStatusLeaseKey) || '';
      const last = parseInt(raw.split(':')[0] || '0', 10);
      if (last > 0 && (now - last) < LIVE_STATUS_LEASE_MS) return false;
      const token = String(now) + ':' + Math.random().toString(36).slice(2);
      localStorage.setItem(liveStatusLeaseKey, token);
      return localStorage.getItem(liveStatusLeaseKey) === token;
    } catch(e) {
      return true;
    }
  }

  function normalizeArabicText(v) {
    return String(v || '')
      .toLowerCase()
      .replace(/[\u064B-\u065F\u0670\u0640]/g, '')
      .replace(/[أإآٱ]/g, 'ا')
      .replace(/ى/g, 'ي')
      .replace(/ئ/g, 'ي')
      .replace(/ط¤/g, 'ظˆ')
      .replace(/ط©/g, 'ظ‡')
      .replace(/\u00a0/g, ' ')
      .replace(/\s+/g, ' ')
      .trim();
  }

  function searchMatches(hay, term) {
    if (!term) return true;
    if (hay.indexOf(term) !== -1) return true;
    const hayCompact = hay.replace(/\s+/g, '');
    const termCompact = term.replace(/\s+/g, '');
    if (termCompact && hayCompact.indexOf(termCompact) !== -1) return true;
    const parts = term.split(' ').filter(Boolean);
    return parts.length > 1 && parts.every(p => hay.indexOf(p) !== -1);
  }

  function sortDefaultSubscriberRows() {
    if (subsServerSort !== '') return;
    const body = document.getElementById('subscribersTableBody');
    if (!body) return;
    const rows = Array.from(body.querySelectorAll('tr[data-subscriber-row]'));
    rows.sort(function(a, b) {
      const ao = a.getAttribute('data-online') === '1';
      const bo = b.getAttribute('data-online') === '1';
      if (ao !== bo) return ao ? -1 : 1;
      const aid = parseInt(a.getAttribute('data-subscriber-row') || '0', 10) || 0;
      const bid = parseInt(b.getAttribute('data-subscriber-row') || '0', 10) || 0;
      if (ao) {
        const ats = parseInt(a.getAttribute('data-online-sort') || String(Number.MAX_SAFE_INTEGER), 10);
        const bts = parseInt(b.getAttribute('data-online-sort') || String(Number.MAX_SAFE_INTEGER), 10);
        if (ats !== bts) return ats - bts;
        return aid - bid;
      }
      const ats = parseInt(a.getAttribute('data-offline-sort') || '0', 10) || 0;
      const bts = parseInt(b.getAttribute('data-offline-sort') || '0', 10) || 0;
      if (ats !== bts) return bts - ats;
      return bid - aid;
    });
    const fragment = document.createDocumentFragment();
    rows.forEach(row => fragment.appendChild(row));
    body.appendChild(fragment);
  }
  window.sortDefaultSubscriberRows = sortDefaultSubscriberRows;
  // The server query already returns this exact order. Re-sorting hundreds of
  // rendered rows during the first paint only delays interaction with the page.

  function applyClientFilters() {
    const term = normalizeArabicText(subsSearchInput ? subsSearchInput.value : '');
    let visibleOnline = 0;
    document.querySelectorAll('#subscribersTableBody tr[data-subscriber-row]').forEach(function(row){
      const isOnline = row.getAttribute('data-online') === '1';
      const isDisabled = row.getAttribute('data-disabled') === '1';
      const statusKey = row.getAttribute('data-status-key') || '';
      const isSelected = !!row.querySelector('.row-selector:checked');
      const hay = normalizeArabicText(row.getAttribute('data-search') || '');
      const matchFilter = (currentClientFilter === 'all') || (currentClientFilter === 'online' && isOnline) || (currentClientFilter === 'offline' && !isOnline) || (currentClientFilter === 'disabled' && isDisabled) || (currentClientFilter === 'limited' && statusKey === 'limited');
      const matchTerm = searchMatches(hay, term);
      const show = matchFilter && matchTerm && (!window.uaShowSelectedOnly || isSelected);
      row.style.display = show ? '' : 'none';
      if (show && isOnline) visibleOnline++;
    });
    return visibleOnline;
  }

  document.querySelectorAll('.js-subs-filter').forEach(function(link){
    link.addEventListener('click', function(e){
      const parentMenu = this.closest('details');
      if (parentMenu) parentMenu.open = false;
    });
  });

  function updateLastUpdateText() {
    const box = document.getElementById('lastUpdateTxt');
    if (!box) return;
    const now = new Date();
    const pad = (n) => String(n).padStart(2, '0');
    box.textContent = now.getFullYear() + '-' + pad(now.getMonth()+1) + '-' + pad(now.getDate()) + ' ' + pad(now.getHours()) + ':' + pad(now.getMinutes()) + ':' + pad(now.getSeconds());
  }

  function formatUsageLabel(bytesValue) {
    const bytes = parseInt(bytesValue || 0, 10);
    if (!Number.isFinite(bytes) || bytes < 0) return null;
    const gb = bytes / 1073741824;
    return 'GB ' + gb.toFixed(2);
  }

  const subscriberStatusClasses = ['is-online', 'is-connected', 'is-offline', 'is-stopped', 'is-limited'];
  const subscriberRowCache = new Map();
  const subscriberLiveSignature = new Map();
  document.querySelectorAll('#subscribersTableBody tr[data-subscriber-row]').forEach(function(rowEl) {
    subscriberRowCache.set(String(rowEl.getAttribute('data-subscriber-row') || ''), {
      row: rowEl,
      dot: rowEl.querySelector('.js-online-indicator'),
      lastSeen: rowEl.querySelector('.js-lastseen-label'),
      cutCell: rowEl.querySelector('.js-lastcut-cell'),
      toggle: rowEl.querySelector('.js-ppp-toggle-btn'),
      sectorCell: rowEl.querySelector('.js-sector-cell'),
      usageCell: rowEl.querySelector('.js-usage-cell')
    });
  });

  function subscriberLiveRowSignature(row) {
    if (!row) return '';
    return [
      row.is_online, row.is_disabled, row.status_key, row.status_class, row.status_label,
      row.last_seen_label, row.last_seen_full, row.last_cut_display,
      row.receiver_name, row.receiver_title, row.receiver_auto,
      (typeof row.used_bytes !== 'undefined' ? row.used_bytes : row.usage_bytes), row.used_label
    ].map(function(value){ return value == null ? '' : String(value); }).join('\u001f');
  }

  function fallbackSubscriberStatus(isOnline, isDisabled) {
    if (isDisabled) return { key: 'stopped', cls: 'is-stopped', label: 'متوقف' };
    if (isOnline) return { key: 'online', cls: 'is-online', label: 'متصل بالإنترنت' };
    return { key: 'offline', cls: 'is-offline', label: 'غير متصل' };
  }

  function updateSubscriberLiveState(id, row) {
    id = String(id);
    const nextSignature = subscriberLiveRowSignature(row);
    if (subscriberLiveSignature.get(id) === nextSignature) return false;
    subscriberLiveSignature.set(id, nextSignature);
    const isOnline = String(row && row.is_online ? '1' : '0') === '1';
    const isDisabled = String(row && row.is_disabled ? '1' : '0') === '1';
    const fallbackStatus = fallbackSubscriberStatus(isOnline, isDisabled);
    const statusKey = row && row.status_key ? String(row.status_key) : fallbackStatus.key;
    const statusClass = row && row.status_class ? String(row.status_class) : fallbackStatus.cls;
    const statusLabel = row && row.status_label ? String(row.status_label) : fallbackStatus.label;
    const cached = subscriberRowCache.get(id) || {};
    const rowEl = cached.row || null;
    const previousOnline = rowEl ? rowEl.getAttribute('data-online') : null;
    const previousOfflineSort = rowEl ? rowEl.getAttribute('data-offline-sort') : null;
    const previousOnlineSort = rowEl ? rowEl.getAttribute('data-online-sort') : null;
    if (rowEl) {
      rowEl.setAttribute('data-online', isOnline ? '1' : '0');
      rowEl.setAttribute('data-disabled', isDisabled ? '1' : '0');
      rowEl.setAttribute('data-status-key', statusKey);
      if (row && row.last_seen_full) {
        const ts = Math.floor(Date.parse(String(row.last_seen_full).replace(' ', 'T')) / 1000);
        if (Number.isFinite(ts) && ts > 946684800) {
          rowEl.setAttribute(isOnline ? 'data-online-sort' : 'data-offline-sort', String(ts));
        }
      }
    }
    const dot = cached.dot || null;
    if (dot) {
      dot.dataset.online = isOnline ? '1' : '0';
      dot.dataset.statusKey = statusKey;
      dot.title = isOnline ? 'متصل' : 'غير متصل';
      const pill = dot.closest('.subs-status-pill');
      if (pill) {
        pill.classList.toggle('is-online', isOnline);
        pill.classList.toggle('is-offline', !isOnline);
        pill.title = isOnline ? 'متصل' : 'غير متصل';
        pill.setAttribute('aria-label', isOnline ? 'متصل' : 'غير متصل');
      }
    }
    if (dot) {
      const pill = dot.closest('.subs-status-pill');
      dot.title = statusLabel;
      if (pill) {
        subscriberStatusClasses.forEach(function(cls){ pill.classList.remove(cls); });
        pill.classList.add(statusClass);
        pill.title = statusLabel;
        pill.setAttribute('aria-label', statusLabel);
        const labelSpan = pill.querySelector('span:not(.subs-status-dot)');
        if (labelSpan) labelSpan.textContent = statusLabel;
      }
    }
    const lastSeen = cached.lastSeen || null;
    if (lastSeen) {
      const wasOnline = lastSeen.dataset.online === '1';
      lastSeen.dataset.online = isOnline ? '1' : '0';
      if (isOnline) {
        lastSeen.innerHTML = '<span class="subs-now-badge">الآن</span>';
      } else if (row && row.last_seen_label) {
        lastSeen.textContent = String(row.last_seen_label);
        if (row.last_seen_full) lastSeen.setAttribute('data-full', String(row.last_seen_full));
      } else if (wasOnline) {
        lastSeen.textContent = 'منذ لحظات';
      }
    }
    const cutCell = cached.cutCell || null;
    if (cutCell && row && typeof row.last_cut_display !== 'undefined') {
      cutCell.textContent = row.last_cut_display || '—';
    }
    const toggle = cached.toggle || null;
    if (toggle) {
      toggle.classList.remove('btn-success', 'btn-warning');
      toggle.classList.add(isDisabled ? 'btn-success' : 'btn-warning');
      toggle.title = isDisabled ? 'استعادة' : 'فصل';
      toggle.innerHTML = '<i class="fa ' + (isDisabled ? 'fa-wifi' : 'fa-chain-broken') + '" aria-hidden="true"></i>';
      const nextGo = isDisabled ? (toggle.dataset.restoreGo || toggle.getAttribute('data-go') || '') : (toggle.dataset.cutGo || toggle.getAttribute('data-go') || '');
      if (nextGo) {
        toggle.setAttribute('href', '#');
        toggle.setAttribute('data-go', nextGo);
      }
    }
    const sectorCell = cached.sectorCell || null;
    if (sectorCell && row && typeof row.receiver_name !== 'undefined') {
      const badge = sectorCell.querySelector('.subs-sector-badge');
      const receiverName = String(row.receiver_name || '—');
      sectorCell.title = row.receiver_title ? String(row.receiver_title) : receiverName;
      if (badge) {
        badge.textContent = receiverName;
        badge.classList.toggle('is-empty', receiverName === '—' || receiverName === '');
        badge.classList.toggle('is-auto', !!row.receiver_auto && receiverName !== '—' && receiverName !== '');
      }
    }
    const usageCell = cached.usageCell || null;
    if (usageCell) {
      const rawBytes = (row && (typeof row.used_bytes !== 'undefined')) ? row.used_bytes : ((row && (typeof row.usage_bytes !== 'undefined')) ? row.usage_bytes : usageCell.getAttribute('data-used-bytes'));
      const nextBytes = parseInt(rawBytes || '0', 10);
      if (Number.isFinite(nextBytes) && nextBytes >= 0) {
        usageCell.setAttribute('data-used-bytes', String(nextBytes));
        const label = (row && row.used_label) ? String(row.used_label) : formatUsageLabel(nextBytes);
        if (label) usageCell.textContent = label;
      }
    }
    return !!rowEl && (
      previousOnline !== rowEl.getAttribute('data-online') ||
      previousOfflineSort !== rowEl.getAttribute('data-offline-sort') ||
      previousOnlineSort !== rowEl.getAttribute('data-online-sort')
    );
  }

  function updateSubsGauge(total, online, unsafe) {
    const gauge = document.getElementById('subsCenterGauge');
    const totalNum = Math.max(1, parseInt(total || 0, 10));
    const onlineNum = Math.max(0, parseInt(online || 0, 10));
    const unsafeNum = Math.max(0, parseInt(unsafe || 0, 10));
    const greenEnd = Math.max(0, Math.min(360, Math.round((onlineNum / totalNum) * 360)));
    const blueEnd = Math.max(greenEnd, Math.min(360, greenEnd + Math.round((unsafeNum / totalNum) * 360)));
    if (gauge) {
      gauge.style.setProperty('--green-end', greenEnd + 'deg');
      gauge.style.setProperty('--blue-end', blueEnd + 'deg');
      gauge.title = 'المتصلين: ' + onlineNum + ' | بلا تقييد: ' + unsafeNum;
    }
  }

  (function initSubsGaugeRuntimeVars(){
    const gauge = document.getElementById('subsCenterGauge');
    if (!gauge) return;
    const greenEnd = Math.max(0, Math.min(360, parseInt(gauge.getAttribute('data-green-end') || '0', 10) || 0));
    const blueEnd = Math.max(greenEnd, Math.min(360, parseInt(gauge.getAttribute('data-blue-end') || String(greenEnd), 10) || greenEnd));
    gauge.style.setProperty('--green-end', greenEnd + 'deg');
    gauge.style.setProperty('--blue-end', blueEnd + 'deg');
  })();

  function subscriberPollingActive() {
    if (document.hidden) return false;
    if (document.body && document.body.classList.contains('ua-fix-open')) return false;
    if ((Date.now() - subscriberLastInteractionAt) < 1200) return false;
    return true;
  }

  function yieldSubscriberUi() {
    return new Promise(function(resolve) {
      if (typeof window.requestIdleCallback === 'function') {
        window.requestIdleCallback(function(){ resolve(); }, { timeout: 80 });
      } else {
        window.requestAnimationFrame(function(){ setTimeout(resolve, 0); });
      }
    });
  }

  async function applySubscriberSnapshot(rows, requestStartedAt, generation) {
    const ids = Object.keys(rows || {});
    const batchSize = 18;
    let onlineCount = 0;
    let orderingChanged = false;
    let changedCount = 0;
    for (let offset = 0; offset < ids.length; offset += batchSize) {
      if (generation !== liveApplyGeneration || subscriberLastInteractionAt > requestStartedAt) {
        return { cancelled: true, onlineCount: onlineCount, orderingChanged: false, changedCount: changedCount };
      }
      const end = Math.min(ids.length, offset + batchSize);
      for (let i = offset; i < end; i++) {
        const id = ids[i];
        const row = rows[id] || {};
        if (parseInt(row.is_online || 0, 10) === 1) onlineCount++;
        const previousSignature = subscriberLiveSignature.get(String(id)) || '';
        if (updateSubscriberLiveState(id, row)) orderingChanged = true;
        if (subscriberLiveSignature.get(String(id)) !== previousSignature) changedCount++;
      }
      if (end < ids.length) await yieldSubscriberUi();
    }
    return { cancelled: false, onlineCount: onlineCount, orderingChanged: orderingChanged, changedCount: changedCount };
  }

  async function refreshSubscribersInBackground(forceRefresh) {
    const force = forceRefresh === true;
    if (bgRefreshInFlight || qualityRequestInFlight) return;
    if (!force && !subscriberPollingActive()) return;
    if (!force && !acquireLiveStatusLease()) return;
    const requestStartedAt = Date.now();
    const applyGeneration = ++liveApplyGeneration;
    bgRefreshInFlight = true;
    const controller = new AbortController();
    bgRefreshAbortController = controller;
    const timeoutId = setTimeout(function(){ controller.abort(); }, 4000);
    const note = document.getElementById('subsRefreshNote');
    if (force && note) note.innerHTML = '<i class="fa fa-refresh fa-spin" aria-hidden="true"></i> تحديث تلقائي';
    try {
      const requestUrl = liveStatusUrl
        + '&since=' + encodeURIComponent(liveStatusRevision)
        + (force ? '&force=1' : '')
        + '&_=' + Date.now();
      const res = await fetch(requestUrl, {
        credentials: 'same-origin',
        cache: 'no-cache',
        signal: controller.signal,
        headers: { 'Accept': 'application/json', 'X-Requested-With': 'XMLHttpRequest' }
      });
      if (!res.ok) throw new Error('HTTP ' + res.status);
      const data = await res.json();
      if (!data || !data.ok) throw new Error('invalid');
      const responseRevision = data.revision ? String(data.revision) : '';
      if (data.unchanged) {
        if (responseRevision) liveStatusRevision = responseRevision;
        updateLastUpdateText();
        if (note) note.innerHTML = '<i class="fa fa-refresh" aria-hidden="true"></i> تحديث تلقائي';
        return;
      }
      if (!data.rows) throw new Error('invalid');
      if (subscriberLastInteractionAt > requestStartedAt) return;
      const applyResult = await applySubscriberSnapshot(data.rows, requestStartedAt, applyGeneration);
      if (applyResult.cancelled) return;
      let onlineCount = applyResult.onlineCount;
      if (applyResult.orderingChanged) {
        await yieldSubscriberUi();
        if (applyGeneration !== liveApplyGeneration || subscriberLastInteractionAt > requestStartedAt) return;
        sortDefaultSubscriberRows();
      }
      const hasActiveClientFilter = currentClientFilter !== 'all'
        || (subsSearchInput && subsSearchInput.value.trim() !== '')
        || !!window.uaShowSelectedOnly;
      const visibleOnline = (hasActiveClientFilter && applyResult.changedCount > 0) ? applyClientFilters() : onlineCount;
      const unifiedOnline = (data.stats && typeof data.stats.online !== 'undefined') ? parseInt(data.stats.online || 0, 10) : onlineCount;
      const activeBox = document.getElementById('mtActiveCount');
      if (activeBox) activeBox.textContent = String(currentClientFilter === 'online' || currentClientFilter === 'offline' || currentClientFilter === 'disabled' || currentClientFilter === 'limited' || (subsSearchInput && subsSearchInput.value.trim() !== '') ? visibleOnline : unifiedOnline);
      onlineCount = Number.isFinite(unifiedOnline) ? unifiedOnline : onlineCount;
      if (data.stats) {
        const disabledCount = (data.stats.financial_cut ?? data.stats.disabled);
        const footMap = {
          uaFootTotal: data.stats.total,
          uaFootOnline: data.stats.online,
          uaFootOffline: data.stats.offline,
          uaFootDisabled: disabledCount,
          uaFootUnsafe: data.stats.unsafe_speed,
          unsafeSpeedCount: data.stats.unsafe_speed,
          mtActiveCount: data.stats.online,
          subsOfflineGaugeCount: data.stats.offline,
          subsDisabledGaugeCount: disabledCount,
          subsTotalGaugeCount: data.stats.total
        };
        Object.keys(footMap).forEach(function(id){
          const el = document.getElementById(id);
          const v = footMap[id];
          if (el && typeof v !== 'undefined' && v !== null) el.textContent = String(parseInt(v || 0, 10));
        });
        const centerGauge = document.getElementById('subsCenterGauge');
        if (centerGauge) {
          const totalForGauge = Math.max(1, parseInt(data.stats.total || 1, 10));
          const onlineForGauge = Math.max(0, parseInt(data.stats.online || 0, 10));
          updateSubsGauge(totalForGauge, onlineForGauge, data.stats.unsafe_speed);
        }
        const unsafeWrap = document.getElementById('uaFootUnsafeWrap');
        if (unsafeWrap && typeof data.stats.unsafe_speed !== 'undefined') unsafeWrap.style.display = parseInt(data.stats.unsafe_speed || 0, 10) > 0 ? '' : 'none';
      }
      if (responseRevision) liveStatusRevision = responseRevision;
      updateLastUpdateText();
      try {
        sessionStorage.setItem('ua_subscribers_top_v1', JSON.stringify({
          ts: Date.now(),
          online: onlineCount,
          updated_at: (document.getElementById('lastUpdateTxt') || {}).textContent || ''
        }));
      } catch(e) {}
      if (note) note.innerHTML = '<i class="fa fa-refresh" aria-hidden="true"></i> تحديث تلقائي';
    } catch (e) {
      if (e && e.name === 'AbortError') {
        if (note) note.innerHTML = '<i class="fa fa-refresh" aria-hidden="true"></i> تحديث تلقائي';
      } else if (note) {
        note.textContent = 'تعذر تحديث الحالات الآن';
      }
    } finally {
      clearTimeout(timeoutId);
      if (bgRefreshAbortController === controller) bgRefreshAbortController = null;
      bgRefreshInFlight = false;
    }
  }

  applyClientFilters();

  const manualRefreshButton = document.getElementById('subsRefreshNote');
  if (manualRefreshButton) {
    manualRefreshButton.addEventListener('click', function() {
      refreshSubscribersInBackground(true);
    });
  }

  if (AUTO_REFRESH_MS > 0) {
    // Initial state is rendered by PHP. Starting another full refresh during
    // first paint made the page unresponsive; the first poll now occurs after
    // the configured seven-second interval (manual refresh remains immediate).
    setInterval(refreshSubscribersInBackground, AUTO_REFRESH_MS);
    document.addEventListener('visibilitychange', function(){ if (subscriberPollingActive()) refreshSubscribersInBackground(); });
  }
  window.addEventListener('pagehide', function(){
    if (bgRefreshAbortController) {
      try { bgRefreshAbortController.abort(); } catch(_e) {}
    }
  });


  const toggleBulkBtn = document.getElementById('bulkModeBtn');
  const bulkToolbar = document.getElementById('bulkToolbar');
  const masterCheckbox = document.getElementById('masterCheckbox');
  const selectedCount = document.getElementById('selectedCount');
  const showSelectedRowsBtn = document.getElementById('showSelectedRowsHead');
  const showSelectedRowsLabel = document.getElementById('showSelectedRowsLabel');
  const bulkActionButtons = [...document.querySelectorAll('.ua-bulk-action')];
  const modalBackdrop = document.getElementById('uaBulkModalBackdrop');
  const modalTitle = document.getElementById('uaBulkModalTitle');
  const modalText = document.getElementById('uaBulkModalText');
  const modalOk = document.getElementById('uaBulkModalOk');
  const modalCancel = document.getElementById('uaBulkModalCancel');

  function uaToast(msg){
    const stack = document.getElementById('uaToastStack');
    if(!stack) return;
    const el = document.createElement('div');
    el.className = 'ua-toast';
    el.textContent = msg;
    stack.appendChild(el);
    setTimeout(()=>{ el.style.opacity='0'; el.style.transform='translateY(8px)'; }, 2800);
    setTimeout(()=>el.remove(), 3300);
  }
  function uaConfirm(title, text){
    if(window.uaDialog){
      return window.uaDialog({title:title || 'تأكيد العملية', message:text || '', kind:'confirm', showCancel:true, confirmText:'موافق', cancelText:'إلغاء', icon:'fa-shield'});
    }
    return new Promise(resolve=>{
      if(!modalBackdrop){ resolve(false); return; }
      modalTitle.textContent = title || 'تأكيد العملية';
      modalText.textContent = text || '';
      modalBackdrop.style.display = 'flex';
      const done = (val)=>{ modalBackdrop.style.display='none'; modalOk.removeEventListener('click', ok); modalCancel.removeEventListener('click', cancel); resolve(val); };
      const ok = ()=>done(true), cancel = ()=>done(false);
      modalOk.addEventListener('click', ok); modalCancel.addEventListener('click', cancel);
    });
  }
  function selectedBoxes(){ return [...document.querySelectorAll('.row-selector:checked')]; }
  function selectedRows(){ return selectedBoxes().map(c=>c.closest('tr')).filter(Boolean); }
  document.getElementById('bulkContractsBtn')?.addEventListener('click',function(e){
    e.preventDefault();e.stopPropagation();
    const ids=selectedBoxes().map(c=>String(c.value||'')).filter(Boolean);
    if(!ids.length){uaToast('حدد مشترك واحد على الأقل.');return;}
    window.location.href='subscriber_contracts.php?bulk_ids='+encodeURIComponent(ids.join(','))+'#bulk-export';
  });
  document.getElementById('bulkSubscriberInfoBtn')?.addEventListener('click',function(e){
    e.preventDefault();e.stopPropagation();
    const ids=selectedBoxes().map(c=>String(c.value||'')).filter(Boolean);
    if(!ids.length){uaToast('حدد مشترك واحد على الأقل.');return;}
    window.location.href='subscriber_info_bulk_export.php?ids='+encodeURIComponent(ids.join(','));
  });
  const customPrintBackdrop=document.getElementById('uaCustomPrintBackdrop');
  const customPrintForm=document.getElementById('uaCustomPrintForm');
  const customPrintIds=document.getElementById('uaCustomPrintIds');
  const customPrintError=document.getElementById('uaCustomPrintError');
  const customPrintProgress=document.getElementById('uaCustomPrintProgress');
  const customPrintProgressCount=document.getElementById('uaCustomPrintProgressCount');
  function closeCustomPrint(){
    if(!customPrintBackdrop)return;
    customPrintBackdrop.hidden=true;
    document.body.classList.remove('ua-custom-print-open');
    if(customPrintError)customPrintError.textContent='';
    if(customPrintProgress)customPrintProgress.hidden=true;
    if(customPrintForm)customPrintForm.dataset.submitting='0';
  }
  document.getElementById('bulkCustomPrintBtn')?.addEventListener('click',function(e){
    e.preventDefault();e.stopPropagation();
    const ids=selectedBoxes().map(c=>String(c.value||'')).filter(Boolean);
    if(!ids.length){uaToast('حدد مشتركاً واحداً على الأقل.');return;}
    if(customPrintIds)customPrintIds.value=ids.join(',');
    customPrintForm?.querySelectorAll('input[name="columns[]"]').forEach(function(input){input.checked=false;});
    if(customPrintError)customPrintError.textContent='';
    if(customPrintBackdrop){customPrintBackdrop.hidden=false;document.body.classList.add('ua-custom-print-open');}
    closeHeadMenus(document.querySelector('details.ua-bulk-menu'));
  });
  document.getElementById('uaCustomPrintClose')?.addEventListener('click',closeCustomPrint);
  document.getElementById('uaCustomPrintCancel')?.addEventListener('click',closeCustomPrint);
  customPrintBackdrop?.addEventListener('click',function(e){if(e.target===customPrintBackdrop)closeCustomPrint();});
  customPrintForm?.addEventListener('submit',function(e){
    const checked=customPrintForm.querySelectorAll('input[name="columns[]"]:checked');
    if(!checked.length){e.preventDefault();if(customPrintError)customPrintError.textContent='اختر عموداً واحداً على الأقل.';return;}
    if(customPrintError)customPrintError.textContent='';
    if(customPrintForm.dataset.submitting==='1')return;
    e.preventDefault();
    customPrintForm.dataset.submitting='1';
    const selectedTotal=selectedBoxes().length;
    if(customPrintProgressCount)customPrintProgressCount.textContent=selectedTotal+' مشترك';
    if(customPrintProgress)customPrintProgress.hidden=false;
    customPrintForm.querySelectorAll('button[type="submit"]').forEach(function(control){control.disabled=true;});
    requestAnimationFrame(function(){setTimeout(function(){customPrintForm.submit();},60);});
  });
  window.addEventListener('pageshow',function(){
    if(!customPrintForm)return;
    customPrintForm.dataset.submitting='0';
    customPrintForm.querySelectorAll('button[type="submit"]').forEach(function(control){control.disabled=false;});
    if(customPrintProgress)customPrintProgress.hidden=true;
  });
  document.addEventListener('keydown',function(e){if(e.key==='Escape'&&customPrintBackdrop&&!customPrintBackdrop.hidden)closeCustomPrint();});
  document.getElementById('bulkSubscriberMessageBtn')?.addEventListener('click',function(e){
    e.preventDefault();e.stopPropagation();
    const ids=selectedBoxes().map(c=>String(c.value||'')).filter(Boolean);
    if(!ids.length){uaToast('حدد مشتركاً واحداً على الأقل.');return;}
    window.location.href='subscriber_messages_bulk.php?ids='+encodeURIComponent(ids.join(','));
  });
  window.uaShowSelectedOnly = false;
  function setShowSelectedOnly(active){
    window.uaShowSelectedOnly = !!active;
    if(showSelectedRowsBtn) {
      showSelectedRowsBtn.classList.toggle('is-active', window.uaShowSelectedOnly);
      showSelectedRowsBtn.setAttribute('aria-pressed', window.uaShowSelectedOnly ? 'true' : 'false');
    }
    if(showSelectedRowsLabel) showSelectedRowsLabel.textContent = window.uaShowSelectedOnly ? 'عرض الكل' : 'عرض المحددين';
    if(typeof applyClientFilters === 'function') applyClientFilters();
  }
  function updateSelectedCount(){
    const rows = selectedRows();
    const count = rows.length;
    if(selectedCount) {
      selectedCount.textContent = count + ' محدد';
      selectedCount.setAttribute('aria-hidden', document.body.classList.contains('ua-bulk-active') ? 'false' : 'true');
    }
    bulkActionButtons.forEach(function(btn){
      btn.setAttribute('aria-disabled', count > 0 ? 'false' : 'true');
      btn.dataset.selectionReady = count > 0 ? '1' : '0';
    });
  }
  function closeHeadMenus(except){
    document.querySelectorAll('details.ua-head-menu[open]').forEach(function(menu){
      if(!except || menu !== except) menu.removeAttribute('open');
    });
  }
  function setBulkMode(active){
    document.body.classList.toggle('ua-bulk-active', !!active);
    if(bulkToolbar) bulkToolbar.style.display = 'none';
    const label = document.getElementById('bulkModeLabel');
    if(label) label.textContent = active ? 'إغلاق التحديد' : 'تفعيل التحديد';
    if(!active){
      document.querySelectorAll('.row-selector').forEach(c=>c.checked=false);
      if(masterCheckbox) masterCheckbox.checked=false;
      setShowSelectedOnly(false);
    }
    updateSelectedCount();
  }
  document.addEventListener('click', function(e){
    const bulkBtn = e.target.closest('#bulkModeBtn');
    if(!bulkBtn) return;
    e.preventDefault();
    e.stopPropagation();
    e.stopImmediatePropagation();
    setBulkMode(!document.body.classList.contains('ua-bulk-active'));
    closeHeadMenus(document.querySelector('details.ua-bulk-menu'));
  }, true);
  document.getElementById('selectAllRowsHead')?.addEventListener('click', function(e){
    e.preventDefault();
    setBulkMode(true);
    document.querySelectorAll('.row-selector').forEach(c=>c.checked=true);
    if(masterCheckbox) masterCheckbox.checked=true;
    updateSelectedCount();
    closeHeadMenus(document.querySelector('details.ua-bulk-menu'));
  });
  document.getElementById('clearAllRowsHead')?.addEventListener('click', function(e){
    e.preventDefault();
    document.querySelectorAll('.row-selector').forEach(c=>c.checked=false);
    if(masterCheckbox) masterCheckbox.checked=false;
    setShowSelectedOnly(false);
    updateSelectedCount();
    closeHeadMenus(document.querySelector('details.ua-bulk-menu'));
  });
  showSelectedRowsBtn?.addEventListener('click', function(e){
    e.preventDefault();
    setBulkMode(true);
    if(window.uaShowSelectedOnly){
      setShowSelectedOnly(false);
    } else if(selectedBoxes().length > 0) {
      setShowSelectedOnly(true);
    } else {
      uaToast('حدد مشترك واحد على الأقل.');
    }
    updateSelectedCount();
    closeHeadMenus(document.querySelector('details.ua-bulk-menu'));
  });
  document.querySelectorAll('details.ua-head-menu').forEach(function(menu){
    menu.addEventListener('toggle', function(){ if(menu.open) closeHeadMenus(menu); });
  });
  document.addEventListener('click', function(e){
    if(!e.target.closest('details.ua-head-menu')) closeHeadMenus();
  });
  document.addEventListener('keydown', function(e){
    if(e.key === 'Escape') closeHeadMenus();
  });
  document.querySelectorAll('details.ua-head-menu .ua-head-item').forEach(function(item){
    item.addEventListener('click', function(){
      if(item.closest('.ua-bulk-menu')) return;
      setTimeout(function(){ closeHeadMenus(); }, 60);
    });
  });
  document.addEventListener('change', function(e){
    if(e.target.matches('.row-selector')) { updateSelectedCount(); if(window.uaShowSelectedOnly) applyClientFilters(); }
    if(e.target===masterCheckbox){ document.querySelectorAll('.row-selector').forEach(c=>c.checked=masterCheckbox.checked); updateSelectedCount(); if(window.uaShowSelectedOnly) applyClientFilters(); }
  });
  document.getElementById('selectAllRows')?.addEventListener('click',()=>{ setBulkMode(true); document.querySelectorAll('.row-selector').forEach(c=>c.checked=true); if(masterCheckbox) masterCheckbox.checked=true; updateSelectedCount(); if(window.uaShowSelectedOnly) applyClientFilters(); });
  document.getElementById('clearAllRows')?.addEventListener('click',()=>{ document.querySelectorAll('.row-selector').forEach(c=>c.checked=false); if(masterCheckbox) masterCheckbox.checked=false; setShowSelectedOnly(false); updateSelectedCount(); });
  let pendingBulkAction = null;
  async function runBulkApiAfterAuth(action, ids, btn){
    const fd = new FormData();
    fd.append('action', action);
    ids.forEach(id => fd.append('ids[]', id));
    const res = await fetch('api/subscribers_queue_enqueue.php', {method:'POST', body:fd, credentials:'same-origin', headers:{'X-Requested-With':'XMLHttpRequest'}});
    const data = await res.json().catch(()=>({ok:false,message:'استجابة غير مفهومة من الخادم'}));
    if(!res.ok || !data.ok) throw new Error((data && data.message) ? data.message : 'فشل إنشاء مهمة التنفيذ');
    uaToast(data.message || 'تم إنشاء مهمة التنفيذ');
    document.querySelectorAll('.row-selector').forEach(c=>c.checked=false);
    if(masterCheckbox) masterCheckbox.checked=false;
    setShowSelectedOnly(false);
    updateSelectedCount();
    if(data.job_id) watchSubscriberJob(data.job_id);
    return data;
  }
  async function watchSubscriberJob(jobId){
    const panel = document.getElementById('uaJobPanel');
    const title = document.getElementById('uaJobTitle');
    const text = document.getElementById('uaJobText');
    const fill = document.getElementById('uaJobFill');
    if(panel) panel.style.display='block';
    if(title) title.textContent = 'مهمة التنفيذ #' + jobId;
    let ticks = 0;
    async function poll(){
      ticks++;
      try{ fetch('api/subscribers_queue_run.php', {method:'POST', credentials:'same-origin', headers:{'X-Requested-With':'XMLHttpRequest'}}).catch(()=>{}); }catch(_e){}
      try{
        const r = await fetch('api/subscribers_queue_status.php?job_id=' + encodeURIComponent(jobId), {credentials:'same-origin', headers:{'X-Requested-With':'XMLHttpRequest'}});
        const j = await r.json();
        if(j && j.ok && j.job){
          const job = j.job;
          const total = Math.max(1, parseInt(job.total || 0,10));
          const processed = parseInt(job.processed || 0,10);
          const pct = Math.max(0, Math.min(100, Math.round((processed / total) * 100)));
          if(fill) fill.style.width = pct + '%';
          if(text) text.textContent = (job.message || 'قيد التنفيذ') + ' — ' + processed + ' / ' + total + ' | نجاح: ' + (job.succeeded||0) + ' | فشل: ' + (job.failed||0);
          if(['done','partial','failed','cancelled'].includes(String(job.status))){
            uaToast(job.message || 'انتهت مهمة التنفيذ');
            await refreshSubscribersInBackground();
            return;
          }
        }
      }catch(_e){}
      if(ticks < 120) setTimeout(poll, 2500);
    }
    poll();
  }
  function submitBulkAction(action, ids){
    if(!ids || !ids.length) { uaToast('لا توجد حسابات صالحة للتنفيذ.'); return; }
    pendingBulkAction = { action, ids };
    secureCurrentGo = 'api/subscribers_queue_enqueue.php';
    secureDisplayGo = secureCurrentGo;
    if(secureTitle) secureTitle.textContent = 'تنفيذ جماعي محمي';
    uaSetSecureMode('auth');
    if(secureBackdrop) secureBackdrop.style.display='flex';
    document.body.classList.add('ua-modal-open');
    if(secureFrame){ secureFrame.src='about:blank'; secureFrame.style.display='none'; }
    if(secureHtml){ secureHtml.innerHTML=''; secureHtml.style.display='none'; }
    if(secureAuth) secureAuth.style.display='flex';
    if(secureError) secureError.textContent='';
    setTimeout(()=>securePassword?.focus({preventScroll:true}), 80);
  }

  document.querySelectorAll('.ua-bulk-action').forEach(function(btn){
    btn.addEventListener('click', async function(e){
      e.preventDefault();
      e.stopPropagation();

      const rows = selectedRows();
      const action = btn.getAttribute('data-bulk-action') || '';
      if(!rows.length){ uaToast('حدد مشترك واحد على الأقل.'); return; }
      if(!action){ uaToast('اختر الإجراء المطلوب.'); return; }
      if(action === 'restore' && rows.some(row => row.getAttribute('data-disabled') !== '1')){
        uaToast('استعادة لا تتفعل إذا كان ضمن التحديد حسابات ليست بوضع الاستعادة.');
        return;
      }

      const ids = rows.map(row => row.querySelector('.row-selector')?.value).filter(Boolean);
      if(!ids.length){ uaToast('تعذر قراءة المشتركين المحددين.'); return; }

      const label = ({cut:'فصل',restore:'استعادة',cancel:'إلغاء اشتراك',open_speed:'سرعة مفتوحة'})[action] || 'الإجراء';
      closeHeadMenus();
      const ok = await uaConfirm('تأكيد '+label, 'سيتم تنفيذ نفس سياسة الحماية وكلمة مرور السوبر أدمن على عدد ' + ids.length + ' مشترك.');
      if(!ok) return;

      submitBulkAction(action, ids);
    });
  });

  updateSelectedCount();

  // تم تعطيل نظام الحماية القديم هنا عمداً لمنع تداخل التعتيم والـ iframe.
  // النظام الوحيد الفعال الآن موجود في السكربت النهائي أسفل الصفحة.

})();
</script>

<script>
(function(){
  'use strict';
  const d=document;
  function toast(msg){
    let s=d.getElementById('uaFixToast');
    if(!s){s=d.createElement('div');s.id='uaFixToast';s.className='ua-feedback-stack ua-fix-toast-stack';d.body.appendChild(s)}
    const x=d.createElement('div');x.className='ua-fix-toast-item';x.textContent=msg;s.appendChild(x);setTimeout(()=>x.remove(),4500)
  }
  function css(){try{d.body&&d.body.classList.add('ua-subscribers-modal-runtime')}catch(_e){}}
  function cssTheme(){try{d.body&&d.body.classList.add('ua-subscribers-modal-runtime')}catch(_e){}}
  function cssPopupPolish(){try{d.body&&d.body.classList.add('ua-subscribers-modal-runtime')}catch(_e){}}
  function cssModalShell(){try{d.body&&d.body.classList.add('ua-subscribers-modal-runtime')}catch(_e){}}
  function authDesign(){let box=d.getElementById('uaFixBox');if(!box||box.getAttribute('data-prompt-design')==='1')return;box.setAttribute('data-prompt-design','1');box.innerHTML='<form id="uaFixForm"><div class="uaFixBody"><div class="uaFixIcon" aria-hidden="true">🔒</div><div class="uaFixPromptTitle" id="uaFixTitle">إدخال كلمة المرور</div><label for="uaFixPwd">كلمة المرور</label><div class="uaFixInputWrap"><input type="password" id="uaFixPwd" autocomplete="current-password" placeholder="••••••••"><button type="button" class="uaFixEye" id="uaFixEye" title="إظهار/إخفاء" aria-label="إظهار أو إخفاء كلمة المرور">👁</button></div><div class="uaFixActions"><button type="button" class="uaFixCancel" id="uaFixClose">إلغاء</button><button class="uaFixSubmit" id="uaFixSubmit" type="submit">موافق</button></div><div class="uaFixErr" id="uaFixErr"></div></div></form>';let close=d.getElementById('uaFixClose');if(close)close.onclick=closeAuth;let eye=d.getElementById('uaFixEye'),pwd=d.getElementById('uaFixPwd');if(eye&&pwd)eye.onclick=function(){pwd.type=pwd.type==='password'?'text':'password';eye.textContent=pwd.type==='password'?'👁':'◉';pwd.focus({preventScroll:true})}}
  function init(){css();cssTheme();cssPopupPolish();cssModalShell();if(!d.getElementById('uaFixAuth')){let a=d.createElement('div');a.id='uaFixAuth';a.innerHTML='<div id="uaFixBox"><div class="uaFixHead"><div class="uaFixTitle" id="uaFixTitle">عملية محمية</div><button type="button" class="uaFixClose" id="uaFixClose">إغلاق</button></div><form id="uaFixForm"><div class="uaFixBody"><label>كلمة مرور السوبر أدمن</label><input type="password" id="uaFixPwd" autocomplete="current-password" placeholder="أدخل كلمة المرور"><button class="uaFixSubmit" id="uaFixSubmit" type="submit">متابعة</button><div class="uaFixErr" id="uaFixErr"></div></div></form></div>';d.body.appendChild(a);d.getElementById('uaFixClose').onclick=closeAuth;a.onclick=e=>{if(e.target===a)closeAuth()}}authDesign();
    if(!d.getElementById('uaFixContent')){let c=d.createElement('div');c.id='uaFixContent';c.innerHTML='<div id="uaFixContentBox"><div class="uaFixHead uaFixWinHead"><div class="uaFixTitle" id="uaFixContentTitle">عرض</div><div class="uaFixWinControls"><button type="button" class="uaFixWinBtn uaFixX" id="uaFixContentClose" title="إغلاق" aria-label="إغلاق">×</button><button type="button" class="uaFixWinBtn uaFixMax" id="uaFixContentMax" title="تكبير" aria-label="تكبير">□</button></div></div><div id="uaFixContentBody"></div></div>';d.body.appendChild(c);d.getElementById('uaFixContentClose').onclick=closeContent;d.getElementById('uaFixContentMax').onclick=function(){let b=d.getElementById('uaFixContentBox');if(b)b.classList.toggle('uaFixMaximized')};c.onclick=e=>{if(e.target===c){e.preventDefault();e.stopPropagation();}}}
  }
  let pending=null;
  function hideOld(){['uaSecureModalBackdrop','uaBulkModalBackdrop'].forEach(id=>{let x=d.getElementById(id);if(x)x.style.display='none'});d.body.classList.remove('ua-modal-open')}
  function forceClear(){let ss=window.uaRememberSubscriberScroll?window.uaRememberSubscriberScroll():null;try{['uaFixAuth','uaFixContent','uaSecureModalBackdrop','uaBulkModalBackdrop'].forEach(id=>{let x=d.getElementById(id);if(x){x.style.display='none'; if(id==='uaFixContent'){let b=d.getElementById('uaFixContentBody'); if(b)b.innerHTML=''}}});d.body.classList.remove('ua-fix-open','ua-modal-open');let p=d.getElementById('uaFixPwd');if(p)p.value='';let e=d.getElementById('uaFixErr');if(e)e.textContent=''}catch(_e){}finally{if(window.uaRestoreSubscriberScroll)window.uaRestoreSubscriberScroll(ss||window.uaSubscriberLastScroll)}}
  function closeAuth(){let ss=window.uaRememberSubscriberScroll?window.uaRememberSubscriberScroll():null;let a=d.getElementById('uaFixAuth');if(a)a.style.display='none';d.body.classList.remove('ua-fix-open');let p=d.getElementById('uaFixPwd');if(p)p.value='';let e=d.getElementById('uaFixErr');if(e)e.textContent='';pending=null;if(window.uaRestoreSubscriberScroll)window.uaRestoreSubscriberScroll(ss||window.uaSubscriberLastScroll)}
  function closeContent(){let ss=window.uaRememberSubscriberScroll?window.uaRememberSubscriberScroll():null;let c=d.getElementById('uaFixContent');if(c)c.style.display='none';d.body.classList.remove('ua-fix-open');let box=d.getElementById('uaFixContentBox');if(box)box.classList.remove('uaFixMaximized');let b=d.getElementById('uaFixContentBody');if(b)b.innerHTML='';if(window.uaRestoreSubscriberScroll)window.uaRestoreSubscriberScroll(ss||window.uaSubscriberLastScroll)}
  function openAuth(title,go,mode,extra){let ss=window.uaRememberSubscriberScroll?window.uaRememberSubscriberScroll():null;init();hideOld();let content=d.getElementById('uaFixContent');if(content){content.style.display='none';let body=d.getElementById('uaFixContentBody');if(body)body.innerHTML=''}pending={title:title||'عملية محمية',go:go||'',mode:mode||'open',extra:extra||{}};d.getElementById('uaFixTitle').textContent=pending.title;d.getElementById('uaFixErr').textContent='';d.getElementById('uaFixAuth').style.display='flex';d.body.classList.add('ua-fix-open');if(window.uaRestoreSubscriberScroll)window.uaRestoreSubscriberScroll(ss);setTimeout(()=>{try{d.getElementById('uaFixPwd').focus({preventScroll:true})}catch(e){}},60)}
  function addEmbed(go){try{let u=new URL(go,location.href);u.searchParams.set('embed','1');u.searchParams.set('ua_modal','1');u.searchParams.set('ua_sub_modal','1');return u.pathname.split('/').pop()+u.search+u.hash}catch(e){return go+(go.indexOf('?')>=0?'&':'?')+'embed=1&ua_modal=1&ua_sub_modal=1'}}
  function viewUrl(go){try{let u=new URL(go,location.href),f=(u.pathname.split('/').pop()||'').toLowerCase(),id=u.searchParams.get('id')||'';if(f==='subscribers_view.php'&&id)return 'api/subscriber_modal_view.php?id='+encodeURIComponent(id)+'&go='+encodeURIComponent(go)}catch(e){}return ''}
  function modalPageAllowed(go){try{let f=(new URL(go,location.href).pathname.split('/').pop()||'').toLowerCase();return ['subscribers_view.php','subscribers_edit.php','subscribers_history.php','subscribers_history_clear.php','subscribers_distribution.php','subscribers_archive.php','subscribers_import_excel.php','subscribers_import_id_cards.php','subscribers_add.php','subscribers_speed_toggle_selected.php','subscriber_router_setup.php','subscriber_credit.php','subscriber_billing_pause.php','android_router_setup.php','sync_mikrotik.php','mikrotik_import.php','import_ppp_from_mikrotik.php','finance_payments.php','pay_invoice.php'].includes(f)}catch(e){return false}}
  function direct(go){try{let f=(new URL(go,location.href).pathname.split('/').pop()||'').toLowerCase();return f==='subscribers_ppp_toggle.php'||f==='subscribers_cancel.php'}catch(e){return false}}
  function modalTitleFromUrl(go, fallback){
    try{
      const u=new URL(go, location.href), f=(u.pathname.split('/').pop()||'').toLowerCase();
      const map={
        'subscribers_view.php':'عرض المشترك',
        'subscribers_edit.php':'تعديل المشترك',
        'subscribers_history.php':'سجل المشترك',
        'subscribers_history_clear.php':'تصفير السجلات',
        'subscribers_distribution.php':'توزيع وتسديد',
        'subscribers_archive.php':'أرشيف المشتركين',
        'subscribers_import_excel.php':'استيراد من Excel',
        'subscribers_add.php':'إضافة مشترك',
        'subscribers_cancel.php':'إلغاء اشتراك',
        'subscribers_speed_toggle_selected.php':'سرعة مفتوحة',
        'sync_mikrotik.php':'مزامنة مع MikroTik',
        'mikrotik_import.php':'استيراد من MikroTik',
        'import_ppp_from_mikrotik.php':'استيراد PPP من MikroTik',
        'finance_payments.php':'المدفوعات',
        'pay_invoice.php':'تسديد فاتورة',
        'subscriber_credit.php':'الرصيد المقدم',
        'subscriber_billing_pause.php':'الإيقاف المؤقت للمشترك'
      };
      return map[f] || fallback || 'نافذة';
    }catch(e){return fallback || 'نافذة'}
  }
  function openSubscriberModalPage(go,title){
    if(!go) return false;
    let vu=viewUrl(go);
    if(vu){
      fetch(vu,{credentials:'same-origin',headers:{'X-Requested-With':'XMLHttpRequest'}})
        .then(r=>r.text().then(t=>({ok:r.ok,text:t})))
        .then(x=>{ if(!x.ok) throw new Error(x.text||'فشل تحميل البيانات'); showContent(title||modalTitleFromUrl(go,'عرض المشترك'),x.text,true,go); })
        .catch(ex=>toast(ex&&ex.message?ex.message:'فشل تحميل البيانات'));
      return true;
    }
    if(modalPageAllowed(go)){
      showContent(title||modalTitleFromUrl(go,'نافذة'),addEmbed(go),false,go);
      return true;
    }
    return false;
  }
  function uiConfirm(title,msg){
    if(window.uaDialog){
      return window.uaDialog({title:title || 'تأكيد العملية', message:msg || '', kind:'confirm', showCancel:true, confirmText:'موافق', cancelText:'إلغاء', icon:'fa-shield'});
    }
    return new Promise(resolve=>{
      init();hideOld();let old=d.getElementById('uaFixConfirm');if(old)old.remove();let esc=x=>String(x||'').replace(/[&<>]/g,m=>({'&':'&amp;','<':'&lt;','>':'&gt;'}[m]));let b=d.createElement('div');b.id='uaFixConfirm';b.className='ua-global-dialog-backdrop se-mt-confirm-backdrop';
      b.innerHTML='<div class="se-mt-confirm-card"><div class="se-mt-confirm-icon"><i class="fa fa-shield"></i></div><div class="se-mt-confirm-title">'+esc(title||'تأكيد العملية')+'</div><div class="se-mt-confirm-body">'+esc(msg||'')+'<div class="se-mt-confirm-actions"><button type="button" id="uaFixConfirmOk" class="se-mt-confirm-ok">موافق</button><button type="button" id="uaFixConfirmCancel" class="se-mt-confirm-cancel">إلغاء</button></div></div></div>';
      d.body.appendChild(b);d.body.classList.add('ua-fix-open');const done=v=>{b.remove();d.body.classList.remove('ua-fix-open');resolve(v)};d.getElementById('uaFixConfirmOk').onclick=()=>done(true);d.getElementById('uaFixConfirmCancel').onclick=()=>done(false);b.onclick=e=>{if(e.target===b)done(false)};
    });
  }

  function iconizeModalButtons(root){
    try{
      if(!root) return;
      const map=[
        [/حفظ|save/i,'fa-save','حفظ'],
        [/تعديل|edit/i,'fa-pencil','تعديل'],
        [/حذف|delete|remove/i,'fa-trash','حذف'],
        [/إلغاء|الغاء|cancel/i,'fa-times','إلغاء'],
        [/رجوع|عودة|back/i,'fa-arrow-right','رجوع'],
        [/إضافة|اضافة|add/i,'fa-plus','إضافة'],
        [/طباعة|print/i,'fa-print','طباعة'],
        [/بحث|search/i,'fa-search','بحث'],
        [/استيراد|import|excel/i,'fa-upload','استيراد'],
        [/مزامنة|sync/i,'fa-refresh','مزامنة'],
        [/تأكيد|موافق|ok|confirm/i,'fa-check','تأكيد']
      ];
      root.querySelectorAll('a.btn,button.btn,.ua-compact-actions a,.history-shell a.btn,.card-body .btn,.content-header .btn').forEach(el=>{
        if(el.classList&&el.classList.contains('ua-icon-action')) return;
        const txt=(el.textContent||el.value||el.getAttribute('title')||el.getAttribute('aria-label')||'').replace(/\s+/g,' ').trim();
        const href=(el.getAttribute('href')||'').toLowerCase();
        if(!txt && !href) return;
        let hit=map.find(x=>x[0].test(txt));
        if(!hit && (href.indexOf('subscribers_cancel.php')>=0 || /cancel/i.test(href) || txt.indexOf('إلغاء اشتراك')>=0 || txt.indexOf('الغاء اشتراك')>=0)) hit=[/./,'fa-user-times',txt||'إلغاء اشتراك'];
        if(!hit && (href.indexOf('subscribers_add.php')>=0 || txt.indexOf('إضافة مشترك')>=0 || txt.indexOf('اضافة مشترك')>=0)) hit=[/./,'fa-user-plus',txt||'إضافة مشترك'];
        if(!hit && (href.indexOf('subscribers_history.php')>=0 || txt.indexOf('السجل')>=0 || txt.indexOf('سجل')>=0)) hit=[/./,'fa-history',txt||'السجل'];
        if(!hit && (href.indexOf('subscribers_distribution.php')>=0 || txt.indexOf('توزيع')>=0 || txt.indexOf('تسديد')>=0)) hit=[/./,'fa-check-circle-o',txt||'توزيع وتسديد'];
        if(!hit && (href.indexOf('subscribers_edit.php')>=0 || txt.indexOf('تعديل')>=0)) hit=[/./,'fa-pencil',txt||'تعديل'];
        if(!hit && (href.indexOf('subscribers_restore.php')>=0 || txt.indexOf('استعادة')>=0)) hit=[/./,'fa-undo',txt||'استعادة'];
        if(!hit && (href.indexOf('subscribers_archive.php')>=0 || txt.indexOf('أرشيف')>=0 || txt.indexOf('ارشيف')>=0)) hit=[/./,'fa-archive',txt||'أرشيف'];
        if(!hit) return;
        el.setAttribute('title',txt);
        el.setAttribute('aria-label',txt);
        el.classList.add('ua-modal-icon-btn');
        if(el.tagName==='INPUT'){
          el.value=hit[2];
          el.style.setProperty('font-size','0','important');
          el.style.setProperty('background-image','none','important');
        }else{
          el.innerHTML='<i class="fa '+hit[1]+'" aria-hidden="true"></i><span class="ua-modal-btn-text">'+txt+'</span>';
        }
      });
    }catch(_e){}
  }
  function cleanModalDom(root){
    try{
      if(!root) return;
      const badText=['رجوع','رجوع للقائمة','رجوع للمشتركين','إغلاق','اغلاق','عودة','Back','Close'];
      root.querySelectorAll('a,button,input[type="button"],input[type="submit"]').forEach(el=>{
        const txt=(el.textContent||el.value||el.getAttribute('title')||el.getAttribute('aria-label')||'').replace(/\s+/g,' ').trim();
        const href=(el.getAttribute('href')||'').toLowerCase();
        const cls=String(el.className||'');
        const isOldClose = cls.indexOf('btn-close')>=0 || el.getAttribute('data-bs-dismiss')==='modal' || el.getAttribute('data-dismiss')==='modal';
        const isBackToList = href.indexOf('subscribers.php')>=0 || href.indexOf('javascript:history')>=0 || href.indexOf('history.back')>=0;
        const isPlainOldChrome = badText.some(t=>txt.indexOf(t)>=0) && (isBackToList || href==='#' || !href || isOldClose);
        if(isPlainOldChrome || isOldClose){
          el.classList.add('ua-modal-hide','uaFixPageChrome');
          el.style.setProperty('display','none','important');
        }
      });
      root.querySelectorAll('.content-header .btn[href*="subscribers.php"], .page-actions .btn[href*="subscribers.php"], .btn-close, [data-bs-dismiss="modal"], [data-dismiss="modal"], a[href^="javascript:history"], button[onclick*="history.back"], a[onclick*="history.back"]').forEach(el=>{el.classList.add('ua-modal-hide','uaFixPageChrome');el.style.setProperty('display','none','important')});
      root.querySelectorAll('.d-flex.gap-2').forEach(el=>{
        const txt=(el.textContent||el.value||el.getAttribute('title')||el.getAttribute('aria-label')||'').replace(/\s+/g,' ').trim();
        if(txt && (txt.indexOf('رجوع')>=0 || txt.indexOf('إغلاق')>=0 || txt.indexOf('اغلاق')>=0)){
          const visible=[...el.children].filter(ch=>!(ch.classList&&ch.classList.contains('ua-modal-hide')));
          if(!visible.length) el.style.setProperty('display','none','important');
        }
      });
      iconizeModalButtons(root);
    }catch(_e){}
  }
  function iframeHtml(src){
    let safe=String(src).replace(/"/g,'&quot;');
    return '<iframe id="uaFixInnerFrame" class="ua-fix-inner-frame" src="'+safe+'"></iframe>';
  }
  function bindIframeCleaner(){
    const fr=d.getElementById('uaFixInnerFrame');
    if(!fr) return;
    fr.addEventListener('load',function(){
      try{
        const doc=fr.contentDocument||fr.contentWindow.document;
        if(doc && doc.documentElement) doc.documentElement.classList.add('ua-subscriber-modal-doc');
        if(doc && doc.body) doc.body.classList.add('ua-subscriber-modal-embed');
        // المرحلة الخامسة: لا نحقن CSS كسلاسل داخل JavaScript.
        // صفحات iframe تمر عبر header.php وتحمل ua_program_unified.css/ua_extracted_pages.css،
        // وهذه الدالة تبقي فقط وسم السياق وتنظيف الكروم القديم.
        cleanModalDom(doc);
        bindSubscriberIframeNavigation(fr, doc);
        setTimeout(function(){fitEditModalHeight(doc)},80);
      }catch(_e){}
    });
  }
  function bindSubscriberIframeNavigation(fr, doc){
    try{
      if(!fr || !doc || doc.__uaSubModalNavigationBound) return;
      doc.__uaSubModalNavigationBound = true;
      doc.addEventListener('click', function(ev){
        const a = ev.target && ev.target.closest ? ev.target.closest('a[href]') : null;
        if(!a) return;
        const href = a.getAttribute('href') || '';
        if(!href || href === '#' || href.indexOf('javascript:') === 0) return;
        let go = '';
        try { go = new URL(href, fr.contentWindow.location.href).href; } catch(_e) { go = href; }
        if(!modalPageAllowed(go)) return;
        const title = a.getAttribute('data-title') || a.getAttribute('title') || (a.textContent || '').trim() || modalTitleFromUrl(go, 'نافذة');
        ev.preventDefault();
        ev.stopPropagation();
        if(ev.stopImmediatePropagation) ev.stopImmediatePropagation();
        openSubscriberModalPage(go, title);
      }, true);
    }catch(_e){}
  }
  function addModalShortcut(title,go){ return; }
  function isEditModalGo(go){try{let f=(new URL(go||'',location.href).pathname.split('/').pop()||'').toLowerCase();return f==='subscribers_edit.php'}catch(_e){return String(go||'').indexOf('subscribers_edit.php')>=0}}
  function isViewModalGo(go){try{let f=(new URL(go||'',location.href).pathname.split('/').pop()||'').toLowerCase();return f==='subscribers_view.php'}catch(_e){return String(go||'').indexOf('subscribers_view.php')>=0}}
  function uaEditModalFixedHeight(){
    try{ return Math.max(680, Math.min(920, window.innerHeight - 28)); }catch(_e){ return 860; }
  }
  function prepareEditModalBox(box){
    if(!box) return;
    const h = uaEditModalFixedHeight();
    box.classList.add('uaFixContentEdit');
    box.classList.remove('uaFixContentAutoSizing');
    box.style.setProperty('height', h + 'px', 'important');
    box.style.setProperty('min-height', '0', 'important');
    box.style.setProperty('max-height', 'calc(100vh - 28px)', 'important');
    box.style.setProperty('transition', 'none', 'important');
  }
  function fitEditModalHeight(doc){
    try{
      const rootDoc = doc || document;
      const box=rootDoc.getElementById('uaFixContentBox');
      if(!box || !box.classList.contains('uaFixContentEdit')) return;
      prepareEditModalBox(box);
    }catch(_e){}
  }
  function showContent(title,content,isHtml,go){
    let ss=window.uaRememberSubscriberScroll?window.uaRememberSubscriberScroll():null;
    init();
    closeAuth();
    const shell=d.getElementById('uaFixContent');
    const box=d.getElementById('uaFixContentBox');
    const isEdit=isEditModalGo(go);
    const isView=isViewModalGo(go);
    if(box){
      box.classList.remove('uaFixMaximized','uaFixContentView');
      if(isEdit){
        prepareEditModalBox(box);
      }else{
        box.classList.remove('uaFixContentEdit');
        if(isView) box.classList.add('uaFixContentView');
        box.style.removeProperty('height');
        box.style.removeProperty('min-height');
        box.style.removeProperty('max-height');
        box.style.removeProperty('transition');
      }
    }
    d.getElementById('uaFixContentTitle').textContent=title||'عرض';
    let b=d.getElementById('uaFixContentBody');
    b.innerHTML=isHtml?content:iframeHtml(content);
    if(isEdit && box){
      prepareEditModalBox(box);
      try{ void box.offsetHeight; }catch(_e){}
    }
    if(isHtml){cleanModalDom(b);setTimeout(function(){fitEditModalHeight(null)},80);}
    if(shell) shell.style.display='flex';
    d.body.classList.add('ua-fix-open');
    if(window.uaRestoreSubscriberScroll)window.uaRestoreSubscriberScroll(ss||window.uaSubscriberLastScroll);
    try{normalizeSubscriberModalChrome(document)}catch(_e){}
    if(!isHtml)bindIframeCleaner();
  }
  d.addEventListener('click', function(e){
    const wrap=d.getElementById('uaFixContentBody');
    if(!wrap || !wrap.contains(e.target)) return;
    const a=e.target.closest && e.target.closest('a[href]');
    if(!a) return;
    const href=a.getAttribute('href')||'';
    if(!href || href==='#' || href.startsWith('javascript:')) return;
    let go='';
    try{ go=new URL(href, location.href).href; }catch(_e){ go=href; }
    const title=a.getAttribute('data-title')||a.getAttribute('title')||a.textContent.trim()||modalTitleFromUrl(go,'نافذة');
    if(openSubscriberModalPage(go,title)){
      e.preventDefault();
      e.stopPropagation();
      e.stopImmediatePropagation();
    }
  }, true);

  async function verify(go,pwd){let fd=new FormData();fd.append('password',pwd);fd.append('go',go);let r=await fetch('api/super_admin_session.php',{method:'POST',body:fd,credentials:'same-origin',headers:{'X-Requested-With':'XMLHttpRequest'}}),j=await r.json().catch(()=>({ok:false,message:'استجابة غير مفهومة'}));if(!r.ok||!j.ok){if(j.logout&&j.redirect){location.href=j.redirect;return null}throw new Error(j.message||'فشل التحقق')}return j}
  async function single(go){
    const fd=new FormData();
    fd.append('go',go);
    const r=await fetch('api/subscribers_single_action.php',{
      method:'POST',body:fd,credentials:'same-origin',headers:{'X-Requested-With':'XMLHttpRequest'}
    });
    const j=await r.json().catch(()=>({ok:false,message:'استجابة غير مفهومة'}));
    if(!r.ok||!j.ok) throw new Error(j.message||'فشل التنفيذ');
    toast(j.message||'تم التنفيذ');
    try{
      const u=new URL(go,location.href);
      const id=parseInt(u.searchParams.get('id')||'0',10);
      const action=(u.searchParams.get('a')||'').toLowerCase();
      const file=(u.pathname.split('/').pop()||'').toLowerCase();
      if(id>0&&action==='cut'&&typeof updateSubscriberLiveState==='function'){
        updateSubscriberLiveState(id,{is_online:0,is_disabled:1,last_cut_display:'الآن'});
      }
      if(id>0&&action==='restore'&&typeof updateSubscriberLiveState==='function'){
        updateSubscriberLiveState(id,{is_online:0,is_disabled:0});
      }
      if(id>0&&file==='subscribers_cancel.php'){
        const row=d.querySelector('tr[data-subscriber-row="'+id+'"]');
        if(row) row.remove();
      }
    }catch(_e){}
    try{
      if(typeof refreshSubscribersInBackground==='function') await refreshSubscribersInBackground(true);
    }catch(_e){}
    setTimeout(()=>{
      try{if(typeof refreshSubscribersInBackground==='function')refreshSubscribersInBackground(true)}catch(_e){}
    },1800);
    return j;
  }
  async function uaFixWatchJob(jobId){
    const panel=d.getElementById('uaJobPanel'), title=d.getElementById('uaJobTitle'), text=d.getElementById('uaJobText'), fill=d.getElementById('uaJobFill');
    if(panel) panel.style.display='block';
    if(title) title.textContent='مهمة التنفيذ #' + jobId;
    if(text) text.textContent='تم إنشاء المهمة، جاري تشغيل العامل الآمن...';
    if(fill) fill.style.width='1%';
    let ticks=0, finished=false;
    async function tick(){
      if(finished) return;
      ticks++;
      try{ await fetch('api/subscribers_queue_run.php',{method:'POST',credentials:'same-origin',headers:{'X-Requested-With':'XMLHttpRequest'}}); }catch(_e){}
      try{
        const r=await fetch('api/subscribers_queue_status.php?job_id='+encodeURIComponent(jobId),{credentials:'same-origin',headers:{'X-Requested-With':'XMLHttpRequest'}});
        const j=await r.json().catch(()=>null);
        if(j&&j.ok&&j.job){
          const job=j.job, total=Math.max(1,parseInt(job.total||0,10)), processed=parseInt(job.processed||0,10);
          const pct=Math.max(0,Math.min(100,Math.round((processed/total)*100)));
          if(fill) fill.style.width=pct+'%';
          if(text) text.textContent=(job.message||'قيد التنفيذ')+' — '+processed+' / '+total+' | نجاح: '+(job.succeeded||0)+' | فشل: '+(job.failed||0);
          const st=String(job.status||'');
          if(['done','partial','failed','cancelled'].includes(st)){
            finished=true;
            toast(job.message||'انتهت مهمة التنفيذ');
            try{ if(typeof refreshSubscribersInBackground==='function') await refreshSubscribersInBackground(); }catch(_e){}
            return;
          }
        }
      }catch(_e){}
      if(ticks<120) setTimeout(tick,2500); else toast('تم إنشاء المهمة، لكن لم تصل نتيجة نهائية. راجع سجل العمليات.');
    }
    tick();
  }
  async function bulk(action,ids){
    let fd=new FormData();
    fd.append('action',action);
    ids.forEach(id=>fd.append('ids[]',id));
    let r=await fetch('api/subscribers_queue_enqueue.php',{method:'POST',body:fd,credentials:'same-origin',headers:{'X-Requested-With':'XMLHttpRequest'}}),
        j=await r.json().catch(()=>({ok:false,message:'استجابة غير مفهومة'}));
    if(!r.ok||!j.ok) throw new Error(j.message||'فشل إنشاء المهمة');
    toast(j.message||'تم إنشاء المهمة');
    if(j.job_id) uaFixWatchJob(j.job_id);
    return j;
  }
  d.addEventListener('click',async e=>{
    const cancel=e.target.closest&&e.target.closest('.js-cancel-btn.js-secure-modal-link');
    if(!cancel)return;
    e.preventDefault();
    e.stopPropagation();
    e.stopImmediatePropagation();
    const go=cancel.getAttribute('data-go')||'';
    if(!go){toast('الرابط المطلوب غير صالح');return}
    const ok=await uiConfirm(
      'تأكيد إلغاء الاشتراك',
      'سيتم حذف حساب PPPoE من MikroTik وأرشفة المشترك. للإيقاف المؤقت استخدم زر الفصل.'
    );
    if(!ok)return;
    openAuth(cancel.getAttribute('data-title')||cancel.title||'إلغاء اشتراك',go,'open',{});
  },true);
  d.addEventListener('submit',async e=>{if(e.target&&e.target.id==='uaFixForm'){e.preventDefault();e.stopPropagation();e.stopImmediatePropagation();if(!pending)return;let cur=pending, pwd=d.getElementById('uaFixPwd').value||'',er=d.getElementById('uaFixErr'),btn=d.getElementById('uaFixSubmit'),old=btn.textContent;if(!pwd){er.textContent='كلمة المرور مطلوبة.';return}btn.disabled=true;btn.textContent='جاري التحقق...';er.textContent='';try{await verify(cur.go,pwd);if(cur.mode==='bulk'){btn.textContent='جاري إنشاء المهمة...';await bulk(cur.extra.action,cur.extra.ids);d.querySelectorAll('.row-selector').forEach(c=>c.checked=false);let m=d.getElementById('masterCheckbox');if(m)m.checked=false;let sc=d.getElementById('selectedCount');if(sc)sc.textContent='0 محدد';forceClear();pending=null}else if(direct(cur.go)){btn.textContent='جاري التنفيذ...';await single(cur.go);forceClear();pending=null}else{let vu=viewUrl(cur.go);if(vu){btn.textContent='جاري تحميل البيانات...';let vr=await fetch(vu,{credentials:'same-origin',headers:{'X-Requested-With':'XMLHttpRequest'}}),html=await vr.text();if(!vr.ok)throw new Error(html||'فشل تحميل البيانات');showContent(cur.title,html,true,cur.go)}else if(modalPageAllowed(cur.go)){btn.textContent='جاري تحميل الصفحة...';showContent(cur.title,addEmbed(cur.go),false,cur.go)}else{btn.textContent='جاري فتح الصفحة...';let next=cur.go;forceClear();pending=null;setTimeout(()=>{window.location.assign(next)},80);return}}}catch(ex){er.textContent=ex&&ex.message?ex.message:'فشل التنفيذ'}finally{btn.disabled=false;btn.textContent=old||'متابعة'}}},true);
  d.addEventListener('click',async e=>{let ms=e.target.closest&&e.target.closest('[data-ua-modal-shortcut]');if(ms){e.preventDefault();e.stopPropagation();e.stopImmediatePropagation();let body=d.getElementById('uaFixContentBody'),content=d.getElementById('uaFixContent');if(body&&body.innerHTML.trim()&&content){content.style.display='flex';d.body.classList.add('ua-fix-open');d.querySelectorAll('.ua-page-tab--modal').forEach(x=>x.classList.remove('is-current'));ms.classList.add('is-current')}else{ms.remove();toast('تم إغلاق هذه النافذة.')}return}let pm=e.target.closest&&e.target.closest('.js-page-modal-link');if(pm){e.preventDefault();e.stopPropagation();e.stopImmediatePropagation();let go=pm.getAttribute('href')||'#',title=pm.getAttribute('data-title')||pm.textContent.trim()||'نافذة';showContent(title,addEmbed(go),false,go);return}let a=e.target.closest&&e.target.closest('.js-secure-modal-link');if(a){e.preventDefault();e.stopPropagation();e.stopImmediatePropagation();let go=a.getAttribute('data-go')||'';if(!go){toast('الرابط المطلوب غير صالح');return}openAuth(a.getAttribute('data-title')||a.title||'عملية محمية',go,'open',{});return}let bm=e.target.closest&&e.target.closest('#bulkModeBtn');if(bm){e.preventDefault();e.stopPropagation();e.stopImmediatePropagation();let ac=!d.body.classList.contains('ua-bulk-active');d.body.classList.toggle('ua-bulk-active',ac);let tb=d.getElementById('bulkToolbar');if(tb)tb.style.display='none';let bl=d.getElementById('bulkModeLabel');if(bl)bl.textContent=ac?'إغلاق التحديد':'تفعيل التحديد';if(!ac){d.querySelectorAll('.row-selector').forEach(c=>c.checked=false);let m=d.getElementById('masterCheckbox');if(m)m.checked=false;let sc=d.getElementById('selectedCount');if(sc)sc.textContent='0 محدد'}let mb=d.querySelector('details.ua-bulk-menu');d.querySelectorAll('details.ua-head-menu[open]').forEach(x=>{if(x!==mb)x.removeAttribute('open')});return}let ba=e.target.closest&&e.target.closest('.ua-bulk-action');if(ba){e.preventDefault();e.stopPropagation();e.stopImmediatePropagation();let rows=[...d.querySelectorAll('.row-selector:checked')],action=ba.getAttribute('data-bulk-action')||'';if(!rows.length){toast('حدد مشترك واحد على الأقل.');return}if(action==='restore'&&rows.some(x=>{let r=x.closest&&x.closest('tr');return !r||r.getAttribute('data-disabled')!=='1'})){toast('استعادة لا تتفعل إذا كان ضمن التحديد حسابات ليست بوضع الاستعادة.');return}let ids=rows.map(x=>x.value).filter(Boolean),label=({cut:'فصل',restore:'استعادة',cancel:'إلغاء اشتراك',open_speed:'سرعة مفتوحة'})[action]||'الإجراء';d.querySelectorAll('details.ua-head-menu[open]').forEach(x=>x.removeAttribute('open'));let ok=await uiConfirm('تأكيد '+label,'سيتم تنفيذ '+label+' لعدد '+ids.length+' مشترك.');if(!ok)return;openAuth('تنفيذ جماعي محمي','api/subscribers_queue_enqueue.php','bulk',{action,ids});return}},true);
  d.addEventListener('change',e=>{if(e.target&&(e.target.matches('.row-selector')||e.target.id==='masterCheckbox')){if(e.target.id==='masterCheckbox')d.querySelectorAll('.row-selector').forEach(c=>c.checked=e.target.checked);let sc=d.getElementById('selectedCount');if(sc)sc.textContent=d.querySelectorAll('.row-selector:checked').length+' محدد'}},true);
})();
</script>
<script>
(function(){
  'use strict';
  const d = document;
  const replacements = new Map([
    ['عملية محمية','عملية محمية'],
    ['إغلاق','إغلاق'],
    ['إغلاق','إغلاق'],
    ['تكبير','تكبير'],
    ['استعادة','استعادة'],
    ['تفعيل التحديد','تفعيل التحديد'],
    ['إغلاق التحديد','إغلاق التحديد'],
    ['محدد','محدد'],
    ['مهمة التنفيذ','مهمة التنفيذ'],
    ['بانتظار بدء التنفيذ...','بانتظار بدء التنفيذ...'],
    ['تأكيد العملية','تأكيد العملية'],
    ['تنفيذ','تنفيذ'],
    ['إلغاء','إلغاء'],
    ['كلمة مرور السوبر أدمن','كلمة مرور السوبر أدمن'],
    ['أدخل كلمة المرور','أدخل كلمة المرور'],
    ['متابعة','متابعة'],
    ['نافذة آمنة','نافذة آمنة'],
    ['عرض','عرض'],
    ['نافذة','نافذة'],
    ['صفحة','صفحة'],
    ['حسناظ‹','حسنا'],
    ['الرابط المطلوب غير صالح','الرابط المطلوب غير صالح'],
    ['حدد مشترك واحد على الأقل.','حدد مشترك واحد على الأقل.'],
    ['اختر الإجراء المطلوب.','اختر الإجراء المطلوب.'],
    ['تعذر قراءة المشتركين المحددين.','تعذر قراءة المشتركين المحددين.'],
    ['لا توجد حسابات صالحة للتنفيذ.','لا توجد حسابات صالحة للتنفيذ.'],
    ['استجابة غير مفهومة من الخادم','استجابة غير مفهومة من الخادم'],
    ['استجابة غير مفهومة','استجابة غير مفهومة'],
    ['فشل إنشاء مهمة التنفيذ','فشل إنشاء مهمة التنفيذ'],
    ['تم إنشاء مهمة التنفيذ','تم إنشاء مهمة التنفيذ'],
    ['قيد التنفيذ','قيد التنفيذ'],
    ['انتهت مهمة التنفيذ','انتهت مهمة التنفيذ'],
    ['نجاح','نجاح'],
    ['فشل','فشل'],
    ['تنفيذ جماعي محمي','تنفيذ جماعي محمي'],
    ['فصل','فصل'],
    ['إلغاء اشتراك','إلغاء اشتراك'],
    ['سرعة مفتوحة','سرعة مفتوحة'],
    ['الإجراء','الإجراء'],
    ['جاري التحقق...','جاري التحقق...'],
    ['جاري إنشاء المهمة...','جاري إنشاء المهمة...'],
    ['جاري التنفيذ...','جاري التنفيذ...'],
    ['جاري تحميل البيانات...','جاري تحميل البيانات...'],
    ['جاري تحميل الصفحة...','جاري تحميل الصفحة...'],
    ['جاري فتح الصفحة...','جاري فتح الصفحة...'],
    ['فشل التحقق','فشل التحقق'],
    ['فشل التنفيذ','فشل التنفيذ'],
    ['فشل تحميل البيانات','فشل تحميل البيانات'],
    ['كلمة المرور مطلوبة.','كلمة المرور مطلوبة.'],
    ['تم التنفيذ','تم التنفيذ'],
    ['الآن','الآن'],
    ['تم إغلاق هذه النافذة.','تم إغلاق هذه النافذة.'],
    ['سيتم تنفيذ','سيتم تنفيذ'],
    ['لعدد','لعدد'],
    ['مشترك','مشترك'],
    ['—',' - '],
    ['□','?'],
    ['أ—','×']
  ]);
  function repair(value){
    let out = String(value ?? '');
    replacements.forEach((to, from) => {
      if (out.includes(from)) out = out.split(from).join(to);
    });
    return out;
  }
  function repairElement(el){
    if (!el || el.nodeType !== 1) return;
    if (el.childNodes.length === 1 && el.firstChild.nodeType === 3) {
      const next = repair(el.textContent);
      if (next !== el.textContent) el.textContent = next;
    }
    ['title','aria-label','placeholder','value'].forEach(attr => {
      if (!el.hasAttribute || !el.hasAttribute(attr)) return;
      const old = el.getAttribute(attr);
      const next = repair(old);
      if (next !== old) el.setAttribute(attr, next);
    });
    if (el.id === 'uaFixContentClose') {
      el.textContent = '×';
      el.setAttribute('title', 'إغلاق');
      el.setAttribute('aria-label', 'إغلاق');
    }
    if (el.id === 'uaFixContentMax') {
      el.textContent = '?';
      el.setAttribute('title', 'تكبير');
      el.setAttribute('aria-label', 'تكبير');
    }
  }
  function repairTree(root){
    try {
      const base = root || d;
      if (base.nodeType === 1) repairElement(base);
      base.querySelectorAll?.('*').forEach(repairElement);
      base.querySelectorAll?.('iframe').forEach(frame => {
        try {
          const doc = frame.contentDocument || frame.contentWindow?.document;
          if (doc) repairTree(doc);
        } catch(_e) {}
      });
    } catch(_e) {}
  }
  const repairRootSelector = '#uaFixAuth,#uaFixContent,#uaBulkModalBackdrop,#uaSecureModalBackdrop,#uaJobPanel,#uaToastStack';
  const repairRoots = [...d.querySelectorAll(repairRootSelector)];
  function closeFixContent(){
    const ss = window.uaRememberSubscriberScroll ? window.uaRememberSubscriberScroll() : null;
    const content = d.getElementById('uaFixContent');
    const body = d.getElementById('uaFixContentBody');
    const box = d.getElementById('uaFixContentBox');
    if (content) content.style.display = 'none';
    if (body) body.innerHTML = '';
    if (box) box.classList.remove('uaFixMaximized');
    d.body.classList.remove('ua-fix-open');
    if (window.uaRestoreSubscriberScroll) window.uaRestoreSubscriberScroll(ss || window.uaSubscriberLastScroll);
  }
  function closeFixAuth(){
    const ss = window.uaRememberSubscriberScroll ? window.uaRememberSubscriberScroll() : null;
    const auth = d.getElementById('uaFixAuth');
    const pwd = d.getElementById('uaFixPwd');
    const err = d.getElementById('uaFixErr');
    if (auth) auth.style.display = 'none';
    if (pwd) pwd.value = '';
    if (err) err.textContent = '';
    d.body.classList.remove('ua-fix-open');
    if (window.uaRestoreSubscriberScroll) window.uaRestoreSubscriberScroll(ss || window.uaSubscriberLastScroll);
  }
  d.addEventListener('click', function(e){
    const closeContent = e.target.closest?.('#uaFixContentClose,.uaFixX');
    if (closeContent) {
      e.preventDefault();
      e.stopPropagation();
      closeFixContent();
      return;
    }
    const closeAuth = e.target.closest?.('#uaFixClose');
    if (closeAuth) {
      e.preventDefault();
      e.stopPropagation();
      closeFixAuth();
      return;
    }
    const max = e.target.closest?.('#uaFixContentMax,.uaFixMax');
    if (max) {
      e.preventDefault();
      e.stopPropagation();
      const box = d.getElementById('uaFixContentBox');
      if (box) box.classList.toggle('uaFixMaximized');
    }
  }, true);
  repairRoots.forEach(root => {
    let scheduled = false;
    const scheduleRepair = () => {
      if (scheduled) return;
      scheduled = true;
      requestAnimationFrame(() => { scheduled = false; repairTree(root); });
    };
    new MutationObserver(scheduleRepair).observe(root, {childList:true, subtree:true, characterData:true, attributes:true, attributeFilter:['title','aria-label','placeholder','value']});
    repairTree(root);
  });
  d.addEventListener('load', e => {
    const frame = e.target;
    if (frame && frame.matches?.(repairRootSelector + ' iframe')) {
      try { repairTree(frame.contentDocument || frame.contentWindow?.document); } catch(_e) {}
    }
  }, true);
})();
</script>
<script>
(function(){
  'use strict';
  function normalizeSubscriberModalChrome(root){
    var scope = root || document;
    var controls = scope.querySelector && scope.querySelector('.uaFixWinControls');
    if (!controls) return;
    var closeBtn = controls.querySelector('#uaFixContentClose,.uaFixX');
    var maxBtn = controls.querySelector('#uaFixContentMax,.uaFixMax');
    if (closeBtn && maxBtn && closeBtn.nextSibling !== maxBtn) {
      controls.insertBefore(closeBtn, maxBtn);
    }
    if (maxBtn) {
      maxBtn.textContent = '';
      maxBtn.setAttribute('title', '\u062a\u0643\u0628\u064a\u0631');
      maxBtn.setAttribute('aria-label', '\u062a\u0643\u0628\u064a\u0631');
    }
    if (closeBtn) {
      closeBtn.textContent = '';
      closeBtn.setAttribute('title', '\u0625\u063a\u0644\u0627\u0642');
      closeBtn.setAttribute('aria-label', '\u0625\u063a\u0644\u0627\u0642');
    }
  }
  normalizeSubscriberModalChrome(document);
  new MutationObserver(function(){ normalizeSubscriberModalChrome(document); }).observe(document.documentElement, {childList:true, subtree:true});
})();
</script>

<script>
(function(){
  'use strict';
  const d = document;
  const brokenText = new Map([
    ['عملية محمية','عملية محمية'],
    ['تأكيد العملية','تأكيد العملية'],
    ['تنفيذ جماعي محمي','تنفيذ جماعي محمي'],
    ['مهمة التنفيذ','مهمة التنفيذ'],
    ['تم إنشاء مهمة التنفيذ','تم إنشاء مهمة التنفيذ'],
    ['تم إنشاء المهمة','تم إنشاء المهمة'],
    ['جاري تشغيل العامل الآمن','جاري تشغيل العامل الآمن'],
    ['قيد التنفيذ','قيد التنفيذ'],
    ['انتهت مهمة التنفيذ','انتهت مهمة التنفيذ'],
    ['نجاح','نجاح'],
    ['فشل','فشل'],
    ['فشل إنشاء المهمة','فشل إنشاء المهمة'],
    ['فشل إنشاء مهمة التنفيذ','فشل إنشاء مهمة التنفيذ'],
    ['لا توجد حسابات صالحة للتنفيذ.','لا توجد حسابات صالحة للتنفيذ.'],
    ['استجابة غير مفهومة من الخادم','استجابة غير مفهومة من الخادم'],
    ['استجابة غير مفهومة','استجابة غير مفهومة'],
    ['حدد مشترك واحد على الأقل.','حدد مشترك واحد على الأقل.'],
    ['إلغاء اشتراك','إلغاء اشتراك'],
    ['سرعة مفتوحة','سرعة مفتوحة'],
    ['فصل','فصل'],
    ['استعادة','استعادة'],
    ['محدد','محدد'],
    ['الآن','الآن'],
    ['—',' - '],
    ['طŒ','،']
  ]);
  function repairMessage(value){
    let out = String(value == null ? '' : value).replace(/\s+/g, ' ').trim();
    brokenText.forEach((to, from) => {
      if (out.includes(from)) out = out.split(from).join(to);
    });
    return out;
  }
  window.uaRepairSubscriberMessage = repairMessage;

  function toastStack(){
    let stack = d.getElementById('uaToastStack') || d.getElementById('uaGlobalToastStack');
    if (!stack) {
      stack = d.createElement('div');
      stack.id = 'uaToastStack';
      stack.className = 'ua-feedback-stack ua-toast-stack';
      d.body.appendChild(stack);
    }
    return stack;
  }
  function toneFromText(text){
    if (/فشل|خطأ|تعذر|غير صالحة/.test(text)) return 'error';
    if (/انتهت|تم|نجاح/.test(text)) return 'success';
    return 'info';
  }
  function showBulkToast(message, options){
    const msg = repairMessage(message);
    if (!msg) return;
    const opts = options || {};
    const stack = toastStack();
    let item = d.getElementById('uaBulkJobToast');
    if (!item) {
      item = d.createElement('div');
      item.id = 'uaBulkJobToast';
      stack.appendChild(item);
    }
    const tone = opts.type || toneFromText(msg);
    item.className = 'ua-toast ua-toast-' + tone + ' show';
    item.textContent = msg;
    clearTimeout(item._uaCloseTimer);
    const final = opts.final || /انتهت|فشل|خطأ|تعذر|نجاح/.test(msg);
    item._uaCloseTimer = setTimeout(function(){
      item.classList.remove('show');
      setTimeout(function(){ if (item.parentNode) item.remove(); }, 260);
    }, final ? 5200 : 9000);
  }
  window.uaSubscribersBulkToast = showBulkToast;

  let lastJobText = '';
  function readJobPanelText(panel){
    const title = repairMessage((d.getElementById('uaJobTitle') || {}).textContent || '');
    const text = repairMessage((d.getElementById('uaJobText') || {}).textContent || '');
    if (text && title && !text.includes(title)) return title + ': ' + text;
    return text || title || repairMessage(panel && panel.textContent);
  }
  function hideJobPanelAsToast(){
    const panel = d.getElementById('uaJobPanel');
    if (!panel) return;
    const inlineDisplay = String(panel.style.display || '').toLowerCase();
    const requestedVisible = inlineDisplay !== '' && inlineDisplay !== 'none';
    const activeJob = panel.getAttribute('data-ua-job-active') === '1';
    const msg = readJobPanelText(panel);
    panel.style.display = 'none';
    panel.setAttribute('aria-hidden', 'true');
    if (!requestedVisible && !activeJob) return;
    if (requestedVisible) panel.setAttribute('data-ua-job-active', '1');
    if (msg && msg !== lastJobText) {
      lastJobText = msg;
      showBulkToast(msg);
      if (/انتهت|فشل|خطأ|تعذر|نجاح/.test(msg)) {
        panel.setAttribute('data-ua-job-active', '0');
      }
    }
  }
  function repairVisibleMessages(root){
    const scope = root && root.nodeType === 1 ? root : d;
    const items = [];
    if (scope.matches && scope.matches('.ua-toast,.ua-fix-toast-item,.alert')) items.push(scope);
    scope.querySelectorAll && scope.querySelectorAll('.ua-toast,.ua-fix-toast-item,.alert').forEach(el => items.push(el));
    items.forEach(function(el){
      const fixed = repairMessage(el.textContent);
      if (fixed && fixed !== el.textContent) el.textContent = fixed;
      if (el.classList.contains('ua-toast')) el.classList.add('show');
    });
    hideJobPanelAsToast();
  }
  const observer = new MutationObserver(function(mutations){
    mutations.forEach(function(m){
      if (m.target && m.target.id === 'uaJobPanel') hideJobPanelAsToast();
      m.addedNodes && m.addedNodes.forEach(function(n){ repairVisibleMessages(n); });
    });
    repairVisibleMessages(d);
  });
  function init(){
    repairVisibleMessages(d);
    const panel = d.getElementById('uaJobPanel');
    if (panel) {
      observer.observe(panel, {childList:true, subtree:true, characterData:true, attributes:true, attributeFilter:['style','class']});
    }
    observer.observe(d.documentElement, {childList:true, subtree:true});
  }
  if (d.readyState === 'loading') d.addEventListener('DOMContentLoaded', init, {once:true}); else init();
})();
</script>

<script src="assets/js/ua_subscribers_action_guard.js?v=<?= (int)(@filemtime(__DIR__ . '/assets/js/ua_subscribers_action_guard.js') ?: time()) ?>"></script>


<?php require_once __DIR__ . '/footer.php'; ?>
