<?php
namespace NeoLibraryFull\NeoGlobal; if (!defined("ABSPATH")) { die("neoWP direct access not allowed"); }

















function is_performance_measure_on() { static $cache = null; $cache ??= isset($_GET["neoperformance"]); return $cache; }


\NeoLibraryFull\NeoGlobal\val("neo_global__performance", []);
\NeoLibraryFull\NeoGlobal\val("neo_global__performance_stack", []);
function performance_checkpoint_implementation($message, $caller_rel_path = null, $time = null, $is_start = false, $is_end = false) {
    if (!is_performance_measure_on()) { return; }
    $time ??= microtime(true);
    $caller_rel_path ??= get_caller_rel_path();
    static $pid = null; $pid ??= getmypid();
    $php_process_log_path = "/var/www/php-processes/php-process-pid-map_pid" . $pid . ".txt";

    $overwrite_second_line_in_php_process_log = function ($perf_message) use ($php_process_log_path) {
        if (ini_get("open_basedir")) { return; }
        if (!file_exists($php_process_log_path)) { return; }
        $fp = fopen($php_process_log_path, "r+");
        if ($fp === false) { return; }
        fgets($fp);
        fwrite($fp, $perf_message);
        ftruncate($fp, ftell($fp));
        fclose($fp);
    };
    if ($is_start) {

        $attribution_before = end(\NeoLibraryFull\NeoGlobal\val("neo_global__performance_stack")) ?: ["message" => "[Other code]", "caller_rel_path" => "[Other files]"];
        \NeoLibraryFull\NeoGlobal\val("neo_global__performance")[] = ["time" => $time, "message" => $attribution_before["message"], "path" => $attribution_before["caller_rel_path"], "suppress_count" => true];
        $overwrite_second_line_in_php_process_log("$caller_rel_path $message");
        \NeoLibraryFull\NeoGlobal\val("neo_global__performance_stack")[] = ["message" => $message, "caller_rel_path" => $caller_rel_path];
    } else if ($is_end) {

        $start_stack_checkpoint = array_pop(\NeoLibraryFull\NeoGlobal\val("neo_global__performance_stack"));
        if ($start_stack_checkpoint === null) { \NeoLibraryFull\NeoGlobal\throw_global_exception("performance_checkpoint error: End checkpoint '$message' without matching start checkpoint."); }
        if ($start_stack_checkpoint["message"] !== $message) { \NeoLibraryFull\NeoGlobal\throw_global_exception("performance_checkpoint error: End checkpoint '$message' does not match start checkpoint '" . $start_stack_checkpoint["message"] . "'."); }
        \NeoLibraryFull\NeoGlobal\val("neo_global__performance")[] = ["time" => $time, "message" => $start_stack_checkpoint["message"], "path" => $start_stack_checkpoint["caller_rel_path"]];

        if (!empty(\NeoLibraryFull\NeoGlobal\val("neo_global__performance_stack"))) {
            $last_performance_checkpoint_start = end(\NeoLibraryFull\NeoGlobal\val("neo_global__performance_stack"));
            $time_next_start = microtime(true); while ($time_next_start === $time) { $time_next_start = microtime(true); }
            \NeoLibraryFull\NeoGlobal\val("neo_global__performance")[] = ["time" => $time_next_start, "message" => $last_performance_checkpoint_start["message"], "path" => $last_performance_checkpoint_start["caller_rel_path"], "suppress_count" => true];
            $overwrite_second_line_in_php_process_log($last_performance_checkpoint_start["caller_rel_path"] . " " . $last_performance_checkpoint_start["message"]);
        } else {
            $overwrite_second_line_in_php_process_log("[Other files] [Other code]");
        }
    } else {
        \NeoLibraryFull\NeoGlobal\val("neo_global__performance")[] = ["time" => $time, "message" => $message, "path" => $caller_rel_path];
        $overwrite_second_line_in_php_process_log("$caller_rel_path $message");
    }
}


