<?php
require_once __DIR__ . '/../auth.php';
if (function_exists('require_login')) require_login('login.php');
if (function_exists('require_any_perm')) require_any_perm(['can_view_dashboard_receivers','can_view_dashboard_summary']);
if (session_status() === PHP_SESSION_ACTIVE) { @session_write_close(); }
require_once __DIR__ . '/../db_bootstrap.php';
if (function_exists('ensure_db')) ensure_db(true);
require_once __DIR__ . '/../airmax_monitor_lib.php';
require_once __DIR__ . '/../receiver_subscriber_match_lib.php';
require_once __DIR__ . '/../routeros_api.php';
require_once __DIR__ . '/../mikrotik_connection_lib.php';
require_once __DIR__ . '/../device_subscriber_confirmation_lib.php';
require_once __DIR__ . '/noc_card_cache.php';

header('Content-Type: application/json; charset=utf-8');
@ini_set('display_errors','0');
while (ob_get_level()>0) { @ob_end_clean(); }
ob_start();


function noc_table_exists_rx(mysqli $c, string $t): bool { $t=$c->real_escape_string($t); $r=@$c->query("SHOW TABLES LIKE '{$t}'"); return $r && $r->num_rows>0; }
function noc_get_mt_fallback_rx(mysqli $c): ?array {
    if (!noc_table_exists_rx($c,'mikrotik_settings')) return null;
    $r=@$c->query('SELECT * FROM mikrotik_settings ORDER BY id ASC LIMIT 1');
    $row=$r?$r->fetch_assoc():null;
    if(!$row) return null;
    return [
        'host'=>(string)($row['host']??''),
        'username'=>(string)($row['username']??''),
        'password'=>(string)($row['password']??''),
        'api_port'=>(int)($row['api_port']??8728),
    ];
}
function noc_api_connect_rx(?array $router){
    if (!$router) return false;
    $err = '';
    return ua_mt_connect_config($router, $err, 0.8);
}
function noc_count_re_rows_rx($rows): int {
    $c=0;
    if(!is_array($rows)) return 0;
    foreach($rows as $r){ if(is_array($r) && (($r['.type']??'')==='!re')) $c++; }
    return $c;
}
function noc_get_ppp_active_total_rx(mysqli $conn): ?int {
    $mt = noc_get_mt_fallback_rx($conn);
    $api = noc_api_connect_rx($mt);
    if (!$api) return null;
    try { $rows = $api->command(['/ppp/active/print']); $count = noc_count_re_rows_rx($rows); }
    catch (Throwable $e) { $count = null; }
    if (method_exists($api,'disconnect')) { try { $api->disconnect(); } catch (Throwable $e) {} }
    return $count;
}

function noc_rx_current_matched_count(array $item): int {
    $subs = (array)($item['matched_subscribers'] ?? []);
    if (!$subs) return (int)($item['matched_count'] ?? 0);
    $seen = [];
    foreach ($subs as $sub) {
        if (!is_array($sub) || empty($sub['observed_now'])) continue;
        $sid = (int)($sub['id'] ?? $sub['subscriber_id'] ?? 0);
        $mac = function_exists('ua_rx_norm_mac') ? ua_rx_norm_mac($sub['matched_mac'] ?? $sub['mac'] ?? '') : '';
        $key = $sid > 0 ? ('sid:' . $sid) : ($mac !== '' ? ('mac:' . $mac) : '');
        if ($key === '') continue;
        $seen[$key] = true;
    }
    return count($seen);
}


