<?php
/**
 * Flexia — contador de tráfico HTTP de la aplicación (peticiones API / action).
 * Persistencia en storage/config/flexia_traffic_stats.json
 */

if (!defined('GRID_ROOT')) {
    define('GRID_ROOT', dirname(__DIR__));
}

const GRID_TRAFFIC_STATS_BASENAME = 'flexia_traffic_stats.json';
const GRID_TRAFFIC_BYDAY_RETENTION_DAYS = 90;

/** @var array<string, mixed>|null */
$GLOBALS['GRID_TRAFFIC_METER_CTX'] = null;

function grid_traffic_stats_path(): string
{
    require_once GRID_ROOT . '/core/security/paths.php';
    AntaraPaths::ensureDirs();

    return AntaraPaths::config() . DIRECTORY_SEPARATOR . GRID_TRAFFIC_STATS_BASENAME;
}

function grid_traffic_meter_user_id(): ?string
{
    if (!class_exists('AntaraSession', false)) {
        return null;
    }
    if (!AntaraSession::isLoggedIn()) {
        return null;
    }
    $u = AntaraSession::user();
    if (!$u || empty($u['id'])) {
        return null;
    }

    return (string) $u['id'];
}

function grid_traffic_meter_request_bytes(): int
{
    $n = 0;
    if (isset($_SERVER['CONTENT_LENGTH']) && $_SERVER['CONTENT_LENGTH'] !== '') {
        $n = max(0, (int) $_SERVER['CONTENT_LENGTH']);
    }
    if ($n > 0) {
        return $n;
    }
    if (!empty($_FILES) && is_array($_FILES)) {
        foreach ($_FILES as $f) {
            if (!is_array($f)) {
                continue;
            }
            if (isset($f['size']) && is_array($f['size'])) {
                foreach ($f['size'] as $s) {
                    $n += max(0, (int) $s);
                }
            } else {
                $n += max(0, (int) ($f['size'] ?? 0));
            }
        }
    }

    return $n;
}

function grid_traffic_meter_begin(): void
{
    if (!empty($GLOBALS['GRID_TRAFFIC_METER_CTX']['active'])) {
        return;
    }
    $GLOBALS['GRID_TRAFFIC_METER_CTX'] = [
        'active' => true,
        'requestBytes' => grid_traffic_meter_request_bytes(),
        'obStarted' => false,
    ];
    if (ob_get_level() === 0) {
        ob_start();
        $GLOBALS['GRID_TRAFFIC_METER_CTX']['obStarted'] = true;
    }
    register_shutdown_function('grid_traffic_meter_shutdown');
}

function grid_traffic_meter_response_bytes(): int
{
    $out = 0;
    if (!empty($GLOBALS['GRID_TRAFFIC_METER_CTX']['obStarted']) && ob_get_level() > 0) {
        $len = ob_get_length();
        if ($len !== false) {
            $out = max($out, (int) $len);
        }
    }
    if (function_exists('headers_list')) {
        foreach (headers_list() as $h) {
            if (stripos($h, 'Content-Length:') === 0) {
                $v = (int) trim(substr($h, strlen('Content-Length:')));
                if ($v > 0) {
                    $out = max($out, $v);
                }
            }
        }
    }

    return $out;
}

function grid_traffic_meter_shutdown(): void
{
    $ctx = $GLOBALS['GRID_TRAFFIC_METER_CTX'] ?? null;
    if (!is_array($ctx) || empty($ctx['active'])) {
        return;
    }
    $in = max(0, (int) ($ctx['requestBytes'] ?? 0));
    $out = grid_traffic_meter_response_bytes();
    if (!empty($ctx['obStarted']) && ob_get_level() > 0) {
        @ob_end_flush();
    }
    grid_traffic_meter_record($in, $out, grid_traffic_meter_user_id());
    $GLOBALS['GRID_TRAFFIC_METER_CTX']['active'] = false;
}

/**
 * @return array{totalInBytes:int,totalOutBytes:int,todayInBytes:int,todayOutBytes:int,day:string}
 */
function grid_traffic_meter_empty_bucket(): array
{
    return [
        'totalInBytes' => 0,
        'totalOutBytes' => 0,
        'todayInBytes' => 0,
        'todayOutBytes' => 0,
        'day' => date('Y-m-d'),
    ];
}