function get_caller_rel_path($force = false) {
    if (!is_performance_measure_on() && !$force) { return null; }
    $plugin_dir = dirname(__FILE__);
    $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);

    foreach ($backtrace as $frame) {
        if (!isset($frame["file"])) { continue; }
        if ($frame["file"] === __FILE__) { continue; }
        if (str_ends_with($frame["file"], "/_global--hook" . ".php"))                      { continue; }
        if (str_ends_with($frame["file"], "/_global--better-wp-functions" . ".php"))       { continue; }
        if (str_ends_with($frame["file"], "/neo-website--1-shortcode-with-echo" . ".php")) { continue; }
        if (str_ends_with($frame["file"], "/neo-website--9-hook-html-comments" . ".php"))  { continue; }
        if (str_starts_with($frame["file"], $plugin_dir . "/")) { return str_replace($plugin_dir . "/", "", $frame["file"]) . ":" . ($frame["line"] ?? 0); }
    }
    return "[Unknown]";
}


function build_table_string($data, $with_summary = false, $total_runtime = 0, $max_diff = 0, $path_width = 10) {
    $table = sprintf("%7s %7s %7s %-" . $path_width . "s %s", "START", "DIFF", "COUNT", "PATH", "MESSAGE") . "\n";
    foreach ($data as $row) {
        $path = (strlen($row["path"]) > $path_width) ? substr($row["path"], 0, $path_width) : $row["path"];
        $table .= sprintf("%7.3f %7.3f %7d %-" . $path_width . "s %s", $row["diff_start"], $row["diff"], $row["count"], $path, $row["message"]) . "\n";
    }
    if ($with_summary) { $table .= sprintf("TOTAL: %7.3f s, MAX DIFF: %7.3f s", $total_runtime, $max_diff) . "\n"; }
    return $table;
}
\NeoLibraryFull\NeoGlobal\performance_checkpoint("[Other code]", caller_rel_path: "[Other files]", time: \NeoLibraryFull\NeoEntrypoint\get_plugin_load_time(), is_start: true);
add_action("shutdown", function () {
    if (!is_performance_measure_on()) { return; }

    while (count(\NeoLibraryFull\NeoGlobal\val("neo_global__performance_stack")) > 1) {
        $start_stack_checkpoint = array_pop(\NeoLibraryFull\NeoGlobal\val("neo_global__performance_stack"));
        $time_next_end = microtime(true); while ($time_next_end === null || $time_next_end === end(\NeoLibraryFull\NeoGlobal\val("neo_global__performance"))["time"]) { $time_next_end = microtime(true); }
        \NeoLibraryFull\NeoGlobal\val("neo_global__performance")[] = ["time" => $time_next_end, "message" => $start_stack_checkpoint["message"], "path" => $start_stack_checkpoint["caller_rel_path"]];
    }

    \NeoLibraryFull\NeoGlobal\performance_checkpoint("[Other code]", caller_rel_path: "[Other files]", is_end: true);

    $performance_table = [];
    $max_path_length   = 0;
    $runtime_max       = 0.0;

    foreach (\NeoLibraryFull\NeoGlobal\val("neo_global__performance") as $i => $entry) {
        $diff_prev = 0.0; $diff_start = 0.0;
        if ($i !== 0) {
            $diff_prev  = round($entry["time"] - \NeoLibraryFull\NeoGlobal\val("neo_global__performance")[$i - 1]["time"], 5);
            $diff_start = round($entry["time"] - \NeoLibraryFull\NeoGlobal\val("neo_global__performance")[0]["time"], 5);
        }
        $performance_table[] = ["message" => $entry["message"], "path" => $entry["path"], "diff_prev" => $diff_prev, "diff_start" => $diff_start, "suppress_count" => $entry["suppress_count"] ?? false];
        $runtime_max = max($runtime_max, $diff_prev);
        $current_length = strlen($entry["path"]);
        if ($current_length > $max_path_length) { $max_path_length = $current_length; }
    }
    $path_width = min($max_path_length, (60));
    $total_runtime  = count($performance_table) ? end($performance_table)["diff_start"] : 0;
    $total_requests = count(\NeoLibraryFull\NeoGlobal\array_filter_better($performance_table, fn ($row) => !$row["suppress_count"]));


    $table_assoc = [];
    foreach ($performance_table as $row) {
        $key = $row["path"] . "\x00" . $row["message"];
        $table_assoc[$key] ??= ["path" => $row["path"], "message" => $row["message"], "diff_start" => $row["diff_start"], "diff" => 0, "count" => 0];
        if (!$row["suppress_count"]) { $table_assoc[$key]["count"] ++; }
        $table_assoc[$key]["diff"] += $row["diff_prev"];
        if ($row["diff_start"] < $table_assoc[$key]["diff_start"]) { $table_assoc[$key]["diff_start"] = $row["diff_start"]; }
    }
    process_aggregated_table_for_neoperformance_add($table_assoc);
    $table = array_values($table_assoc);
    usort($table, function ($a, $b) { return $b["diff"] <=> $a["diff"]; });

    $table_string = build_table_string($table, with_summary: false, total_runtime: 0, max_diff: 0, path_width: $path_width);
    $summary_string = "Total Runtime: " . $total_runtime . "s\nTotal Count: " . $total_requests . "\n";
    $table_string   = "### " . \NeoLibraryFull\NeoGlobal\plugin_slug() . " - Aggregated Performance Log ###" . "\n" . $table_string;
    $summary_string = "### " . \NeoLibraryFull\NeoGlobal\plugin_slug() . " - Performance Summary ###"        . "\n" . $summary_string;
    ?>
    <script type="module">
        console.log(<?php echo \NeoLibraryFull\NeoGlobal\php_to_js_object($table_string); ?>);
        console.log(<?php echo \NeoLibraryFull\NeoGlobal\php_to_js_object($summary_string); ?>);
    </script>
<?php }, PHP_INT_MAX);