try {
    $conn = $GLOBALS['conn'] ?? null;
    if (!($conn instanceof mysqli)) noc_json_response(['ok'=>false,'items'=>[]],500);

    $cacheKey = 'noc_receivers_card';
    $cachePath = noc_cache_file($cacheKey);
    $cache = noc_load_json_file($cachePath, ['ok'=>true,'items'=>[],'last_update'=>0,'next_idx'=>0,'summary'=>['total'=>0,'down'=>0,'total_stations'=>null,'count_source'=>'ppp_active','count_label'=>'جلسات PPPoE النشطة']]);

    $devices = array_values(array_filter(airmax_get_all($conn, true), static fn($d) => function_exists('airmax_device_is_station') ? airmax_device_is_station($d) : in_array(($d['device_type'] ?? ''), ['station','powerbeam'], true)));
    foreach ($devices as $d) {
        $id = (int)($d['id'] ?? 0);
        if (!isset($cache['items'][$id])) {
            $cache['items'][$id] = [
                'id'=>$id,'name'=>(string)($d['name'] ?? '—'),'host'=>(string)($d['host'] ?? ''),
                'model'=>'—','cpu'=>null,'memory'=>null,'quality'=>null,'capacity'=>null,
                'clients'=>null,'stations'=>null,'state'=>'WARN','uptime'=>'','signal'=>null,'ccq'=>null
            ];
        }
    }

    $now = time();
    if ($devices && ((int)($cache['last_update'] ?? 0) < $now - 60)) {
        $lock = noc_try_lock($cacheKey);
        if ($lock) {
            $idx = (int)($cache['next_idx'] ?? 0);
            $passes = min(1, count($devices));
            for ($pass = 0; $pass < $passes; $pass++) {
                $d = $devices[($idx + $pass) % count($devices)];
                $rid = (int)($d['id'] ?? 0);
                $cached = airmax_json_read(airmax_result_cache_path($d), null);
                $age = is_array($cached) ? ($now - (int)($cached['ts'] ?? 0)) : 999999;
                // Dashboard reads must not poll receivers automatically.
                // Receiver probing is handled by cron/receiver_match_worker.php or the manual refresh button.

                $old = $cache['items'][$rid] ?? [];
                if (is_array($cached) && isset($cached['out']['probe'])) {
                    $p = $cached['out']['probe'];
                    $metrics = is_array($p['metrics'] ?? null) ? $p['metrics'] : [];
                    $cache['items'][$rid] = [
                        'id'=>$rid,
                        'host'=>(string)($d['host'] ?? ''),
                        'name'=>(string)($d['name'] ?? '—'),
                        'model'=>(string)(($metrics['model'] ?? ($old['model'] ?? '—')) ?: '—'),
                        'cpu'=>isset($metrics['cpu']) && $metrics['cpu'] !== '' ? (float)$metrics['cpu'] : ($old['cpu'] ?? null),
                        'memory'=>isset($metrics['memory']) && $metrics['memory'] !== '' ? (float)$metrics['memory'] : ($old['memory'] ?? null),
                        'quality'=>isset($metrics['quality']) && $metrics['quality'] !== '' ? (float)$metrics['quality'] : ($old['quality'] ?? null),
                        'capacity'=>isset($metrics['capacity']) && $metrics['capacity'] !== '' ? (float)$metrics['capacity'] : ($old['capacity'] ?? null),
                        'signal'=>isset($metrics['signal']) && $metrics['signal'] !== '' ? (float)$metrics['signal'] : ($old['signal'] ?? null),
                        'ccq'=>isset($metrics['ccq']) && $metrics['ccq'] !== '' ? (float)$metrics['ccq'] : ($old['ccq'] ?? null),
                        'clients'=>isset($metrics['clients']) && $metrics['clients'] !== '' ? (int)$metrics['clients'] : (($old['clients'] ?? null) !== null ? (int)$old['clients'] : null),
                        'stations'=>isset($metrics['clients']) && $metrics['clients'] !== '' ? (int)$metrics['clients'] : (($old['stations'] ?? null) !== null ? (int)$old['stations'] : null),
                        'uptime'=>(string)(($metrics['uptime'] ?? ($old['uptime'] ?? '')) ?: ''),
                        'state'=>(string)($p['state'] ?? ($old['state'] ?? 'DOWN')),
                    ];
                }
            }

            $cache['next_idx'] = $idx + $passes;
            $cache['last_update'] = $now;

            $list = [];
            foreach ($devices as $dd) {
                $iid = (int)($dd['id'] ?? 0);
                $list[] = $cache['items'][$iid] ?? [
                    'id'=>$iid,'name'=>(string)($dd['name'] ?? '—'),'host'=>(string)($dd['host'] ?? ''),
                    'model'=>'—','cpu'=>null,'memory'=>null,'quality'=>null,'capacity'=>null,
                    'clients'=>null,'stations'=>null,'state'=>'WARN','uptime'=>'','signal'=>null,'ccq'=>null
                ];
            }
            $down = 0;
            $receiverStations = 0;
            foreach ($list as $it) {
                if (in_array(strtoupper((string)($it['state'] ?? '')), ['OFFLINE','DOWN'], true)) $down++;
                $receiverStations += (int)($it['stations'] ?? $it['clients'] ?? 0);
            }
            $cache['summary'] = [
                'total'=>count($list),
                'down'=>$down,
                // Device-reported clients are not subscriber matches. Keep this diagnostic
                // out of the dashboard subscriber count; official matching is applied below.
                'total_stations'=>null,
                'device_clients_total'=>$receiverStations,
                'count_source'=>'receiver_device_clients_diagnostic',
                'count_label'=>'قراءة تشخيصية من أجهزة المستقبلات وليست مطابقة مشتركين'
            ];
            noc_save_json_file($cachePath, $cache);
            noc_unlock($lock);
        }
    }

    $list = [];
    foreach ($devices as $d) {
        $id = (int)($d['id'] ?? 0);
        $oldItem = is_array($cache['items'][$id] ?? null) ? $cache['items'][$id] : [];
        $list[] = function_exists('airmax_dashboard_item_from_cache')
            ? airmax_dashboard_item_from_cache($d, $oldItem)
            : ($oldItem ?: [
                'id'=>$id,'name'=>(string)($d['name'] ?? '—'),'host'=>(string)($d['host'] ?? ''),
                'model'=>'—','cpu'=>null,'memory'=>null,'quality'=>null,'capacity'=>null,
                'clients'=>null,'stations'=>null,'state'=>'WARN','uptime'=>'','signal'=>null,'ccq'=>null
            ]);
    }

    $rxSnapshot = function_exists('ua_receiver_match_snapshot') ? ua_receiver_match_snapshot($conn, 60, true) : [];
    $rxReceiverSummary = (array)($rxSnapshot['receiver_summary'] ?? $rxSnapshot['summary'] ?? []);
    $rxRouterPortSummary = (array)($rxSnapshot['router_port_summary'] ?? []);
    $rxByDevice = function_exists('ua_receiver_match_by_device') ? ua_receiver_match_by_device($rxSnapshot) : [];
    $hasOfficialRxSnapshot = !empty($rxSnapshot['ok']) && empty($rxSnapshot['source_scan_stale']) && !empty($rxByDevice);
    $rxLive = function_exists('ua_receiver_match_live_counts') ? ua_receiver_match_live_counts($conn, $rxSnapshot) : ['total'=>0,'by_device'=>[]];
    $rxLiveByDevice = (array)($rxLive['by_device'] ?? []);
    $down = 0; $receiverStations = 0; $receiverLiveStations = 0;
    foreach ($list as &$it) {
        $iid = (int)($it['id'] ?? 0);
        if (isset($rxByDevice[$iid])) {
            $m = $rxByDevice[$iid];
            $raw = (int)($m['raw_mac_count'] ?? ($it['stations'] ?? $it['clients'] ?? 0));
            $officialMatched = (int)($m['matched_count'] ?? 0);
            $matched = noc_rx_current_matched_count((array)$m);
            $live = (int)($rxLiveByDevice[$iid] ?? 0);
            if ($matched <= 0 && $live > 0 && (int)($m['current_client_mac_count'] ?? 0) <= 0) {
                $matched = $live;
                $it['count_source'] = 'subscriber_live_assignment_fallback';
            }
            $it['raw_macs'] = $raw;
            $it['raw_clients'] = (int)($it['clients'] ?? 0);
            $it['matched_clients'] = $matched;
            $it['official_matched_clients'] = $officialMatched;
            $it['live_clients'] = $live;
            $it['stations_display'] = function_exists('ua_receiver_match_display_pair') ? ua_receiver_match_display_pair($matched, $live) : ($matched . '/' . $live);
            $it['unknown_macs'] = (int)($m['unknown_count'] ?? 0);
            $it['ambiguous_macs'] = (int)($m['ambiguous_count'] ?? 0);
            $it['clients'] = $matched;
            $it['stations'] = $matched;
            if (empty($it['count_source'])) $it['count_source'] = 'receiver_mac_match';
        } else {
            $it['live_clients'] = (int)($rxLiveByDevice[$iid] ?? 0);
            $it['raw_clients'] = (int)($it['clients'] ?? 0);
            $it['matched_clients'] = null;
            $it['clients'] = null;
            $it['stations'] = null;
            $it['stations_display'] = (int)$it['live_clients'] > 0 ? ('—/' . (int)$it['live_clients']) : '—';
            $it['count_source'] = $hasOfficialRxSnapshot ? 'receiver_mac_match_missing_device' : 'receiver_match_unavailable';
        }
        if (in_array(strtoupper((string)($it['state'] ?? '')), ['OFFLINE','DOWN'], true)) $down++;
        if (isset($rxByDevice[$iid])) $receiverStations += (int)($it['stations'] ?? $it['clients'] ?? 0);
        $receiverLiveStations += (int)($it['live_clients'] ?? 0);
    }
    unset($it);

    $downChecks=[];
    foreach($list as $it){if((string)($it['device_type']??'')==='router_port')continue;if(in_array(strtoupper((string)($it['state']??'')),['OFFLINE','DOWN'],true))$downChecks[]=['group'=>'receivers','id'=>(int)($it['id']??0)];}
    $confirmed=ua_device_subscriber_confirm_batch($conn,$downChecks);$alertDown=0;
    foreach($list as &$it){$key='receivers:id:'.(int)($it['id']??0);if(isset($confirmed['items'][$key])){$it['subscriber_confirmation']=$confirmed['items'][$key];if(!empty($confirmed['items'][$key]['allow_alert']))$alertDown++;}}
    unset($it);

    // Append persisted custom router-port matching rows from the official receiver snapshot.
    // They are not physical receiver devices, but the dashboard must display them beside receivers.
    $existingIds = [];
    foreach ($list as $li) { $existingIds[(int)($li['id'] ?? 0)] = true; }
    foreach ($rxByDevice as $rid => $m) {
        $rid = (int)$rid;
        if ($rid <= 0 || isset($existingIds[$rid])) continue;
        if ((string)($m['source_type'] ?? '') !== 'router_port') continue;
        $officialMatched = (int)($m['matched_count'] ?? 0);
        $matched = noc_rx_current_matched_count((array)$m);
        $live = (int)($rxLiveByDevice[$rid] ?? 0);
        $state = 'UP';
        $list[] = [
            'id'=>$rid,
            'name'=>(string)($m['name'] ?? 'منفذ راوتر مخصص'),
            'host'=>(string)($m['host'] ?? ($m['router_host'] ?? '')),
            'model'=>'RouterOS Port',
            'vendor'=>'mikrotik',
            'device_type'=>'router_port',
            'state'=>$state,
            'uptime'=>'',
            'signal'=>null,
            'ccq'=>null,
            'quality'=>null,
            'capacity'=>null,
            'clients'=>$matched,
            'stations'=>$matched,
            'raw_macs'=>(int)($m['raw_mac_count'] ?? 0),
            'raw_clients'=>(int)($m['raw_mac_count'] ?? 0),
            'matched_clients'=>$matched,
            'official_matched_clients'=>$officialMatched,
            'live_clients'=>$live,
            'stations_display'=>function_exists('ua_receiver_match_display_pair') ? ua_receiver_match_display_pair($matched, $live) : ($matched . '/' . $live),
            'unknown_macs'=>(int)($m['unknown_count'] ?? 0),
            'ambiguous_macs'=>(int)($m['ambiguous_count'] ?? 0),
            'count_source'=>'router_port_mac_match',
            'router_id'=>(int)($m['router_id'] ?? 0),
            'router_name'=>(string)($m['router_name'] ?? ''),
            'interface'=>(string)($m['interface'] ?? ''),
            'interface_label'=>(string)($m['interface_label'] ?? ''),
        ];
        // Do not add router-port counts to receiver totals. They are displayed as independent rows only.
        $existingIds[$rid] = true;
    }

    $summary = [
        // Keep the legacy dashboard total receiver-only. Router ports are displayed as
        // independent rows, but must not inflate the visible receiver count or station totals.
        'total'=>count($devices),
        'visible_total'=>count($list),
        'receiver_total'=>count($devices),
        'router_port_total'=>(int)($rxRouterPortSummary['ports'] ?? $rxRouterPortSummary['devices'] ?? 0),
        'down'=>$down,
        'alert_down'=>$alertDown,
        'total_stations'=>$hasOfficialRxSnapshot ? $receiverStations : null,
        'live_total_stations'=>$receiverLiveStations,
        'total_stations_display'=>$hasOfficialRxSnapshot
            ? (function_exists('ua_receiver_match_display_pair') ? ua_receiver_match_display_pair($receiverStations, $receiverLiveStations) : ($receiverStations . '/' . $receiverLiveStations))
            : '—',
        'raw_macs_total'=>(int)($rxReceiverSummary['raw_mac_total'] ?? 0),
        'client_macs_total'=>(int)($rxReceiverSummary['current_client_mac_total'] ?? $rxReceiverSummary['client_mac_total'] ?? 0),
        'official_matched_total'=>$hasOfficialRxSnapshot ? (int)($rxReceiverSummary['matched_total'] ?? 0) : null,
        'matched_total'=>$hasOfficialRxSnapshot ? $receiverStations : null,
        'unknown_total'=>(int)($rxReceiverSummary['unknown_total'] ?? 0),
        'ambiguous_total'=>(int)($rxReceiverSummary['ambiguous_total'] ?? 0),
        'router_port_summary'=>$rxRouterPortSummary,
        'official_snapshot'=>$hasOfficialRxSnapshot,
        'snapshot_age'=>(int)($rxSnapshot['age'] ?? 999999),
        'snapshot_stale'=>!empty($rxSnapshot['stale']),
        'source_scan_age'=>(int)($rxSnapshot['source_scan_age'] ?? 999999),
        'source_scan_stale'=>!empty($rxSnapshot['source_scan_stale']),
        'count_source'=>$hasOfficialRxSnapshot ? 'receiver_mac_match' : 'receiver_match_unavailable',
        'count_label'=>$hasOfficialRxSnapshot
            ? 'مطابقة MAC من قراءة المستقبلات الحالية مع مصادر هوية المشتركين — منافذ الراوتر معروضة مستقلة ولا تضخم أرقام المستقبلات'
            : 'جاري التحميل'
    ];
    noc_json_response(['ok'=>true,'items'=>$list,'summary'=>$summary,'last_update'=>$cache['last_update'] ?? 0],200);
} catch (Throwable $e) {
    noc_json_response(['ok'=>true,'items'=>[],'summary'=>['total'=>0,'down'=>0,'total_stations'=>null,'count_source'=>'ppp_active','count_label'=>'جلسات PPPoE النشطة'],'error'=>'fallback'],200);
}