function grid_traffic_meter_read_stats(): array
{
    $path = grid_traffic_stats_path();
    if (!is_file($path)) {
        return [
            'version' => 1,
            'global' => ['totalInBytes' => 0, 'totalOutBytes' => 0, 'byDay' => []],
            'users' => [],
            'updatedAt' => null,
        ];
    }
    $raw = @file_get_contents($path);
    if ($raw === false || $raw === '') {
        return [
            'version' => 1,
            'global' => ['totalInBytes' => 0, 'totalOutBytes' => 0, 'byDay' => []],
            'users' => [],
            'updatedAt' => null,
        ];
    }
    $data = json_decode($raw, true);
    if (!is_array($data)) {
        return [
            'version' => 1,
            'global' => ['totalInBytes' => 0, 'totalOutBytes' => 0, 'byDay' => []],
            'users' => [],
            'updatedAt' => null,
        ];
    }
    if (!isset($data['global']) || !is_array($data['global'])) {
        $data['global'] = ['totalInBytes' => 0, 'totalOutBytes' => 0, 'byDay' => []];
    }
    if (!isset($data['users']) || !is_array($data['users'])) {
        $data['users'] = [];
    }

    return $data;
}

/**
 * @param array<string, mixed> $stats
 */
function grid_traffic_meter_prune_by_day(array &$stats): void
{
    $cut = (new DateTimeImmutable('today'))->modify('-' . GRID_TRAFFIC_BYDAY_RETENTION_DAYS . ' days')->format('Y-m-d');
    foreach (['global'] as $key) {
        if (!isset($stats[$key]['byDay']) || !is_array($stats[$key]['byDay'])) {
            continue;
        }
        foreach (array_keys($stats[$key]['byDay']) as $d) {
            if ((string) $d < $cut) {
                unset($stats[$key]['byDay'][$d]);
            }
        }
    }
    if (!isset($stats['users']) || !is_array($stats['users'])) {
        return;
    }
    foreach ($stats['users'] as $uid => &$ub) {
        if (!is_array($ub) || !isset($ub['byDay']) || !is_array($ub['byDay'])) {
            continue;
        }
        foreach (array_keys($ub['byDay']) as $d) {
            if ((string) $d < $cut) {
                unset($ub['byDay'][$d]);
            }
        }
    }
    unset($ub);
}

