<?php
require_once __DIR__ . '/../auth.php';
if (function_exists('require_login')) require_login('login.php');
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__ . '/../helpers.php';
require_once __DIR__ . '/../mikrotik_lib.php';
require_once __DIR__ . '/../ua_unified_stats_lib.php';
require_once __DIR__ . '/../stats_snapshot_lib.php';
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';

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

function noc_json_out($out, $code) {
    http_response_code((int)$code);
    while (ob_get_level() > 0) { @ob_end_clean(); }
    echo json_encode($out, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    exit;
}
function noc_safe_contains($haystack, $needle) {
    return $needle !== '' && strpos((string)$haystack, (string)$needle) !== false;
}
function noc_setting_read($conn, $key, $default = null) {
    if (!($conn instanceof mysqli)) return $default;
    $keyEsc = $conn->real_escape_string($key);
    $r = @$conn->query("SELECT setting_value v FROM app_settings WHERE setting_key='$keyEsc' LIMIT 1");
    if ($r && ($row = $r->fetch_assoc())) return $row['v'];
    $r = @$conn->query("SELECT value v FROM app_settings WHERE `key`='$keyEsc' LIMIT 1");
    if ($r && ($row = $r->fetch_assoc())) return $row['v'];
    return $default;
}
function noc_table_exists_safe($conn, $table) {
    if (function_exists('table_exists')) return @table_exists($conn, $table);
    $table = $conn->real_escape_string($table);
    $r = @$conn->query("SHOW TABLES LIKE '$table'");
    return ($r && $r->num_rows > 0);
}
function noc_col_exists_safe($conn, $table, $col) {
    if (function_exists('col_exists')) return @col_exists($conn, $table, $col);
    $table = $conn->real_escape_string($table);
    $col = $conn->real_escape_string($col);
    $r = @$conn->query("SHOW COLUMNS FROM `$table` LIKE '$col'");
    return ($r && $r->num_rows > 0);
}

function noc_airmax_cached_probe($conn) {
    $items = array();
    $devices = function_exists('airmax_get_all') ? airmax_get_all($conn, true) : array();
    foreach ($devices as $d) {
        $id = (int)($d['id'] ?? 0);
        if ($id <= 0) continue;
        $cached = null;
        if (function_exists('airmax_json_read') && function_exists('airmax_result_cache_path')) {
            $cached = airmax_json_read(airmax_result_cache_path($d), null);
        }
        $probe = is_array($cached) ? (array)($cached['probe'] ?? $cached) : array();
        $metrics = (array)($probe['metrics'] ?? ($cached['metrics'] ?? array()));
        $state = (string)($probe['state'] ?? ($cached['state'] ?? 'WARN'));
        if ($state === '' && !empty($probe['up'])) $state = 'UP';
        $items[] = array(
            'id'=>$id,
            'name'=>(string)($d['name'] ?? ($probe['name'] ?? '')),
            'host'=>(string)($d['host'] ?? ($probe['host'] ?? '')),
            'type'=>(string)($d['device_type'] ?? ($probe['type'] ?? '')),
            'state'=>$state ?: 'WARN',
            'metrics'=>$metrics,
            'latency_ms'=>$probe['latency_ms'] ?? null,
            'cache_only'=>true,
        );
    }
    return array('ok'=>true, 'items'=>$items, 'summary'=>array('total'=>count($items)), 'cache_only'=>true);
}

function noc_get_routers_all($conn) {
    $rows = array();
    if (!noc_table_exists_safe($conn, 'routers')) return $rows;
    $sql = "SELECT * FROM routers";
    if (noc_col_exists_safe($conn, 'routers', 'is_enabled')) $sql .= " WHERE is_enabled=1";
    $sql .= " ORDER BY id ASC";
    $r = @$conn->query($sql);
    if ($r) { while ($row = $r->fetch_assoc()) $rows[] = $row; }
    return $rows;
}
function noc_get_mikrotik_fallback_row($conn) {
    if (!noc_table_exists_safe($conn, 'mikrotik_settings')) return null;
    $r = @$conn->query("SELECT * FROM mikrotik_settings ORDER BY id ASC LIMIT 1");
    if (!$r) return null;
    $row = $r->fetch_assoc();
    if (!$row) return null;
    return array(
        'id' => 0,
        'name' => 'MikroTik',
        'host' => isset($row['host']) ? (string)$row['host'] : '',
        'username' => isset($row['username']) ? (string)$row['username'] : '',
        'password' => isset($row['password']) ? (string)$row['password'] : '',
        'api_port' => isset($row['api_port']) ? (int)$row['api_port'] : 8728,
        'model' => '—',
        'role' => 'pppoe'
    );
}
function noc_router_api_connect($router) {
    if (!is_array($router)) return false;
    $err = '';
    return ua_mt_connect_config($router, $err, 0.8);
}
function noc_first_re_row($rows) {
    if (!is_array($rows)) return null;
    foreach ($rows as $r) { if (is_array($r) && (($r['.type'] ?? '') === '!re')) return $r; }
    return null;
}
function noc_count_re_rows($rows) {
    $c = 0;
    if (!is_array($rows)) return 0;
    foreach ($rows as $r) { if (is_array($r) && (($r['.type'] ?? '') === '!re')) $c++; }
    return $c;
}
function noc_probe_router_card($router, $fallbackMt) {
    $base = array(
        'id' => (int)(isset($router['id']) ? $router['id'] : 0),
        'name' => isset($router['name']) ? (string)$router['name'] : 'Router',
        'host' => isset($router['host']) ? (string)$router['host'] : '',
        'identity' => isset($router['name']) ? (string)$router['name'] : 'Router',
        'model' => isset($router['model']) && $router['model'] !== '' ? (string)$router['model'] : '—',
        'version' => '',
        'uptime' => '—',
        'cpu_pct' => 0,
        'ram_pct' => 0,
        'ppp_active' => 0,
        'state' => 'OFFLINE',
        'state_note' => ''
    );

    $candidates = array($router);
    if (is_array($fallbackMt)) {
        $sameHost = trim((string)($fallbackMt['host'] ?? '')) === trim((string)($router['host'] ?? ''));
        $role = strtolower(trim((string)($router['role'] ?? '')));
        $name = strtolower(trim((string)($router['name'] ?? '')));
        if ($sameHost || $role === 'pppoe' || noc_safe_contains($name, '2116') || noc_safe_contains($name, 'ppp')) {
            $cand = $router;
            $cand['host'] = $fallbackMt['host'];
            $cand['username'] = $fallbackMt['username'];
            $cand['password'] = $fallbackMt['password'];
            $cand['api_port'] = $fallbackMt['api_port'];
            $candidates[] = $cand;
        }
    }

    foreach ($candidates as $cand) {
        $api = noc_router_api_connect($cand);
        if (!$api) continue;

        $card = $base;
        $card['host'] = isset($cand['host']) ? (string)$cand['host'] : $card['host'];
        $connected = true;
        $resourceOk = false;
        $identityOk = false;

        try {
            $row = noc_first_re_row($api->command(array('/system/resource/print')));
            if ($row) {
                $resourceOk = true;
                $cpu = (int)($row['cpu-load'] ?? 0);
                $free = (float)($row['free-memory'] ?? 0);
                $total = (float)($row['total-memory'] ?? 0);
                $card['cpu_pct'] = $cpu;
                $card['ram_pct'] = $total > 0 ? (int)round((1 - ($free / $total)) * 100) : 0;
                $card['uptime'] = (string)($row['uptime'] ?? '—');
                if (!empty($row['board-name'])) $card['model'] = (string)$row['board-name'];
                elseif (!empty($row['model'])) $card['model'] = (string)$row['model'];
                $card['version'] = (string)($row['version'] ?? '');
            }
        } catch (Throwable $e) {}

        try {
            $row = noc_first_re_row($api->command(array('/system/identity/print')));
            if ($row) {
                $identityOk = true;
                $card['identity'] = (string)($row['name'] ?? $card['identity']);
                if ($card['name'] === 'Router' || $card['name'] === '') $card['name'] = $card['identity'];
            }
        } catch (Throwable $e) {}

        try {
            $card['ppp_active'] = noc_count_re_rows($api->command(array('/ppp/active/print')));
        } catch (Throwable $e) {}

        if (method_exists($api, 'disconnect')) { try { $api->disconnect(); } catch (Throwable $e) {} }

        $card['state'] = ($resourceOk && $identityOk) ? 'ONLINE' : 'PARTIAL';
        if (!$resourceOk && !$identityOk) $card['state_note'] = 'connected_minimal';
        elseif (!$resourceOk) $card['state_note'] = 'resource_unavailable';
        elseif (!$identityOk) $card['state_note'] = 'identity_unavailable';
        return $card;
    }

    return $base;
}

$conn = isset($GLOBALS['conn']) ? $GLOBALS['conn'] : null;
if (!($conn instanceof mysqli)) {
    noc_json_out(array('ok' => false, 'reason' => 'db_not_ready'), 500);
}

$out = array(
    'ok' => true,
    'server_time' => date('Y-m-d H:i:s'),
    'router' => array('state' => 'OFFLINE', 'host' => null, 'identity' => '—', 'model' => '—', 'version' => '', 'uptime' => '—'),
    'routers_cards' => array(),
    'kpi' => array('cpu_pct' => 0, 'ram_pct' => 0, 'ppp_active' => 0, 'wan_rx_mbps' => 0.0, 'wan_tx_mbps' => 0.0, 'wan_total_mbps' => 0.0),
    'counts' => array('total' => 0, 'online' => 0, 'offline' => 0, 'financial_cut' => 0, 'ppp_active' => 0, 'unsafe_speed' => 0),
    'iface' => array('selected' => '', 'max_mbps' => (float)noc_setting_read($conn, 'hud_max_mbps', 300)),
    'sectors' => array('items' => array(), 'down' => 0, 'total' => 0),
    'subtowers' => array('items' => array(), 'total_stations' => 0, 'known' => 0),
    'discovery' => array('iface' => 'airMAX', 'new' => 0, 'total_seen' => 0),
    'ppp' => array('events' => array()),
    'alerts' => array(),
    'meta' => array('errors' => array())
);

try {
    if (function_exists('ua_app_stats_snapshot') || function_exists('ua_unified_stats')) {
        $counts = function_exists('ua_app_stats_snapshot') ? ua_app_stats_snapshot($conn, 8, false) : ua_unified_stats($conn, 8);
        if (is_array($counts)) {
            $out['counts']['total'] = (int)($counts['total'] ?? 0);
            $out['counts']['online'] = (int)($counts['online'] ?? 0);
            $out['counts']['offline'] = (int)($counts['offline'] ?? 0);
            $out['counts']['financial_cut'] = (int)($counts['disabled'] ?? 0);
            $out['counts']['ppp_active'] = (int)($counts['ppp_active'] ?? $counts['online'] ?? 0);
            $out['counts']['unsafe_speed'] = (int)($counts['unsafe_speed'] ?? 0);
        }
    }

    $routers = noc_get_routers_all($conn);
    $fallbackMt = noc_get_mikrotik_fallback_row($conn);
    if (!$routers && $fallbackMt) $routers[] = $fallbackMt;

    $wanted = json_decode((string)noc_setting_read($conn, 'hud_resource_router_ids', '[]'), true);
    $wantedIds = is_array($wanted) ? array_map('intval', $wanted) : array();
    $selectedRouters = array();
    if ($wantedIds) {
        foreach ($routers as $router) {
            if (in_array((int)($router['id'] ?? 0), $wantedIds, true)) $selectedRouters[] = $router;
        }
    }
    if (!$selectedRouters) $selectedRouters = $routers;

    foreach ($selectedRouters as $router) {
        try {
            $card = noc_probe_router_card($router, $fallbackMt);
            $out['routers_cards'][] = $card;
            if ($card['state'] === 'OFFLINE') {
                $out['alerts'][] = array('level' => 'danger', 'title' => 'راوتر غير متصل', 'msg' => $card['name'] . ' - ' . $card['host'], 'ts' => $out['server_time']);
            } elseif ($card['state'] === 'PARTIAL') {
                $out['alerts'][] = array('level' => 'warn', 'title' => 'راوتر بقراءة جزئية', 'msg' => $card['name'] . ' - تم الوصول دون كل القراءات', 'ts' => $out['server_time']);
            }
        } catch (Throwable $e) {
            $out['meta']['errors'][] = 'router_probe: ' . $e->getMessage();
        }
    }

    if (!empty($out['routers_cards'])) {
        $first = $out['routers_cards'][0];
        $out['router'] = array('state' => $first['state'], 'host' => $first['host'], 'identity' => $first['identity'], 'model' => $first['model'], 'version' => $first['version'], 'uptime' => $first['uptime']);
        $out['kpi']['cpu_pct'] = (int)$first['cpu_pct'];
        $out['kpi']['ram_pct'] = (int)$first['ram_pct'];
    }


    // Stage 11: unsafe/open-speed count is intentionally read only from ua_unified_stats().
    // This prevents a second calculation path and avoids stale temp-file counters.
    if ((int)($out['counts']['unsafe_speed'] ?? 0) > 0) {
        $out['alerts'][] = array('level' => 'warn', 'title' => 'تنبيه تقييد السرعة', 'msg' => 'يوجد ' . $out['counts']['unsafe_speed'] . ' حساب يعمل بلا تقييد سرعة فعلي', 'ts' => $out['server_time']);
    }

    // كرت الراوتر قد يقرأ أكثر من راوتر؛ عداد المشتركين يبقى من المصدر الموحد فقط.
    $out['kpi']['ppp_active'] = (int)($out['counts']['ppp_active'] ?? $out['counts']['online'] ?? 0);

    try {
        $probe = noc_airmax_cached_probe($conn);
        $rxSnapshot = function_exists('ua_receiver_match_snapshot') ? ua_receiver_match_snapshot($conn, 60, true) : array();
        $rxByDevice = function_exists('ua_receiver_match_by_device') ? ua_receiver_match_by_device($rxSnapshot) : array();
        $rxLive = function_exists('ua_receiver_match_live_counts') ? ua_receiver_match_live_counts($conn, $rxSnapshot) : array('by_device'=>array());
        $rxLiveByDevice = is_array($rxLive) ? (array)($rxLive['by_device'] ?? array()) : array();
        $sectorDown = 0; $sectorTotal = 0; $stationSum = 0; $stationLiveSum = 0; $known = 0; $subItems = array();
        $items = isset($probe['items']) && is_array($probe['items']) ? $probe['items'] : array();
        foreach ($items as $it) {
            $m = (isset($it['metrics']) && is_array($it['metrics'])) ? $it['metrics'] : array();
            $state = ($it['state'] ?? '') === 'UP' ? 'UP' : ((($it['state'] ?? '') === 'WARN') ? 'WARN' : 'DOWN');
            $devId = (int)($it['id'] ?? 0);
            $matchedInfo = isset($rxByDevice[$devId]) ? $rxByDevice[$devId] : array();
            $isStationDevice = function_exists('airmax_device_is_station') ? airmax_device_is_station(['device_type'=>($it['type'] ?? '')]) : in_array(($it['type'] ?? ''), ['station','powerbeam'], true);
            $matchedClients = ($isStationDevice && isset($matchedInfo['matched_count'])) ? (int)$matchedInfo['matched_count'] : null;
            $liveClients = $isStationDevice ? (int)($rxLiveByDevice[$devId] ?? 0) : 0;
            $row = array(
                'host' => isset($it['host']) ? $it['host'] : '',
                'name' => isset($it['name']) ? $it['name'] : (isset($it['host']) ? $it['host'] : ''),
                'state' => $state,
                'model' => isset($m['model']) ? $m['model'] : null,
                'cpu' => (isset($m['cpu']) && $m['cpu'] !== '') ? (float)$m['cpu'] : null,
                'memory' => (isset($m['memory']) && $m['memory'] !== '') ? (float)$m['memory'] : null,
                'uptime' => isset($m['uptime']) ? $m['uptime'] : null,
                'clients' => $matchedClients,
                'live_clients' => $liveClients,
                'stations_display' => ($matchedClients !== null ? ((string)$matchedClients . '/' . (string)$liveClients) : ('—/' . (string)$liveClients)),
                'raw_macs' => isset($matchedInfo['raw_mac_count']) ? (int)$matchedInfo['raw_mac_count'] : null,
                'unknown_macs' => isset($matchedInfo['unknown_count']) ? (int)$matchedInfo['unknown_count'] : 0,
                'ambiguous_macs' => isset($matchedInfo['ambiguous_count']) ? (int)$matchedInfo['ambiguous_count'] : 0,
                'count_source' => isset($matchedInfo['matched_count']) ? 'receiver_mac_match_lan0' : ($isStationDevice ? 'receiver_match_pending' : 'access_skipped'),
                'signal' => isset($m['signal']) ? $m['signal'] : null,
                'latency_ms' => isset($it['latency_ms']) ? $it['latency_ms'] : null,
            );
            if (!$isStationDevice) {
                $sectorTotal++;
                if ($state === 'DOWN') $sectorDown++;
                $out['sectors']['items'][] = $row;
            } else {
                $row['stations'] = $row['clients'];
                $subItems[] = $row;
            }
            if ($row['clients'] !== null) $stationSum += (int)$row['clients'];
            $stationLiveSum += (int)($row['live_clients'] ?? 0);
            if ($row['clients'] !== null || ($row['signal'] !== null)) $known++;
        }
        $out['sectors']['total'] = $sectorTotal;
        $out['sectors']['down'] = $sectorDown;
        $out['subtowers']['items'] = $subItems;
        $out['subtowers']['total_stations'] = $stationSum;
        $out['subtowers']['live_total_stations'] = $stationLiveSum;
        $out['subtowers']['total_stations_display'] = ((string)$stationSum . '/' . (string)$stationLiveSum);
        $out['subtowers']['known'] = $known;
        $out['discovery']['total_seen'] = isset($probe['summary']['total']) ? (int)$probe['summary']['total'] : count($items);
    } catch (Throwable $e) {
        $out['meta']['errors'][] = 'airmax_probe: ' . $e->getMessage();
    }

    noc_json_out($out, 200);
} catch (Throwable $e) {
    $out['ok'] = false;
    $out['reason'] = 'exception';
    $out['error'] = $e->getMessage();
    noc_json_out($out, 200);
}
