<?php

/**
 * Help Center Script loader.
 */

namespace Extendify\Shared;

defined('ABSPATH') || die('No direct access.');

use Extendify\Config;
use Extendify\PartnerData;
use Extendify\Shared\Controllers\UserSelectionController;
use Extendify\Shared\DataProvider\PartnerNotificationData;
use Extendify\Shared\DataProvider\ResourceData;
use Extendify\Shared\Services\AdminMenuList;
use Extendify\Shared\Services\ApexDomain\ApexDomain;
use Extendify\Shared\Services\Escaper;
use Extendify\Shared\Services\PluginDependencies\SimplyBook;
use Extendify\Shared\Services\PluginsActivation\Metricool as MetricoolActivation;
use Extendify\Shared\Services\PluginsActivation\SimplyBook as SimplyBookActivation;
use Extendify\SiteSettings;
use Extendify\Shared\Controllers\ImageGenerationController;
use Extendify\Shared\DataProvider\ProductsData;

/**
 * This class handles any file loading for the admin area.
 */

class Admin
{
    /**
     * Adds various actions to set up the page
     *
     * @return void
     */
    public function __construct()
    {
        \add_action('init', [$this, 'addExtraMetaFields']);
        \add_action('admin_enqueue_scripts', [$this, 'loadGlobalScripts']);
        \add_action('wp_enqueue_scripts', [$this, 'loadGlobalScripts']);
        \add_action('wp_ajax_search-install-plugins', [$this, 'recordPluginsSearchTerms'], -1);
        \add_action('rest_api_init', [$this, 'recordBlocksSearchTerms']);
        \add_action('wp_ajax_query-themes', [$this, 'recordThemesSearchTerms'], -1);
        AdminMenuList::init();
        \add_action('simplybook_activation', [SimplyBook::class, 'getIndustryCode'], 10, 0);
    }

    // phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh
    /**
     * Adds scripts to every page
     *
     * @return void
     */
    public function loadGlobalScripts()
    {
        \wp_enqueue_media();

        $this->updateUserMeta();

        $version = constant('EXTENDIFY_DEVMODE') ? uniqid() : Config::$version;

        /**
         * Enqueue shared JavaScript files if they exist in the asset manifest
         * Ensures proper loading order: vendors -> common
         */

        // Enqueue the vendor chunk first.
        if (isset(Config::$assetManifest['extendify-vendors.js'])) {
            wp_enqueue_script(
                'extendify-vendors',
                EXTENDIFY_BASE_URL . 'public/build/' . Config::$assetManifest['extendify-vendors.js'],
                [],
                $version,
                true
                // Load in footer.
            );
        }

        // Enqueue the common chunk next, dependent on vendors.
        if (isset(Config::$assetManifest['extendify-common.js'])) {
            wp_enqueue_script(
                'extendify-common',
                EXTENDIFY_BASE_URL . 'public/build/' . Config::$assetManifest['extendify-common.js'],
                ['extendify-vendors'],
                $version,
                true
            );
        }

        /*
        * Loads a unique runtime generated by Webpack to manage all the other generated scripts.
        * Without this runtime, the other Extendify scripts won't load.
        */
        $scriptAssetPath = EXTENDIFY_PATH . 'public/build/' . Config::$assetManifest['extendify-runtime.php'];
        $fallback = [
            'dependencies' => [],
            'version' => $version,
        ];
        $scriptAsset = file_exists($scriptAssetPath) ? require $scriptAssetPath : $fallback;

        foreach ($scriptAsset['dependencies'] as $style) {
            \wp_enqueue_style($style);
        }

        \wp_enqueue_script(
            Config::$slug . '-runtime-scripts',
            EXTENDIFY_BASE_URL . 'public/build/' . Config::$assetManifest['extendify-runtime.js'],
            $scriptAsset['dependencies'],
            $scriptAsset['version'],
            true
        );

        $scriptAssetPath = EXTENDIFY_PATH . 'public/build/' . Config::$assetManifest['extendify-shared.php'];
        $fallback = [
            'dependencies' => [],
            'version' => $version,
        ];
        $scriptAsset = file_exists($scriptAssetPath) ? require $scriptAssetPath : $fallback;

        foreach ($scriptAsset['dependencies'] as $style) {
            \wp_enqueue_style($style);
        }

        \wp_enqueue_script(
            Config::$slug . '-shared-scripts',
            EXTENDIFY_BASE_URL . 'public/build/' . Config::$assetManifest['extendify-shared.js'],
            $scriptAsset['dependencies'],
            $scriptAsset['version'],
            true
        );

        $partnerData = PartnerData::getPartnerData();
        $userConsent = get_user_meta(get_current_user_id(), 'extendify_ai_consent', true);
        $htmlAllowlist = [
            'a' => [
                'target' => [],
                'href' => [],
                'rel' => [],
            ],
        ];

        if (!function_exists('get_plugins')) {
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }

        $activePlugins = array_values(\get_option('active_plugins', []));
        $activePluginSlugs = array_map(function ($plugin) {
            return dirname($plugin);
        }, $activePlugins);
        $productActivationPlugins = PartnerData::setting('showProductActivation') ?? [];

        $productActivationPlugins = array_filter(
            $productActivationPlugins,
            function ($plugin) use ($activePluginSlugs) {
                return in_array($plugin['slug'], $activePluginSlugs);
            }
        );

        $productActivationPlugins = array_map(function ($plugin) {
            foreach ([MetricoolActivation::class, SimplyBookActivation::class] as $activation) {
                if ($plugin['slug'] === $activation::slug()) {
                    return array_merge($plugin, ['scriptData' => $activation::scriptData()]);
                }
            }

            return $plugin;
        }, $productActivationPlugins);

        // Visitors read the stamp and the alt text, so both resolve in the site's locale.
        $switchedLocale = \switch_to_locale(\get_locale());
        // translators: Short label stamped onto an image marking it as AI-generated.
        // Give the all-caps form your language uses.
        $aiImageLabel = \_x('AI GENERATED', 'uppercase', 'extendify-local');
        // translators: %s is the image description. Alt text prefix marking an image as AI-generated.
        $aiImageAltPattern = \__('AI Generated: %s', 'extendify-local');
        if ($switchedLocale) {
            \restore_previous_locale();
        }

        $extendifyCodeData = (array) PartnerData::setting('extendifyCodeData');

        // esc_url() strips the {DESCRIPTION} braces; shield the placeholder across it.
        $extendifyCodeLink = str_replace(
            '__EXTENDIFY_DESCRIPTION__',
            '{DESCRIPTION}',
            \esc_url_raw(str_replace(
                '{DESCRIPTION}',
                '__EXTENDIFY_DESCRIPTION__',
                (string) ($extendifyCodeData['link'] ?? '')
            ))
        );

        \wp_add_inline_script(
            Config::$slug . '-shared-scripts',
            'window.extSharedData = ' . \wp_json_encode([
                'root' => \esc_url_raw(rest_url(Config::$slug . '/' . Config::$apiVersion)),
                'homeUrl' => \esc_url_raw(\get_home_url()),
                'adminUrl' => \esc_url_raw(\admin_url()),
                'nonce' => \esc_attr(\wp_create_nonce('wp_rest')),
                'devbuild' => (bool) constant('EXTENDIFY_DEVMODE'),
                'assetPath' => \esc_url(EXTENDIFY_URL . 'public/assets'),
                'siteId' => \esc_attr(\get_option('extendify_site_id', '')),
                'siteCreatedAt' => \esc_attr(SiteSettings::getSiteCreatedAt()),
                'themeSlug' => \esc_attr(\get_option('stylesheet')),
                'version' => \esc_attr(Config::$version),
                'siteTitle' => \esc_attr(\get_bloginfo('name')),
                'siteProfile' => \get_option('extendify_site_profile', []),
                // Empty when the launch-time image fetch failed.
                'siteImages' => \get_option('extendify_site_images', []),
                'wpLanguage' => \esc_attr(\get_locale()),
                'aiImageLabel' => $aiImageLabel,
                'aiImageAltPattern' => $aiImageAltPattern,
                'wpVersion' => \esc_attr(\get_bloginfo('version')),
                'isBlockTheme' => function_exists('wp_is_block_theme') ? (bool) wp_is_block_theme() : false,
                'userId' => \esc_attr(\get_current_user_id()),
                // phpcs:ignore WordPress.Security.NonceVerification
                'userEmail' => isset($_GET['extendify-launch-success'])
                    ? \esc_attr(\wp_get_current_user()->user_email)
                    : null,
                'partnerLogo' => \esc_attr(PartnerData::$logo),
                'partnerId' => \esc_attr(PartnerData::$id),
                'partnerName' => \esc_attr(PartnerData::$name),
                'launchDataLegacy' => \wp_json_encode((UserSelectionController::get()->get_data() ?? [])),
                'resourceData' => \wp_json_encode((new ResourceData())->getData()),
                'partnerNotifications' => \wp_json_encode(PartnerNotificationData::get()),
                'notifications' => \get_user_meta(
                    \get_current_user_id(),
                    'extendify_notifications',
                    true
                ) ?: ['dismissed' => [], 'viewed' => []],
                'showAIConsent' => isset($partnerData['showAIConsent']) ? (bool) $partnerData['showAIConsent'] : false,
                'showChat' => (bool) (PartnerData::setting('showChat') || constant('EXTENDIFY_DEVMODE')),
                'useAgentOnboarding' => (bool) (
                    PartnerData::setting('useAgentOnboarding') ||
                    Config::preview('agent-onboarding') ||
                    constant('EXTENDIFY_DEVMODE')
                ),
                'showAIPageCreation' => (bool) (
                    PartnerData::setting('showAIPageCreation') || constant('EXTENDIFY_DEVMODE')
                ),
                'showAILogo' => (bool) PartnerData::setting('showAILogo'),
                'showImprint' => array_map('esc_attr', (array) PartnerData::setting('showImprint')),
                'showProductActivation' => array_values($productActivationPlugins),
                'consentTermsCustom' => \wp_kses((html_entity_decode(
                    ($partnerData['consentTermsCustom'] ?? ''),
                    ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401
                ) ?? ''), $htmlAllowlist),
                'userGaveConsent' => $userConsent ? (bool) $userConsent : false,
                'installedPlugins' => array_map('esc_attr', array_keys(\get_plugins())),
                'activePlugins' => $activePlugins,
                'installedPluginsSlugs' => array_values(array_filter(array_map(function ($p) {
                    return $p['TextDomain'] ?? '';
                }, \get_plugins()))),
                'frontPage' => \esc_attr(\get_option('page_on_front', 0)),
                'globalStylesPostID' => \esc_attr(\WP_Theme_JSON_Resolver::get_user_global_styles_post_id()),
                'showLocalizedCopy' => (bool) array_key_exists('showLocalizedCopy', $partnerData),
                'activity' => \wp_json_encode(\get_option('extendify_shared_activity', null)),
                'showDraft' => isset($partnerData['showDraft']) ? (bool) $partnerData['showDraft'] : false,
                'showLaunch' => Config::$showLaunch,
                'phpVersion' => \esc_attr(PHP_VERSION),
                'apexDomain' => PartnerData::setting('enableApexDomain')
                    ? rawurlencode(ApexDomain::getApexDomain(\get_home_url()))
                    : null,
                'launchCompletedAt' => \esc_attr(\get_option('extendify_onboarding_completed', false)),
                'showSiteQuestions' => (bool) (
                    PartnerData::setting('showLaunchQuestions') || Config::preview('launch-questions')
                ),
                'products' => ProductsData::get(),
                'showAIAgents' => (bool) (PartnerData::setting('showAIAgents') || Config::preview('ai-agent')),
                'showExtendifyCode' => (bool) PartnerData::setting('showExtendifyCode'),
                'extendifyCodeData' => [
                    'link' => $extendifyCodeLink,
                    'title' => $extendifyCodeData['title'] ?? '',
                    'message' => $extendifyCodeData['message'] ?? '',
                    'ctaPrimary' => $extendifyCodeData['cta-primary'] ?? '',
                ],
                'pluginGroupId' => Escaper::recursiveEscAttr(PartnerData::setting('pluginGroupId')),
                'adminPagesMenuList' => get_option('_transient_extendify_admin_pages_menu', []),
                'globalState' => ImageGenerationController::get()->get_data(),
            ]),
            'before'
        );

        \wp_set_script_translations('extendify-common', 'extendify-local', EXTENDIFY_PATH . 'languages/js');
        \wp_set_script_translations(
            Config::$slug . '-shared-scripts',
            'extendify-local',
            EXTENDIFY_PATH . 'languages/js'
        );

        \wp_enqueue_style(
            Config::$slug . '-shared-common-styles',
            EXTENDIFY_BASE_URL . 'public/build/' . Config::$assetManifest['extendify-shared.css'],
            [],
            Config::$version,
            'all'
        );
        $cssColorVars = PartnerData::cssVariableMapping();
        $cssString = implode('; ', array_map(function ($k, $v) {
            return "$k: $v";
        }, array_keys($cssColorVars), $cssColorVars));
        \wp_add_inline_style(
            Config::$slug . '-shared-common-styles',
            wp_strip_all_tags(":root { $cssString; }")
        );
    }

