<?php

declare(strict_types=1);

namespace Plugin\tnt_stabila_shop\src\Helper;

use JTL\Shop;

class ServerHealth
{
    public const MIN_GOOD_CACHE_SPEED = 1.5;
    /**
     * @return object{actualLoad: float, pastLoads: object{oneMin: float, fiveMin: float, fifteenMin: float},
     *     threads: int}
     */
    public static function getDataCPU(): object
    {
        $result = \sys_getloadavg();
        if ($result === false) {
            $result = [0.0, 0.0, 0.0];
        }

        $cpuThreads = \shell_exec('nproc');
        if (\is_numeric($cpuThreads) === false) {
            $cpuThreads = 1;
        }

        return (object)[
            'actualLoad' => \round($result[0] * 100, 2),
            'pastLoads' => (object)[
                'oneMin'     => \round($result[0] * 100, 2),
                'fiveMin'    => \round($result[1] * 100, 2),
                'fifteenMin' => \round($result[2] * 100, 2),
            ],
            'threads' => (int)$cpuThreads,
        ];
    }

    /**
     * @return object{total: float, used: float, free: float, shared: float, cached: float,
     *     available: float, usage: float}
     */
    public static function getDataMemory(): object
    {
        $freeOutput = \shell_exec('free');
        if (\is_string($freeOutput) === false) {
            return (object)[
                'total'     => 0.0,
                'used'      => 0.0,
                'free'      => 0.0,
                'shared'    => 0.0,
                'cached'    => 0.0,
                'available' => 0.0,
                'usage'     => 0.0,
            ];
        }
        $freeLines = \preg_split('/\R/', \trim($freeOutput));
        if (isset($freeLines[1]) === false) {
            return (object)[
                'total'     => 0.0,
                'used'      => 0.0,
                'free'      => 0.0,
                'shared'    => 0.0,
                'cached'    => 0.0,
                'available' => 0.0,
                'usage'     => 0.0,
            ];
        }
        $memoryColumns = \preg_split('/\s+/', \trim($freeLines[1]));
        if (\is_array($memoryColumns) === false || \count($memoryColumns) < 7) {
            return (object)[
                'total'     => 0.0,
                'used'      => 0.0,
                'free'      => 0.0,
                'shared'    => 0.0,
                'cached'    => 0.0,
                'available' => 0.0,
                'usage'     => 0.0,
            ];
        }
        $memoryTotal     = ((float)$memoryColumns[1]) / 1000000;
        $memoryUsed      = ((float)$memoryColumns[2]) / 1000000;
        $memoryFree      = ((float)$memoryColumns[3]) / 1000000;
        $memoryShared    = ((float)$memoryColumns[4]) / 1000000;
        $memoryCached    = ((float)$memoryColumns[5]) / 1000000;
        $memoryAvailable = ((float)$memoryColumns[6]) / 1000000;
        $memoryUsage     = $memoryTotal > 0.0 ? ($memoryUsed * 100) / $memoryTotal : 0.0;

        return (object)[
            'total'     => \round($memoryTotal, 2),
            'used'      => \round($memoryUsed, 2),
            'free'      => \round($memoryFree, 2),
            'shared'    => \round($memoryShared, 2),
            'cached'    => \round($memoryCached, 2),
            'available' => \round($memoryAvailable, 2),
            'usage'     => \round($memoryUsage, 2),
        ];
    }

    /**
     * @return object{total: float, used: float, free: float, usage: float}
     */
    public static function getDataDisk(): object
    {
        $diskFree  = \disk_free_space(\PFAD_ROOT) / 1000000000;
        $diskTotal = \disk_total_space(\PFAD_ROOT) / 1000000000;
        $diskUsed  = $diskTotal - $diskFree;
        $diskUsage = ($diskUsed * 100) / $diskTotal;

        return (object)[
            'total' => \round($diskTotal, 2),
            'used'  => \round($diskUsed, 2),
            'free'  => \round($diskFree, 2),
            'usage' => \round($diskUsage, 2),
        ];
    }

    /**
     * @return object{memoryTotal: float, memoryFree: float, cacheSize: float, cacheMemoryUsage: float,
     *     cachePerformance: float}|null
     */
    public static function getDataCache(): object|null
    {
        $cacheFilePath = \PFAD_ROOT . \PFAD_COMPILEDIR . 'cache.sqlite3';
        if (\file_exists($cacheFilePath) === false) {
            return null;
        }
        $cacheSize = \filesize($cacheFilePath);
        if ($cacheSize === false) {
            return null;
        }
        $cachePerformance = self::getCachePerformance();
        if ($cachePerformance === null) {
            return null;
        }
        $totalCacheMemory = PerformanceBooster::MMAP_SIZE / 1000000000;
        $freeCacheMemory  = $totalCacheMemory - ($cacheSize / 1000000000);

        return (object)[
            'memoryTotal'      => \round($totalCacheMemory, 2),
            'memoryFree'       => \round($freeCacheMemory, 2),
            'cacheSize'        => \round($cacheSize / 1000000, 2),
            'cacheMemoryUsage' => \round((100 - ($freeCacheMemory * 100) / $totalCacheMemory), 2),
            'cachePerformance' => \round($cachePerformance, 2),
        ];
    }

    private static function getCachePerformance(): float|null
    {
        $cache  = Shop::Container()->getCache();
        $method = $cache->getActiveMethod();
        if (
            $method->getJournalID() !== 'sqlite_journal'
            || $cache->isAvailable() === false
            || $cache->isActive() === false
        ) {
            return null;
        }

        $benchmark = $cache->benchmark(
            ['sqlite'],
            ['test1' => 'string number one', 'test2' => 'string number two', 'test3' => 333],
            10,
            100,
            false,
            true,
        );
        $readTime  = (float)\str_replace(',', '.', (string)$benchmark['timings']['get']);
        $writeTime = (float)\str_replace(',', '.', (string)$benchmark['timings']['set']);

        return (($readTime + $writeTime) * 0.5) * 1000; // Convert to milliseconds
    }
}
