<?php

namespace GOSP\GravityOps\Core\SuiteCore;

use GOSP\GravityOps\Core\SuiteCore\Admin\AdminShell;
use GOSP\GravityOps\Core\SuiteCore\Admin\ReviewPrompter;
use GOSP\GravityOps\Core\SuiteCore\Admin\SuiteMenu;
use GOSP\GravityOps\Core\SuiteCore\Admin\SurveyPrompter;
use GOSP\GravityOps\Core\SuiteCore\Admin\TrustedLogin;
if (!defined('ABSPATH')) {
    exit;
}
/**
 * SuiteCore entry point for GravityOps shared functionality.
 */
class SuiteCore
{
    public const VERSION = '2.0.5';
    private const STATE_KEY = 'gravityops_suite_core_state';
    /**
     * Local singleton instance (per loaded copy).
     *
     * @var SuiteCore|null
     */
    private static ?SuiteCore $local_instance = null;
    /**
     * Holds config state.
     *
     * @var Config
     */
    private Config $config;
    /**
     * Holds suite catalog state.
     *
     * @var SuiteCatalog
     */
    private SuiteCatalog $catalog;
    /**
     * Holds admin shell state.
     *
     * @var AdminShell
     */
    private AdminShell $shell;
    /**
     * Holds suite menu state.
     *
     * @var SuiteMenu
     */
    private SuiteMenu $suite_menu;
    /**
     * Holds admin assets state.
     *
     * @var AdminAssetHelper
     */
    private AdminAssetHelper $admin_assets;
    /**
     * Holds trusted login state.
     *
     * @var \TrustedLogin|null
     */
    private ?TrustedLogin $trusted_login = null;
    /**
     * Shared suite survey helper.
     *
     * @var SurveyPrompter|null
     */
    private ?SurveyPrompter $survey_prompter = null;
    /**
     * Flag indicating whether the boot hook has been registered.
     *
     * @var bool
     */
    private bool $boot_hook_registered = false;
    /**
     * Flag indicating whether the admin shell has been booted.
     *
     * @var bool
     */
    private bool $did_boot = false;
    /**
     * Base URL for plugin assets.
     *
     * @var string
     */
    private string $assets_base_url = '';
    /**
     * Initialize the SuiteCore instance.
     */
    private function __construct()
    {
        $this->config = new Config();
        $this->catalog = new SuiteCatalog();
        $this->admin_assets = new AdminAssetHelper($this);
        $this->suite_menu = new SuiteMenu($this);
        $this->shell = new AdminShell($this);
    }
    /**
     * Register a plugin's assets with SuiteCore.
     *
     * @param array $args Registration metadata.
     *
     * @return object SuiteCore provider instance.
     */
    public static function register(array $args = [])
    {
        $instance = self::local_instance();
        $instance->register_plugin($args);
        $instance->register_boot_hook();
        $instance->maybe_set_provider($args);
        return self::instance();
    }
    /**
     * Return the negotiated SuiteCore provider instance.
     *
     * @return object SuiteCore provider instance.
     */
    public static function instance()
    {
        $state =& self::state();
        if (isset($state['provider']) && is_object($state['provider'])) {
            return $state['provider'];
        }
        $instance = self::local_instance();
        $state['provider'] = $instance;
        $state['provider_version'] = self::VERSION;
        if ($instance->assets_base_url) {
            $state['provider_assets_base_url'] = $instance->assets_base_url;
        }
        return $instance;
    }
    /**
     * Return the config state array shared across prefixed copies.
     *
     * @return array
     */
    public static function &config_state()
    {
        $state =& self::state();
        if (empty($state['config']) || !is_array($state['config'])) {
            $state['config'] = [];
        }
        return $state['config'];
    }
    /**
     * Retrieve the AdminShell instance.
     *
     * @return AdminShell The instance of the shell.
     */
    public function shell()
    {
        return $this->shell;
    }
    /**
     * Retrieve the suite menu instance.
     *
     * @return SuiteMenu The suite menu instance.
     */
    public function suite_menu()
    {
        return $this->suite_menu;
    }
    /**
     * Retrieve the suite catalog instance.
     *
     * @return SuiteCatalog The suite catalog instance.
     */
    public function catalog()
    {
        return $this->catalog;
    }
    /**
     * Retrieve the configuration instance.
     *
     * @return Config The configuration instance.
     */
    public function config()
    {
        return $this->config;
    }
    /**
     * Retrieve the admin asset helper instance.
     *
     * @return AdminAssetHelper The admin asset helper.
     */
    public function admin_assets()
    {
        return $this->admin_assets;
    }
    /**
     * Retrieve the TrustedLogin instance, creating it if needed.
     *
     * @return \TrustedLogin The trusted login helper.
     */
    public function trusted_login()
    {
        if (null === $this->trusted_login) {
            $this->trusted_login = new TrustedLogin($this);
        }
        return $this->trusted_login;
    }
    /**
     * Create a review prompter for the given plugin details.
     *
     * @param string $prefix               Plugin prefix used for option keys.
     * @param string $plugin_title         Human-readable plugin title.
     * @param mixed  $review_url_or_params Review URL or configuration parameters.
     * @param string $capability           Capability required to use the prompt.
     *
     * @return ReviewPrompter The configured review prompter.
     */
    public function review_prompter(string $prefix, string $plugin_title, $review_url_or_params, string $capability = 'gform_full_access')
    {
        return new ReviewPrompter($prefix, $plugin_title, $review_url_or_params, $capability);
    }
    /**
     * Return the shared suite survey prompter.
     *
     * Legacy plugin releases may still pass their former per-plugin survey
     * arguments. Register that metadata so staggered suite updates remain safe.
     *
     * @param mixed ...$legacy_args Legacy prefix, title, version, and plan resolver.
     *
     * @return SurveyPrompter The configured survey prompter.
     */
    public function survey_prompter(...$legacy_args)
    {
        if (count($legacy_args) >= 4) {
            $this->register_survey_product((string) $legacy_args[0], (string) $legacy_args[1], (string) $legacy_args[2], $legacy_args[3]);
        }
        if (null === $this->survey_prompter) {
            $this->survey_prompter = new SurveyPrompter($this);
        }
        return $this->survey_prompter;
    }
    /**
     * Register a product for inclusion in the suite-wide survey metadata.
     *
     * @param string $prefix         Stable plugin prefix.
     * @param string $plugin_title   Human-readable plugin title.
     * @param string $plugin_version Current plugin version.
     * @param mixed  $plan_resolver  Plan resolver or plan value.
     *
     * @return void
     */
    public function register_survey_product(string $prefix, string $plugin_title, string $plugin_version, $plan_resolver)
    {
        $state =& self::state();
        if (empty($state['survey_products']) || !is_array($state['survey_products'])) {
            $state['survey_products'] = [];
        }
        $state['survey_products'][$prefix] = ['title' => $plugin_title, 'version' => $plugin_version, 'plan_resolver' => $plan_resolver];
    }
    /**
     * Return products registered for the suite-wide survey.
     *
     * @return array<string,array<string,mixed>>
     */
    public function survey_products()
    {
        $state =& self::state();
        return isset($state['survey_products']) && is_array($state['survey_products']) ? $state['survey_products'] : [];
    }
    /**
     * Get the base URL used for SuiteCore assets.
     *
     * @return string The normalized base URL.
     */
    public function assets_base_url()
    {
        if ($this->assets_base_url) {
            return $this->assets_base_url;
        }
        $state = self::state();
        return isset($state['provider_assets_base_url']) && is_string($state['provider_assets_base_url']) ? $state['provider_assets_base_url'] : '';
    }
    /**
     * Boot the admin shell if this instance is the active provider.
     *
     * @return void
     */
    public function maybe_boot()
    {
        if ($this->did_boot) {
            return;
        }
        $state = self::state();
        if (isset($state['provider']) && $state['provider'] === $this) {
            $this->boot();
        }
    }
    /**
     * Boot the admin shell once.
     *
     * @return void
     */
    private function boot()
    {
        if ($this->did_boot) {
            return;
        }
        $this->shell->boot();
        if (is_admin()) {
            $this->survey_prompter()->init();
            add_action('wp_ajax_gops_dismiss_review', [ReviewPrompter::class, 'ajax_dismiss']);
        }
        $this->did_boot = true;
    }
    /**
     * Register plugin config overrides and asset URL.
     *
     * @param array $args Registration metadata.
     *
     * @return void
     */
    private function register_plugin(array $args): void
    {
        if (!empty($args['config']) && is_array($args['config'])) {
            $this->config->apply_overrides($args['config']);
        }
        if (!empty($args['assets_base_url']) && is_string($args['assets_base_url'])) {
            $this->assets_base_url = $this->normalize_base_url($args['assets_base_url']);
        }
    }
    /**
     * Set this instance as the provider if it has the best version.
     *
     * @param array $args Registration metadata.
     *
     * @return void
     */
    private function maybe_set_provider($args)
    {
        $state =& self::state();
        $current_version = isset($state['provider_version']) ? (string) $state['provider_version'] : '';
        $is_better = '' === $current_version || version_compare(self::VERSION, $current_version, '>');
        if (!$is_better) {
            return;
        }
        $state['provider'] = $this;
        $state['provider_version'] = self::VERSION;
        if (!empty($args['assets_base_url']) && is_string($args['assets_base_url'])) {
            $this->assets_base_url = $this->normalize_base_url($args['assets_base_url']);
            $state['provider_assets_base_url'] = $this->assets_base_url;
        } elseif ($this->assets_base_url) {
            $state['provider_assets_base_url'] = $this->assets_base_url;
        }
    }
    /**
     * Register the hook that triggers the admin shell boot.
     *
     * @return void
     */
    private function register_boot_hook()
    {
        if ($this->boot_hook_registered) {
            return;
        }
        $this->boot_hook_registered = true;
        $has_run_already = did_action('plugins_loaded') && !doing_action('plugins_loaded');
        if ($has_run_already) {
            $this->maybe_boot();
            return;
        }
        add_action('plugins_loaded', [$this, 'maybe_boot'], 20);
    }
    /**
     * Normalize a base URL by ensuring a trailing slash.
     *
     * @param string $url Base URL to normalize.
     *
     * @return string Normalized base URL.
     */
    private function normalize_base_url($url)
    {
        return trailingslashit($url);
    }
    /**
     * Retrieves the local instance of the SuiteCore class. If the instance
     * does not already exist, it initializes a new one.
     *
     * @return SuiteCore The single local instance of the SuiteCore class.
     */
    private static function local_instance()
    {
        if (null === self::$local_instance) {
            self::$local_instance = new self();
        }
        return self::$local_instance;
    }
    /**
     * Retrieve the shared state for SuiteCore provider negotiation.
     *
     * @return array Shared state array.
     */
    private static function &state()
    {
        if (empty($GLOBALS[self::STATE_KEY]) || !is_array($GLOBALS[self::STATE_KEY])) {
            $GLOBALS[self::STATE_KEY] = ['provider' => null, 'provider_version' => '', 'provider_assets_base_url' => '', 'config' => [], 'survey_products' => []];
        }
        return $GLOBALS[self::STATE_KEY];
    }
}