    /**
     * Adds additional meta fields to post types
     *
     * @return void
     */
    public function addExtraMetaFields()
    {
        // Add a tag to pages that were made with Launch.
        register_post_meta('page', 'made_with_extendify_launch', [
            'single' => true,
            'type' => 'boolean',
            'show_in_rest' => true,
        ]);
        register_post_meta('post', 'made_with_extendify_launch', [
            'single' => true,
            'type' => 'boolean',
            'show_in_rest' => true,
        ]);

        // Marks AI-generated images for EU AI Act disclosure.
        register_post_meta('attachment', 'extendify_ai_generated', [
            'single' => true,
            'type' => 'boolean',
            'show_in_rest' => true,
        ]);
    }

    /**
     * Records plugin search terms from the WordPress plugin search page
     * Stores terms in the 'extendify_plugin_search_terms' option
     *
     * @return void
     */
    public function recordPluginsSearchTerms()
    {
        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.NonceVerification.Missing
        $searchTerm = isset($_POST['s']) ? \sanitize_text_field(\wp_unslash(urldecode($_POST['s']))) : '';
        if (empty($searchTerm)) {
            return;
        }

        $searchTerms = \get_option('extendify_plugin_search_terms', []);
        $searchTerms[] = $searchTerm;
        $searchTerms = array_unique($searchTerms);

        \update_option('extendify_plugin_search_terms', $searchTerms);
    }

