<?php

namespace WPSN\Core;

use WPSN\Core\Settings;

defined('ABSPATH') || exit;

/**
 * Best-effort fatal error tracker (plugin-scoped).
 *
 * Why:
 * - When users hit "There has been a critical error" they often can't copy the stack trace.
 * - WP emails can be missed.
 * - We want a local, privacy-friendly breadcrumb.
 *
 * Notes:
 * - Only records fatal-ish errors originating from this plugin directory.
 * - Stores a small ring buffer in wp_options (no DB schema).
 * - Never throws (shutdown handler must be ultra defensive).
 */
final class ErrorTracker
{
    private const OPTION = 'wpsn_fatal_log';
    private const MAX_ITEMS = 30;

    private static bool $enabled = true;
    private static string $pluginDir = '';
    private static string $pluginVersion = '';
    private static string $pluginBuild = '';

    public static function init(Settings $settings): void
    {
        // Allow disabling in wp-config.
        if (defined('WPSN_DISABLE_ERROR_TRACKER') && (bool) constant('WPSN_DISABLE_ERROR_TRACKER')) {
            self::$enabled = false;
            return;
        }

        // Best-effort on/off flag in settings (defaults to true).
        try {
            self::$enabled = (bool) $settings->get('diagnostics.error_tracker_enabled', true);
        } catch (\Throwable $e) {
            self::$enabled = true;
        }

        self::$pluginDir = defined('WPSN_PLUGIN_DIR') ? (string) WPSN_PLUGIN_DIR : '';
        self::$pluginVersion = defined('WPSN_VERSION') ? (string) WPSN_VERSION : '';
        self::$pluginBuild = defined('WPSN_BUILD') ? (string) WPSN_BUILD : '';

        // Avoid double registration.
        static $registered = false;
        if ($registered) {
            return;
        }
        $registered = true;

        register_shutdown_function([self::class, 'onShutdown']);
    }

    /**
     * @return array<int,array<string,mixed>>
     */
    public static function getLog(): array
    {
        $v = function_exists('get_option') ? get_option(self::OPTION, []) : [];
        return is_array($v) ? $v : [];
    }

    public static function clear(): void
    {
        if (function_exists('delete_option')) {
            delete_option(self::OPTION);
        }
    }

    public static function onShutdown(): void
    {
        if (!self::$enabled) {
            return;
        }

        // error_get_last() exists in PHP core.
        $err = error_get_last();
        if (!is_array($err) || empty($err['type'])) {
            return;
        }

        $type = (int) $err['type'];
        // Only fatal-ish types.
        $fatalTypes = [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR];
        if (!in_array($type, $fatalTypes, true)) {
            return;
        }

        $file = isset($err['file']) ? (string) $err['file'] : '';
        if ($file === '') {
            return;
        }

        // Only track errors originating from this plugin.
        $dir = self::$pluginDir;
        if ($dir !== '') {
            if (strpos($file, $dir) === false) {
                return;
            }
        } else {
            if (strpos($file, 'wp-seo-next') === false) {
                return;
            }
        }

        $msg = isset($err['message']) ? (string) $err['message'] : '';
        $line = isset($err['line']) ? (int) $err['line'] : 0;

        $item = [
            'at_utc' => gmdate('c'),
            'ts' => time(),
            'type' => $type,
            'message' => self::shorten($msg, 800),
            'file' => self::relativizePath($file),
            'line' => $line,
            'plugin_version' => self::$pluginVersion,
            'build' => self::$pluginBuild,
            'php' => PHP_VERSION,
        ];

        // Store ring buffer in wp_options (best-effort).
        if (!function_exists('get_option') || !function_exists('update_option')) {
            return;
        }

        try {
            $log = get_option(self::OPTION, []);
            $log = is_array($log) ? $log : [];

            array_unshift($log, $item);
            if (count($log) > self::MAX_ITEMS) {
                $log = array_slice($log, 0, self::MAX_ITEMS);
            }

            update_option(self::OPTION, $log, false);
        } catch (\Throwable $e) {
            // Never throw from shutdown.
        }
    }

    private static function relativizePath(string $abs): string
    {
        $abs = (string) $abs;
        if ($abs === '') {
            return '';
        }

        $base = defined('ABSPATH') ? (string) ABSPATH : '';
        if ($base !== '' && strpos($abs, $base) === 0) {
            return ltrim(substr($abs, strlen($base)), '/\\');
        }

        return $abs;
    }

    private static function shorten(string $s, int $max): string
    {
        $s = trim((string) $s);
        if ($s === '') {
            return '';
        }
        if (strlen($s) <= $max) {
            return $s;
        }
        return substr($s, 0, $max) . '…';
    }
}
