<?php

namespace WPSN\Core;

defined('ABSPATH') || exit;

/**
 * Frontend Footprint Guard
 *
 * Goal: keep public-facing pages clean and fast.
 *
 * When enabled, this guard prevents WP SEO Next from enqueueing any frontend
 * JS/CSS except in explicit editor contexts (Frontend Inspector or supported
 * front-end page builders).
 */
final class FrontendFootprintGuard
{
    private static bool $initialized = false;

    private static ?Settings $settings = null;

    /** @var string[] */
    private static array $removedScripts = [];

    /** @var string[] */
    private static array $removedStyles = [];

    /** @var string[] */
    private static array $scriptsEnqueued = [];

    /** @var string[] */
    private static array $stylesEnqueued = [];

    /** @var string[] */
    private static array $allowedScripts = [];

    /** @var string[] */
    private static array $allowedStyles = [];

    private static string $requestUri = '';

    public static function init(Settings $settings): void
    {
        if (self::$initialized) {
            return;
        }
        self::$initialized = true;
        self::$settings = $settings;

        // Frontend only.
        if (is_admin()) {
            return;
        }

        // Run late so we can remove anything enqueued earlier.
        add_action('wp_enqueue_scripts', [self::class, 'guard'], 9999);
    }

    public static function guard(): void
    {
        self::$removedScripts = [];
        self::$removedStyles = [];
        self::$allowedScripts = [];
        self::$allowedStyles = [];
        self::$requestUri = isset($_SERVER['REQUEST_URI']) ? substr((string) wp_unslash($_SERVER['REQUEST_URI']), 0, 240) : '';

        if (!self::$settings) {
            return;
        }

        $enabled = (bool) self::$settings->get('performance.frontend.contract_enabled', true);

        // Always refresh snapshot.
        self::refreshEnqueued();

        if (!$enabled) {
            return;
        }

        self::buildAllowList();

        // Remove any WPSN assets that might have slipped in.
        foreach (self::$scriptsEnqueued as $h) {
            if (!in_array($h, self::$allowedScripts, true)) {
                wp_dequeue_script($h);
                wp_deregister_script($h);
                self::$removedScripts[] = $h;
            }
        }

        foreach (self::$stylesEnqueued as $h) {
            if (!in_array($h, self::$allowedStyles, true)) {
                wp_dequeue_style($h);
                wp_deregister_style($h);
                self::$removedStyles[] = $h;
            }
        }

        self::refreshEnqueued();
    }

    /**
     * @return array<string,mixed>
     */
    public static function snapshot(): array
    {
        return [
            'enabled' => self::$settings ? (bool) self::$settings->get('performance.frontend.contract_enabled', true) : false,
            'request_uri' => self::$requestUri,
            'scripts_enqueued' => self::$scriptsEnqueued,
            'styles_enqueued' => self::$stylesEnqueued,
            'allowed_scripts' => self::$allowedScripts,
            'allowed_styles' => self::$allowedStyles,
            'removed_scripts' => self::$removedScripts,
            'removed_styles' => self::$removedStyles,
        ];
    }

    private static function refreshEnqueued(): void
    {
        self::$scriptsEnqueued = self::collectWpsnScripts();
        self::$stylesEnqueued  = self::collectWpsnStyles();
    }

    private static function buildAllowList(): void
    {
        $allowScripts = [];
        $allowStyles  = [];

        // Frontend Inspector assets.
        if (self::allowInspectorAssets()) {
            $allowScripts[] = 'wpsn-frontend-inspector';
            $allowStyles[]  = 'wpsn-frontend-inspector';
        }

        // Universal front-end metabox (Divi/Beaver builder contexts).
        if (self::allowUniversalMetaboxAssets()) {
            $allowScripts[] = 'wpsn-universal-metabox';
            $allowStyles[]  = 'wpsn-universal-metabox';
        }

        $allowScripts = array_values(array_unique(array_filter($allowScripts, 'is_string')));
        $allowStyles  = array_values(array_unique(array_filter($allowStyles, 'is_string')));
        sort($allowScripts);
        sort($allowStyles);

        self::$allowedScripts = $allowScripts;
        self::$allowedStyles  = $allowStyles;
    }

    private static function allowInspectorAssets(): bool
    {
        if (!self::$settings) {
            return false;
        }

        $onDemand = (bool) self::$settings->get('performance.frontend.inspector_on_demand', true);
        if ($onDemand) {
            return isset($_GET['wpsn_inspector']) && (string) wp_unslash($_GET['wpsn_inspector']) === '1';
        }

        // If not on-demand, allow only for privileged logged-in users.
        if (function_exists('is_user_logged_in') && !is_user_logged_in()) {
            return false;
        }
        if (function_exists('current_user_can')) {
            return current_user_can('edit_posts') || current_user_can('manage_options');
        }
        return false;
    }

    private static function allowUniversalMetaboxAssets(): bool
    {
        // Divi Visual Builder / preview.
        if (isset($_GET['et_fb']) || isset($_GET['et_pb_preview'])) {
            return true;
        }

        // Beaver Builder.
        if (isset($_GET['fl_builder']) || isset($_GET['fl_builder_ui'])) {
            return true;
        }

        return false;
    }

    /**
     * @return string[]
     */
    private static function collectWpsnScripts(): array
    {
        global $wp_scripts;
        if (!isset($wp_scripts) || !is_object($wp_scripts) || !isset($wp_scripts->queue) || !is_array($wp_scripts->queue)) {
            return [];
        }
        return self::filterWpsnHandles($wp_scripts->queue);
    }

    /**
     * @return string[]
     */
    private static function collectWpsnStyles(): array
    {
        global $wp_styles;
        if (!isset($wp_styles) || !is_object($wp_styles) || !isset($wp_styles->queue) || !is_array($wp_styles->queue)) {
            return [];
        }
        return self::filterWpsnHandles($wp_styles->queue);
    }

    /**
     * @param array<int,mixed> $handles
     * @return string[]
     */
    private static function filterWpsnHandles(array $handles): array
    {
        $out = [];
        foreach ($handles as $h) {
            if (!is_string($h)) {
                continue;
            }
            // All our assets use the "wpsn-" prefix.
            if (strpos($h, 'wpsn-') === 0) {
                $out[] = $h;
            }
        }
        $out = array_values(array_unique($out));
        sort($out);
        return $out;
    }
}