    /**
     * Records block search terms from the WordPress the editor's block search
     * Stores terms in the 'extendify_block_search_terms' option
     *
     * @return void
     */
    public function recordBlocksSearchTerms()
    {
        // Exits early if it is not a REST API request.
        if (!\wp_is_serving_rest_request()) {
            return;
        }

        // Exits early if it is not a GET request.
        if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] !== 'GET') {
            return;
        }

        $wp = $GLOBALS['wp'];

        $restRoute = ($wp->query_vars['rest_route'] ?? '');
        // Exits early if it's not the blocks search route.
        if ($restRoute !== '/wp/v2/block-directory/search') {
            return;
        }

        $searchTerm = \sanitize_text_field(\wp_unslash(($wp->query_vars['term'] ?? '')));
        // Exits early if the search term is empty or is not user input.
        if (empty($searchTerm) || str_starts_with($searchTerm, 'block:')) {
            return;
        }

        // Get current search term from the url and merge it with existing search terms.
        $searchTerms = \get_option('extendify_block_search_terms', []);
        \update_option('extendify_block_search_terms', array_merge($searchTerms, [$searchTerm]));
    }

    /**
     * Updates the user meta to disable the welcome guide from the Gutenberg editor
     * and close the pattern modal.
     *
     * @return void
     */
    public function updateUserMeta()
    {
        $currentPreferences = get_user_meta(get_current_user_id(), 'wp_persisted_preferences', true);
        if (!$currentPreferences) {
            $currentPreferences = [];
        }

        $postPreferences = array_key_exists('core/edit-post', $currentPreferences)
            ? $currentPreferences['core/edit-post']
            : [];
        $corePreferences = array_key_exists('core', $currentPreferences) ? $currentPreferences['core'] : [];

        $newPreferences = array_merge($currentPreferences, [
            'core/edit-post' => array_merge($postPreferences, ['welcomeGuide' => false]),
            'core' => array_merge($corePreferences, ['enableChoosePatternModal' => false]),
            '_modified' => wp_date('Y-m-d\TH:i:s.v\Z'),
        ]);

        update_user_meta(get_current_user_id(), 'wp_persisted_preferences', $newPreferences);
    }

    /**
     * Records search terms used when browsing themes in the admin interface.
     *
     * This method listens for the 'query-themes' AJAX action, extracts the search term
     * from the request, and stores it in the 'extendify_theme_search_terms' option.
     * Duplicate terms are filtered out.
     *
     * @return void
     */
    public function recordThemesSearchTerms()
    {
        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.NonceVerification.Missing
        $searchTerm = \sanitize_text_field(\wp_unslash(urldecode(($_POST['request']['search'] ?? ''))));
        if (empty($searchTerm)) {
            return;
        }

        $searchTerms = \get_option('extendify_theme_search_terms', []);
        $searchTerms[] = $searchTerm;
        $searchTerms = array_unique($searchTerms);

        \update_option('extendify_theme_search_terms', $searchTerms);
    }
}
