(function () {
  'use strict';

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', init, {once: true});
  } else {
    init();
  }

  function init() {
  var body = document.body;
  if (!body || !body.classList.contains('ua-authed') || window.top !== window) return;

  var POLL_MS = 30000;
  var SNAPSHOT_TTL_MS = 12 * 60 * 60 * 1000;
  var ALERT_COOLDOWN_MS = 15 * 60 * 1000;
  var ROUTER_DOWN_CONFIRMATIONS = 3;
  var DEVICE_DOWN_CONFIRMATIONS = 2;
  var ROUTER_MIN_DOWN_MS = 45000;
  var DEVICE_MIN_DOWN_MS = 60000;
  var STORAGE_KEY = 'ua_device_connectivity_snapshot_v2';
  var connectedStates = new Set(['ONLINE', 'UP', 'PARTIAL', 'ONLINE_PARTIAL']);
  var downStates = new Set(['OFFLINE', 'DOWN']);
  var groups = [
    {key: 'routers', label: 'راوتر', url: 'api/noc_routers_card.php'},
    {key: 'sectors', label: 'مرسل', url: 'api/noc_sectors_card.php'},
    {key: 'receivers', label: 'مستقبل', url: 'api/noc_receivers_card.php'}
  ];
  var enabled = body.dataset.uaDeviceDisconnectAlert !== '0';
  var polling = false;
  var timer = 0;
  var snapshot = loadSnapshot();
  var activeEvents = new Map();

  function emptySnapshot() {
    return {updatedAt: 0, groups: {routers: {}, sectors: {}, receivers: {}}};
  }

  function loadSnapshot() {
    try {
      var parsed = JSON.parse(localStorage.getItem(STORAGE_KEY) || 'null');
      if (!parsed || typeof parsed !== 'object' || !parsed.groups || Date.now() - Number(parsed.updatedAt || 0) > SNAPSHOT_TTL_MS) return emptySnapshot();
      groups.forEach(function (group) {
        if (!parsed.groups[group.key] || typeof parsed.groups[group.key] !== 'object') parsed.groups[group.key] = {};
      });
      return parsed;
    } catch (_) {
      return emptySnapshot();
    }
  }

  function saveSnapshot() {
    snapshot.updatedAt = Date.now();
    try { localStorage.setItem(STORAGE_KEY, JSON.stringify(snapshot)); } catch (_) {}
  }

  function normalizedState(item) {
    return String(item && item.state || '').trim().toUpperCase();
  }

  function stableKey(groupKey, item) {
    var id = Number(item && item.id || 0);
    if (id > 0) return groupKey + ':id:' + id;
    var host = String(item && item.host || '').trim().toLowerCase();
    if (host) return groupKey + ':host:' + host;
    var name = String(item && (item.name || item.identity) || '').trim().toLowerCase();
    return name ? groupKey + ':name:' + name : '';
  }

  function isPhysicalReceiver(item) {
    return String(item && item.device_type || '').toLowerCase() !== 'router_port';
  }

  function observationId(group, payload, item) {
    var itemProbe = Number(item && (item.last_probe_ts || item.probe_ts) || 0);
    if (itemProbe > 0) return String(itemProbe);
    if (group.key === 'routers') {
      var payloadProbe = Number(payload && payload.last_update || 0);
      if (payloadProbe > 0) return String(payloadProbe);
    }
    return '';
  }

  function requiredDownSamples(group) {
    return group.key === 'routers' ? ROUTER_DOWN_CONFIRMATIONS : DEVICE_DOWN_CONFIRMATIONS;
  }

  function requiredDownDuration(group) {
    return group.key === 'routers' ? ROUTER_MIN_DOWN_MS : DEVICE_MIN_DOWN_MS;
  }

  function processPayload(group, payload) {
    if (!payload || payload.ok === false || !Array.isArray(payload.items)) return {failures: [], recoveries: []};
    var previous = snapshot.groups[group.key] || {};
    var next = Object.assign({}, previous);
    var failures = [];
    var recoveries = [];

    payload.items.forEach(function (item) {
      if (!item || typeof item !== 'object') return;
      if (group.key === 'receivers' && !isPhysicalReceiver(item)) return;
      var key = stableKey(group.key, item);
      var current = normalizedState(item);
      if (!key || (!connectedStates.has(current) && !downStates.has(current))) return;
      var sampleId = observationId(group, payload, item);
      // A DOWN value without a fresh probe identifier is cached/ambiguous, not
      // a new physical observation. It must never advance the alarm counter.
      if (downStates.has(current) && !sampleId) return;

      var old = previous[key] && typeof previous[key] === 'object' ? previous[key] : {};
      if (sampleId && String(old.sampleId || '') === sampleId) return;
      var stable = String(old.stableState || old.state || '').toUpperCase();
      var now = Date.now();
      var record = Object.assign({}, old, {sampleId: sampleId, seenAt: now, observedState: current});

      if (connectedStates.has(current)) {
        // A successful authenticated/device response is strong evidence. It
        // resets pending failures immediately and establishes the baseline.
        if (downStates.has(stable) && Number(old.lastAlertAt || 0) > 0) {
          recoveries.push({key: key, group: group.key, id: Number(item.id || 0), type: group.label,
            name: String(item.name || item.identity || 'جهاز غير مسمى'), host: String(item.host || ''),
            state: 'UP', offline_seconds: Math.max(0, Math.round((now - Number(old.downSince || now)) / 1000))});
        }
        record.stableState = current;
        record.state = current;
        record.downCount = 0;
        record.downSince = 0;
        next[key] = record;
        return;
      }

      if (!connectedStates.has(stable)) {
        // First sight of a device while it is already down is a baseline, not
        // an online-to-offline transition and therefore must not alert.
        record.stableState = downStates.has(stable) ? stable : current;
        record.state = record.stableState;
        record.downCount = Math.max(1, Number(old.downCount || 0));
        record.downSince = Number(old.downSince || now);
        next[key] = record;
        return;
      }

      record.downCount = Number(old.downCount || 0) + 1;
      record.downSince = Number(old.downSince || now);
      record.stableState = stable;
      record.state = stable;
      var confirmed = record.downCount >= requiredDownSamples(group)
        && now - record.downSince >= requiredDownDuration(group);
      var cooledDown = now - Number(old.lastAlertAt || 0) >= ALERT_COOLDOWN_MS;
      if (confirmed) {
        if (cooledDown) {
          failures.push({
            key: key,
            group: group.key,
            id: Number(item.id || 0),
            type: group.label,
            name: String(item.name || item.identity || 'جهاز غير مسمى'),
            host: String(item.host || ''),
            state: current
          });
        }
      }
      next[key] = record;
    });

    snapshot.groups[group.key] = next;
    return {failures: failures, recoveries: recoveries};
  }

  async function mutedMap(events) {
    var tokens = events.filter(function (event) { return event.id > 0; }).map(function (event) { return event.group + ':' + event.id; });
    if (!tokens.length) return {};
    try {
      var payload = await fetchPayload('api/device_admin_alert_status.php?items=' + encodeURIComponent(tokens.join(',')));
      return payload && payload.muted && typeof payload.muted === 'object' ? payload.muted : {};
    } catch (_) { return {}; }
  }

  async function publishAdminEvents(events) {
    if (!events.length) return;
    try {
      await fetch('api/device_admin_alert_event.php', {method: 'POST', credentials: 'same-origin', cache: 'no-store',
        headers: {'Content-Type': 'application/json', 'Accept': 'application/json'}, body: JSON.stringify({events: events})});
    } catch (_) {}
  }

  async function fetchPayload(url) {
    var controller = typeof AbortController === 'function' ? new AbortController() : null;
    var timeout = controller ? setTimeout(function () { controller.abort(); }, 12000) : 0;
    try {
      var response = await fetch(url + (url.indexOf('?') >= 0 ? '&' : '?') + '_=' + Date.now(), {
        credentials: 'same-origin',
        cache: 'no-store',
        headers: {'Accept': 'application/json'},
        signal: controller ? controller.signal : undefined
      });
      if (!response.ok) throw new Error('HTTP ' + response.status);
      var text = await response.text();
      return JSON.parse(text.replace(/^\uFEFF/, ''));
    } finally {
      if (timeout) clearTimeout(timeout);
    }
  }

  async function confirmWithSubscribers(events) {
    if (!events.length) return [];
    // RouterOS already requires three distinct failed probes. Subscriber
    // evidence is device-scoped only for sectors/receivers, not whole routers.
    var checkable = events.filter(function (event) { return event.id > 0 && event.group !== 'routers'; });
    if (!checkable.length) return events;
    var tokens = checkable.map(function (event) { return event.group + ':' + event.id; }).join(',');
    try {
      var payload = await fetchPayload('api/device_subscriber_confirmation.php?items=' + encodeURIComponent(tokens));
      if (!payload || payload.ok === false || !payload.items) return [];
      return events.filter(function (event) {
        if (!(event.id > 0) || event.group === 'routers') return true;
        var result = payload.items[event.group + ':id:' + event.id];
        var groupSnapshot = snapshot.groups[event.group] || {};
        var record = groupSnapshot[event.key];
        if (record && result) {
          if (result.allow_alert === true) {
            record.stableState = event.state;
            record.state = event.state;
            record.lastAlertAt = Date.now();
          } else if (result.reason === 'linked_subscriber_online') {
            // The subscriber page still proves traffic through this device.
            // Reset the candidate and require a completely new down sequence.
            record.downCount = 0;
            record.downSince = 0;
          }
          groupSnapshot[event.key] = record;
        }
        // Fail closed: an absent/inconclusive subscriber check delays the alert.
        return !!(result && result.allow_alert === true);
      });
    } catch (_) {
      // A failed second-source check is not proof of a physical outage.
      return [];
    }
  }

  function escapeHtml(value) {
    return String(value == null ? '' : value).replace(/[&<>"']/g, function (char) {
      return {'&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;'}[char];
    });
  }

  function modal() {
    var root = document.getElementById('uaDeviceDisconnectAlert');
    if (root) return root;
    root = document.createElement('div');
    root.id = 'uaDeviceDisconnectAlert';
    root.className = 'ua-device-disconnect-alert';
    root.innerHTML = '<section class="ua-device-disconnect-alert__card" role="alertdialog" aria-modal="true" aria-labelledby="uaDeviceDisconnectTitle" aria-describedby="uaDeviceDisconnectSummary">'
      + '<header class="ua-device-disconnect-alert__head"><span class="ua-device-disconnect-alert__icon" aria-hidden="true"><i class="fa fa-exclamation-triangle"></i></span>'
      + '<div class="ua-device-disconnect-alert__title"><strong id="uaDeviceDisconnectTitle">خطر: فُقد الاتصال بجهاز</strong><small>تنبيه فوري من مراقب الشبكة</small></div>'
      + '<button type="button" class="ua-device-disconnect-alert__close" aria-label="إغلاق">×</button></header>'
      + '<div class="ua-device-disconnect-alert__body"><p class="ua-device-disconnect-alert__summary" id="uaDeviceDisconnectSummary"></p><ul class="ua-device-disconnect-alert__list"></ul></div>'
      + '<footer class="ua-device-disconnect-alert__actions"><button type="button" class="ua-device-disconnect-alert__ack">تم الاطلاع</button></footer></section>';
    document.body.appendChild(root);
    var close = function () {
      root.classList.remove('is-open');
      activeEvents.clear();
    };
    root.querySelector('.ua-device-disconnect-alert__close').addEventListener('click', close);
    root.querySelector('.ua-device-disconnect-alert__ack').addEventListener('click', close);
    root.addEventListener('keydown', function (event) { if (event.key === 'Escape') close(); });
    return root;
  }

  function showEvents(events, preview) {
    events.forEach(function (event) { activeEvents.set(event.key, event); });
    var items = Array.from(activeEvents.values());
    if (!items.length) return;
    var root = modal();
    var count = items.length;
    root.querySelector('#uaDeviceDisconnectTitle').textContent = preview ? 'معاينة تنبيه فقد الاتصال' : (count === 1 ? 'خطر: فُقد الاتصال بجهاز' : 'خطر: فُقد الاتصال بعدة أجهزة');
    root.querySelector('#uaDeviceDisconnectSummary').textContent = preview
      ? 'هذا هو شكل التنبيه الذي سيظهر عند اكتشاف انتقال مؤكد من الاتصال إلى الانقطاع.'
      : (count === 1 ? 'تم رصد انتقال مؤكد إلى حالة الانقطاع. راجع الجهاز التالي فوراً.' : 'تم رصد انتقال مؤكد إلى حالة الانقطاع لعدد ' + count + ' من الأجهزة.');
    root.querySelector('.ua-device-disconnect-alert__list').innerHTML = items.map(function (item) {
      return '<li class="ua-device-disconnect-alert__item"><div><strong>' + escapeHtml(item.name) + '</strong><small>' + escapeHtml(item.host || 'العنوان غير مسجل') + '</small></div><span class="ua-device-disconnect-alert__type">' + escapeHtml(item.type) + '</span></li>';
    }).join('');
    root.classList.add('is-open');
    root.setAttribute('tabindex', '-1');
    root.focus({preventScroll: true});
  }

  async function poll() {
    if (!enabled || polling || document.hidden) return;
    polling = true;
    try {
      snapshot = loadSnapshot();
      var results = await Promise.allSettled(groups.map(function (group) { return fetchPayload(group.url); }));
      var failures = [], recoveries = [];
      results.forEach(function (result, index) {
        if (result.status === 'fulfilled') {
          var transitions = processPayload(groups[index], result.value);
          failures = failures.concat(transitions.failures);
          recoveries = recoveries.concat(transitions.recoveries);
        }
      });
      saveSnapshot();
      if (recoveries.length) publishAdminEvents(recoveries);
      if (failures.length) {
        var confirmedFailures = await confirmWithSubscribers(failures);
        saveSnapshot();
        if (confirmedFailures.length) {
          publishAdminEvents(confirmedFailures.map(function (event) { return Object.assign({}, event, {state: 'DOWN'}); }));
          var muted = await mutedMap(confirmedFailures);
          var visible = confirmedFailures.filter(function (event) { return !muted[event.group + ':id:' + event.id]; });
          if (visible.length) showEvents(visible, false);
        }
      }
    } finally {
      polling = false;
    }
  }

  function schedule(delay) {
    clearTimeout(timer);
    if (!enabled) return;
    timer = window.setTimeout(async function tick() {
      await poll();
      schedule(POLL_MS);
    }, delay);
  }

  function setEnabled(value) {
    enabled = value === true || value === 1 || value === '1';
    body.dataset.uaDeviceDisconnectAlert = enabled ? '1' : '0';
    if (enabled) schedule(250);
    else {
      clearTimeout(timer);
      var root = document.getElementById('uaDeviceDisconnectAlert');
      if (root) root.classList.remove('is-open');
      activeEvents.clear();
    }
  }

  function preview() {
    activeEvents.clear();
    showEvents([{key: 'preview', type: 'جهاز شبكة', name: 'مثال لجهاز فقد الاتصال', host: '192.0.2.10'}], true);
  }

  window.addEventListener('message', function (event) {
    if (event.origin !== window.location.origin || !event.data || typeof event.data !== 'object') return;
    if (event.data.type === 'ua-device-disconnect-alert-preview') preview();
    if (event.data.type === 'ua-device-disconnect-alert-config') setEnabled(event.data.enabled);
  });
  document.addEventListener('visibilitychange', function () { if (!document.hidden && enabled) schedule(200); });

  window.UADeviceDisconnectAlert = {
    preview: preview,
    pollNow: poll,
    setEnabled: setEnabled,
    isEnabled: function () { return enabled; }
  };

  schedule(2200);
  }
})();