function grid_traffic_meter_record(int $inBytes, int $outBytes, ?string $userId): void
{
    if ($inBytes <= 0 && $outBytes <= 0) {
        return;
    }
    require_once GRID_ROOT . '/includes/settings_helpers.php';

    $path = grid_traffic_stats_path();
    $dir = dirname($path);
    if (!is_dir($dir) && !@mkdir($dir, 0755, true)) {
        return;
    }

    $fp = @fopen($path, 'c+');
    if ($fp === false) {
        return;
    }
    if (!@flock($fp, LOCK_EX)) {
        @fclose($fp);
        return;
    }

    $raw = stream_get_contents($fp);
    $stats = grid_traffic_meter_read_stats();
    if ($raw !== false && $raw !== '') {
        $decoded = json_decode($raw, true);
        if (is_array($decoded)) {
            $stats = $decoded;
            if (!isset($stats['global']) || !is_array($stats['global'])) {
                $stats['global'] = ['totalInBytes' => 0, 'totalOutBytes' => 0, 'byDay' => []];
            }
            if (!isset($stats['users']) || !is_array($stats['users'])) {
                $stats['users'] = [];
            }
        }
    }

    $day = date('Y-m-d');
    if (!isset($stats['global']['byDay']) || !is_array($stats['global']['byDay'])) {
        $stats['global']['byDay'] = [];
    }
    if (!isset($stats['global']['byDay'][$day]) || !is_array($stats['global']['byDay'][$day])) {
        $stats['global']['byDay'][$day] = ['in' => 0, 'out' => 0];
    }
    $stats['global']['totalInBytes'] = (int) ($stats['global']['totalInBytes'] ?? 0) + $inBytes;
    $stats['global']['totalOutBytes'] = (int) ($stats['global']['totalOutBytes'] ?? 0) + $outBytes;
    $stats['global']['byDay'][$day]['in'] = (int) ($stats['global']['byDay'][$day]['in'] ?? 0) + $inBytes;
    $stats['global']['byDay'][$day]['out'] = (int) ($stats['global']['byDay'][$day]['out'] ?? 0) + $outBytes;

    if ($userId !== null && $userId !== '') {
        if (!isset($stats['users'][$userId]) || !is_array($stats['users'][$userId])) {
            $stats['users'][$userId] = ['totalInBytes' => 0, 'totalOutBytes' => 0, 'byDay' => []];
        }
        if (!isset($stats['users'][$userId]['byDay']) || !is_array($stats['users'][$userId]['byDay'])) {
            $stats['users'][$userId]['byDay'] = [];
        }
        if (!isset($stats['users'][$userId]['byDay'][$day]) || !is_array($stats['users'][$userId]['byDay'][$day])) {
            $stats['users'][$userId]['byDay'][$day] = ['in' => 0, 'out' => 0];
        }
        $stats['users'][$userId]['totalInBytes'] = (int) ($stats['users'][$userId]['totalInBytes'] ?? 0) + $inBytes;
        $stats['users'][$userId]['totalOutBytes'] = (int) ($stats['users'][$userId]['totalOutBytes'] ?? 0) + $outBytes;
        $stats['users'][$userId]['byDay'][$day]['in'] = (int) ($stats['users'][$userId]['byDay'][$day]['in'] ?? 0) + $inBytes;
        $stats['users'][$userId]['byDay'][$day]['out'] = (int) ($stats['users'][$userId]['byDay'][$day]['out'] ?? 0) + $outBytes;
    }

    $stats['version'] = 1;
    $stats['updatedAt'] = time();
    grid_traffic_meter_prune_by_day($stats);

    $json = json_encode($stats, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
    if ($json === false) {
        @flock($fp, LOCK_UN);
        @fclose($fp);
        return;
    }
    ftruncate($fp, 0);
    rewind($fp);
    fwrite($fp, $json . "\n");
    fflush($fp);
    @flock($fp, LOCK_UN);
    @fclose($fp);
}

/**
 * @return array{global: array, user: ?array, note: string}
 */
function grid_traffic_meter_snapshot(?string $userId): array
{
    $stats = grid_traffic_meter_read_stats();
    $day = date('Y-m-d');
    $gDay = $stats['global']['byDay'][$day] ?? ['in' => 0, 'out' => 0];
    $global = [
        'totalInBytes' => (int) ($stats['global']['totalInBytes'] ?? 0),
        'totalOutBytes' => (int) ($stats['global']['totalOutBytes'] ?? 0),
        'todayInBytes' => (int) ($gDay['in'] ?? 0),
        'todayOutBytes' => (int) ($gDay['out'] ?? 0),
        'day' => $day,
    ];
    $user = null;
    if ($userId !== null && $userId !== '' && isset($stats['users'][$userId]) && is_array($stats['users'][$userId])) {
        $ub = $stats['users'][$userId];
        $uDay = $ub['byDay'][$day] ?? ['in' => 0, 'out' => 0];
        $user = [
            'totalInBytes' => (int) ($ub['totalInBytes'] ?? 0),
            'totalOutBytes' => (int) ($ub['totalOutBytes'] ?? 0),
            'todayInBytes' => (int) ($uDay['in'] ?? 0),
            'todayOutBytes' => (int) ($uDay['out'] ?? 0),
            'day' => $day,
        ];
    }

    return [
        'global' => $global,
        'user' => $user,
        'updatedAt' => isset($stats['updatedAt']) ? (int) $stats['updatedAt'] : null,
        'scopeNote' => 'flexia_http_api',
    ];
}

/**
 * @param array<string, mixed> $envPayload
 * @param array<string, mixed> $settings
 * @return list<array{type:string, level:string, message:string}>
 */
function grid_traffic_meter_alerts(array $envPayload, array $settings): array
{
    $alerts = [];
    $diskWarn = (float) ($settings['osDiskWarnPercent'] ?? 90);
    $disk = $envPayload['disk'] ?? null;
    if (is_array($disk) && isset($disk['usedPercent']) && (float) $disk['usedPercent'] >= $diskWarn) {
        $alerts[] = [
            'type' => 'disk',
            'level' => 'warn',
            'message' => 'Disco del proyecto al ' . round((float) $disk['usedPercent'], 1) . '%',
            'percent' => (float) $disk['usedPercent'],
        ];
    }
    $warnMb = (int) ($settings['osTrafficUserWarnMb'] ?? 0);
    if ($warnMb > 0) {
        $traffic = $envPayload['traffic'] ?? null;
        $user = is_array($traffic) ? ($traffic['user'] ?? null) : null;
        if (is_array($user)) {
            $todayOut = (int) ($user['todayOutBytes'] ?? 0);
            $limit = $warnMb * 1048576;
            if ($todayOut >= $limit) {
                $alerts[] = [
                    'type' => 'traffic_user',
                    'level' => 'warn',
                    'message' => 'Tráfico de hoy (usuario) supera el aviso configurado',
                    'todayOutBytes' => $todayOut,
                    'limitBytes' => $limit,
                ];
            }
        }
    }
    $srv = $envPayload['server'] ?? [];
    if (is_array($srv) && empty($srv['httpsActive']) && ($envPayload['environment'] ?? '') === 'hosting') {
        $alerts[] = [
            'type' => 'https',
            'level' => 'warn',
            'message' => 'Conexión sin HTTPS detectada en hosting',
        ];
    }

    return $alerts;
}

function grid_os_detect_https_active(): bool
{
    if (!empty($_SERVER['HTTPS']) && strtolower((string) $_SERVER['HTTPS']) !== 'off') {
        return true;
    }
    if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && strtolower((string) $_SERVER['HTTP_X_FORWARDED_PROTO']) === 'https') {
        return true;
    }
    if (!empty($_SERVER['HTTP_X_FORWARDED_SSL']) && strtolower((string) $_SERVER['HTTP_X_FORWARDED_SSL']) === 'on') {
        return true;
    }

    return false;
}
