<?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_traffic','can_view_dashboard_stats']);
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__ . '/../mikrotik_lib.php';
require_once __DIR__ . '/../routeros_api.php';
require_once __DIR__ . '/../mikrotik_connection_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();
if (function_exists('mysqli_report')) { @mysqli_report(MYSQLI_REPORT_OFF); }

const NOC_TRAFFIC_CACHE_TTL = 5;
const NOC_TRAFFIC_STALE_GRACE = 45;

function noc_traffic_setting_get(mysqli $conn, string $key, $default = null) {
    $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_traffic_setting_any(mysqli $conn, array $keys, $default = null) {
    foreach ($keys as $key) {
        $value = noc_traffic_setting_get($conn, (string)$key, null);
        if ($value !== null && $value !== '') return $value;
    }
    return $default;
}

function noc_traffic_routers(mysqli $conn): array {
    $rows = [];
    $r = @$conn->query("SHOW TABLES LIKE 'routers'");
    if (!$r || !$r->num_rows) return $rows;
    $sql = 'SELECT * FROM routers';
    $cc = @$conn->query("SHOW COLUMNS FROM routers LIKE 'is_enabled'");
    if ($cc && $cc->num_rows) $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_traffic_sources(mysqli $conn, array $routers, float $defaultMax): array {
    $byId = [];
    foreach ($routers as $router) $byId[(int)($router['id'] ?? 0)] = $router;

    $sources = json_decode((string)noc_traffic_setting_any($conn, ['noc_traffic_sources', 'hud_traffic_sources'], '[]'), true);
    if (!is_array($sources)) $sources = [];

    if (!$sources) {
        $legacy = trim((string)noc_traffic_setting_any($conn, ['noc_hud_iface_name', 'hud_iface_name'], ''));
        $primary = (int)noc_traffic_setting_any($conn, ['noc_primary_router_id', 'hud_primary_router_id'], '0');
        if ($legacy !== '' && !empty($routers)) {
            $fallback = isset($byId[$primary]) ? $byId[$primary] : $routers[0];
            $sources[] = [
                'router_id' => (int)($fallback['id'] ?? 0),
                'iface' => $legacy,
                'label' => $legacy,
                'max_mbps' => $defaultMax,
                'enabled' => 1,
            ];
        }
    }

    $out = [];
    foreach ($sources as $src) {
        if (!is_array($src)) continue;
        if (isset($src['enabled']) && (int)$src['enabled'] !== 1) continue;
        $rid = (int)($src['router_id'] ?? 0);
        $iface = trim((string)($src['iface'] ?? ''));
        if ($rid <= 0 || $iface === '' || !isset($byId[$rid])) continue;
        $src['router_id'] = $rid;
        $src['iface'] = $iface;
        $src['_router'] = $byId[$rid];
        $out[] = $src;
    }
    return $out;
}

function noc_traffic_previous_sources(array $cache): array {
    $map = [];
    foreach (($cache['sources'] ?? []) as $row) {
        if (!is_array($row)) continue;
        $key = (int)($row['router_id'] ?? 0) . ':' . strtolower(trim((string)($row['iface'] ?? '')));
        if ($key !== '0:') $map[$key] = $row;
    }
    return $map;
}

function noc_traffic_monitor_once($api, string $iface): array {
    if (!is_object($api)) return ['ok' => false, 'rx_bps' => 0, 'tx_bps' => 0, 'error' => 'api_not_connected'];

    $rows = null;
    try {
        if (method_exists($api, 'command')) {
            $rows = $api->command(['/interface/monitor-traffic', '=interface=' . $iface, '=once=true', '=__ua_cache_bypass=yes']);
        } elseif (method_exists($api, 'comm')) {
            $rows = $api->comm('/interface/monitor-traffic', ['interface' => $iface, 'once' => '']);
        }
    } catch (Throwable $e) {
        return ['ok' => false, 'rx_bps' => 0, 'tx_bps' => 0, 'error' => $e->getMessage()];
    }

    if (!is_array($rows)) return ['ok' => false, 'rx_bps' => 0, 'tx_bps' => 0, 'error' => 'empty_reply'];
    foreach ($rows as $row) {
        if (!is_array($row)) continue;
        if (($row['.type'] ?? '') === '!trap' || ($row['.type'] ?? '') === '!fatal') {
            return ['ok' => false, 'rx_bps' => 0, 'tx_bps' => 0, 'error' => (string)($row['message'] ?? 'routeros_trap')];
        }
        if (($row['.type'] ?? '') !== '!re') continue;
        return [
            'ok' => true,
            'rx_bps' => max(0, (int)($row['rx-bits-per-second'] ?? $row['rx-bits-per-sec'] ?? 0)),
            'tx_bps' => max(0, (int)($row['tx-bits-per-second'] ?? $row['tx-bits-per-sec'] ?? 0)),
            'error' => '',
        ];
    }
    return ['ok' => false, 'rx_bps' => 0, 'tx_bps' => 0, 'error' => 'no_traffic_row'];
}

function noc_traffic_read_source($api, array $src, array $previous, int $now, int $cacheTs): array {
    $iface = (string)$src['iface'];
    $first = noc_traffic_monitor_once($api, $iface);
    $rx = round(((float)$first['rx_bps']) / 1000000, 1);
    $tx = round(((float)$first['tx_bps']) / 1000000, 1);
    $total = round($rx + $tx, 1);

    $prevTotal = (float)($previous['total_mbps'] ?? 0);
    if ((!$first['ok'] || ($total <= 0.0 && $prevTotal > 0.0)) && is_object($api)) {
        usleep(180000);
        $second = noc_traffic_monitor_once($api, $iface);
        $rx2 = round(((float)$second['rx_bps']) / 1000000, 1);
        $tx2 = round(((float)$second['tx_bps']) / 1000000, 1);
        $total2 = round($rx2 + $tx2, 1);
        if ($second['ok'] && ($total2 > 0.0 || !$first['ok'])) {
            $first = $second;
            $rx = $rx2;
            $tx = $tx2;
            $total = $total2;
        }
    }

    $stale = false;
    if (!$first['ok'] && $prevTotal > 0.0 && ($now - $cacheTs) <= NOC_TRAFFIC_STALE_GRACE) {
        $rx = round((float)($previous['rx_mbps'] ?? 0), 1);
        $tx = round((float)($previous['tx_mbps'] ?? 0), 1);
        $total = round($rx + $tx, 1);
        $stale = true;
    }

    $router = $src['_router'] ?? [];
    $label = trim((string)($src['label'] ?? ''));
    if ($label === '') $label = $iface;

    return [
        'router_id' => (int)$src['router_id'],
        'router_name' => (string)($router['name'] ?? ''),
        'router_host' => (string)($router['host'] ?? ''),
        'iface' => $iface,
        'label' => $label,
        'rx_mbps' => $rx,
        'tx_mbps' => $tx,
        'total_mbps' => $total,
        'max_mbps' => (float)($src['max_mbps'] ?? 300),
        'ok' => (bool)$first['ok'],
        'stale' => $stale,
        'error' => $first['ok'] ? '' : (string)($first['error'] ?? ''),
        'ts' => $now,
    ];
}

function noc_traffic_sum(array $out): array {
    $rx = 0.0;
    $tx = 0.0;
    foreach (($out['sources'] ?? []) as $source) {
        $rx += (float)($source['rx_mbps'] ?? 0);
        $tx += (float)($source['tx_mbps'] ?? 0);
    }
    $total = round($rx + $tx, 1);
    $out['sum'] = ['rx_mbps' => round($rx, 1), 'tx_mbps' => round($tx, 1), 'total_mbps' => $total];
    $out['kpi']['wan_rx_mbps'] = $out['sum']['rx_mbps'];
    $out['kpi']['wan_tx_mbps'] = $out['sum']['tx_mbps'];
    $out['kpi']['wan_total_mbps'] = $total;
    return $out;
}

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

    $cacheKey = 'noc_traffic_card';
    $cachePath = noc_cache_file($cacheKey);
    $empty = [
        'ok' => true,
        'server_time' => '',
        'iface' => ['selected' => '', 'max_mbps' => 300],
        'sum' => ['rx_mbps' => 0.0, 'tx_mbps' => 0.0, 'total_mbps' => 0.0],
        'kpi' => ['wan_rx_mbps' => 0.0, 'wan_tx_mbps' => 0.0, 'wan_total_mbps' => 0.0],
        'sources' => [],
        'peak' => ['iface' => '', 'wan_peak_mbps' => 0.0],
        'last_update' => 0,
        'meta' => ['cache_ttl' => NOC_TRAFFIC_CACHE_TTL, 'stale_grace' => NOC_TRAFFIC_STALE_GRACE],
    ];
    $cache = noc_load_json_file($cachePath, $empty);
    $now = time();

    if ((string)($_GET['cache_only'] ?? '') === '1') {
        noc_json_response(noc_traffic_sum($cache), 200);
    }

    $cacheTs = (int)($cache['last_update'] ?? 0);
    if ($cacheTs >= ($now - NOC_TRAFFIC_CACHE_TTL)) {
        noc_json_response(noc_traffic_sum($cache), 200);
    }

    $lock = noc_try_lock($cacheKey);
    if (!$lock) {
        clearstatcache(true, $cachePath);
        $cache = noc_load_json_file($cachePath, $cache);
        noc_json_response(noc_traffic_sum($cache), 200);
    }

    $defaultMax = (float)noc_traffic_setting_any($conn, ['noc_hud_max_mbps', 'hud_max_mbps'], 300);
    $routers = noc_traffic_routers($conn);
    $sources = noc_traffic_sources($conn, $routers, $defaultMax);
    $previous = noc_traffic_previous_sources($cache);

    $out = $empty;
    $out['server_time'] = date('Y-m-d H:i:s');
    $out['last_update'] = $now;
    $out['iface']['max_mbps'] = $defaultMax;
    $out['meta'] = ['cache_ttl' => NOC_TRAFFIC_CACHE_TTL, 'stale_grace' => NOC_TRAFFIC_STALE_GRACE, 'errors' => []];

    $selected = [];
    $maxSeen = $defaultMax;
    $byRouter = [];
    foreach ($sources as $src) {
        $byRouter[(int)$src['router_id']][] = $src;
    }

    foreach ($byRouter as $routerSources) {
        $router = $routerSources[0]['_router'] ?? [];
        $err = '';
        $api = ua_mt_connect_config($router, $err, 0.8);

        foreach ($routerSources as $src) {
            $key = (int)$src['router_id'] . ':' . strtolower((string)$src['iface']);
            $row = noc_traffic_read_source($api, $src, $previous[$key] ?? [], $now, $cacheTs);
            if (!$row['ok'] && $row['error'] === '' && $err !== '') $row['error'] = $err;
            if (!$row['ok'] && !$row['stale']) $out['meta']['errors'][] = $row['label'] . ': ' . $row['error'];
            $out['sources'][] = $row;
            $selected[] = $row['label'];
            $maxSeen = max($maxSeen, (float)$row['max_mbps']);
        }

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

    $out['iface']['selected'] = implode(' + ', $selected);
    $out['iface']['max_mbps'] = $maxSeen;
    $out = noc_traffic_sum($out);
    noc_save_json_file($cachePath, $out);
    noc_unlock($lock);
    noc_json_response($out, 200);
} catch (Throwable $e) {
    if (isset($lock) && $lock) noc_unlock($lock);
    if (isset($cachePath)) {
        $stale = noc_load_json_file($cachePath, null);
        if (is_array($stale)) noc_json_response(noc_traffic_sum($stale), 200);
    }
    noc_json_response([
        'ok' => true,
        'sum' => ['rx_mbps' => 0, 'tx_mbps' => 0, 'total_mbps' => 0],
        'sources' => [],
        'iface' => ['selected' => '-', 'max_mbps' => 300],
        'kpi' => ['wan_rx_mbps' => 0, 'wan_tx_mbps' => 0, 'wan_total_mbps' => 0],
        'meta' => ['errors' => [$e->getMessage()]],
    ], 200);
}