function process_aggregated_table_for_neoperformance_add($aggregated_table) {
    if (!(\NeoLibraryFull\NeoGlobal\query_param("neoperformance") === "add")) { return; }
    \NeoLibraryFull\NeoGlobal\synclock_dir(\NeoLibraryFull\NeoGlobal\cache_path("neo-global--neoperformance"), function() use ($aggregated_table) {
        $filepath = \NeoLibraryFull\NeoGlobal\cache_path("neo-global--neoperformance") . "/neoperformance-add.txt";
        if (\NeoLibraryFull\NeoGlobal\fs_file_exists($filepath) && (\NeoLibraryFull\NeoGlobal\fs_filesize($filepath) > 50 * 1024 * 1024)) { \NeoLibraryFull\NeoGlobal\throw_global_exception("Performance aggregation file limit reached!"); }
        \NeoLibraryFull\NeoGlobal\fs_file_put_contents($filepath, \NeoLibraryFull\NeoGlobal\json_encode_better($aggregated_table) . "\n", FILE_APPEND);
    });
}


\NeoLibraryFull\NeoGlobal\add_action_hook("neo_init", function () {
    if (!(\NeoLibraryFull\NeoGlobal\query_param("neoperformance") === "sum")) { return; }
    $final_aggregated_table = [];
    \NeoLibraryFull\NeoGlobal\synclock_dir(\NeoLibraryFull\NeoGlobal\cache_path("neo-global--neoperformance"), function () use (&$final_aggregated_table) {

        $filepath = \NeoLibraryFull\NeoGlobal\cache_path("neo-global--neoperformance") . "/neoperformance-add.txt";
        $aggregated_table_lines = []; if (\NeoLibraryFull\NeoGlobal\fs_file_exists($filepath)) { $aggregated_table_lines = explode("\n", trim(\NeoLibraryFull\NeoGlobal\fs_file_get_contents($filepath))); }
        $aggregated_table_lines = \NeoLibraryFull\NeoGlobal\array_filter_better($aggregated_table_lines, fn ($line) => !empty(trim($line)));
        $aggregated_table_lines = array_map(fn ($line) => \NeoLibraryFull\NeoGlobal\json_decode_better($line), $aggregated_table_lines);

        $final_aggregated_table = [];
        foreach ($aggregated_table_lines as $table) {
            foreach ($table as $key => $entry) {
                $final_aggregated_table[$key] ??= ["path" => $entry["path"], "message" => $entry["message"], "diff_start" => $entry["diff_start"], "diff" => 0, "count" => 0];
                $final_aggregated_table[$key]["count"] += $entry["count"];
                $final_aggregated_table[$key]["diff"]  += $entry["diff"];
            }
        }

        usort($final_aggregated_table, function ($a, $b) { return $b["diff"] <=> $a["diff"]; });

        if (\NeoLibraryFull\NeoGlobal\fs_file_exists($filepath)) { \NeoLibraryFull\NeoGlobal\fs_unlink($filepath); }
    });

    ?><pre style="width: 100%; height: 100%; overflow: auto; white-space: pre-wrap; word-break: break-all;"><?php 
    echo build_table_string(array_values($final_aggregated_table), with_summary: false, path_width: 60);
    ?></pre><?php 
    exit;
});
