<?php

// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedNamespaceFound -- APD is the established namespace shared with the Pro add-on and retained for backward compatibility.
namespace APD;

use APD\Admin\AdminServiceProvider;
use APD\Api\ApiServiceProvider;
use APD\Contracts\ServiceProviderInterface;
use APD\Demo\DemoServiceProvider;
use APD\Front\FrontServiceProvider;
use APD\Gutenberg\GutenbergServiceProvider;
use APD\Installation\UpgradeManager;
use APD\I18n\PluginTranslationManager;
use APD\Mail\MailServiceProvider;
use APD\Newsletter\NewsletterServiceProvider;
use APD\Operations\OperationsServiceProvider;
use APD\Repositories\ProductConfigRepository;
use APD\Support\Capabilities;
use APD\Support\DesignerAssets;
use APD\WooCommerce\WooServiceProvider;

defined('ABSPATH') || exit;

class Plugin
{
    protected Container $container;

    /** @var ServiceProviderInterface[] */
    protected array $providers = [];

    protected bool $asukaDesignerImportMapPrinted = false;

    public function __construct()
    {
        $this->container = new Container();
        $this->registerCore();
    }

    public function container() : Container
    {
        return $this->container;
    }

    public function boot() : void
    {
        $pluginTranslations = new PluginTranslationManager();
        add_filter('gettext', [$pluginTranslations, 'filterGettext'], 20, 3);
        add_filter('gettext_with_context', [$pluginTranslations, 'filterGettextWithContext'], 20, 4);
        add_filter('ngettext', [$pluginTranslations, 'filterNgettext'], 20, 5);
        add_filter('ngettext_with_context', [$pluginTranslations, 'filterNgettextWithContext'], 20, 6);
        add_action('init', [$this, 'ensureCapabilities'], 1);
        add_action('init', [UpgradeManager::class, 'maybeUpgrade'], 2);
        add_filter('script_loader_tag', [$this, 'filterScriptLoaderTag'], 10, 3);
        foreach ($this->providers as $provider) {
            $provider->register();
        }
        foreach ($this->providers as $provider) {
            $provider->boot();
        }
    }

    public function ensureCapabilities() : void
    {
        if (! is_admin()) {
            return;
        }

        Capabilities::syncAdministratorCaps();
        Capabilities::syncShopManagerCaps();
    }


    public function filterScriptLoaderTag(string $tag, string $handle, string $src) : string
    {
        $moduleHandles = [
            'apd-vendor',
            'apd-runtime-bridge',
            'apd-admin',
            'apd-front-app',
        ];

        if (! in_array($handle, $moduleHandles, true) && ! str_starts_with($handle, 'apd-addon-')) {
            return $tag;
        }

        $moduleTag = sprintf(
            // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript -- This filter only adds type="module" to a script already registered and enqueued by WordPress.
            '<script type="module" id="%1$s-js" src="%2$s"></script>',
            esc_attr($handle),
            esc_url($src)
        );

        if ($handle !== 'apd-vendor' || $this->asukaDesignerImportMapPrinted) {
            return $moduleTag;
        }

        $this->asukaDesignerImportMapPrinted = true;

        return $this->asukaDesignerImportMapTag($src) . "\n" . $moduleTag;
    }

    private function asukaDesignerImportMapTag(string $versionedCoreSrc) : string
    {
        $coreSrc = esc_url_raw($versionedCoreSrc);
        $unversionedCoreSrc = esc_url_raw(remove_query_arg('ver', $coreSrc));

        if ($coreSrc === '' || $unversionedCoreSrc === '') {
            return '';
        }

        $entries = DesignerAssets::importMapEntries();
        $entries[$unversionedCoreSrc] = $coreSrc;
        $imports = ['imports' => $entries];

        return sprintf(
            '<script type="importmap" id="apd-asuka-designer-importmap">%s</script>',
            wp_json_encode($imports, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)
        );
    }

    protected function registerCore() : void
    {
        $this->container->set(ProductConfigRepository::class, fn() => new ProductConfigRepository());

        $this->providers = [
            new AdminServiceProvider($this->container),
            new FrontServiceProvider($this->container),
            new ApiServiceProvider($this->container),
            new GutenbergServiceProvider($this->container),
            new WooServiceProvider($this->container),
            new OperationsServiceProvider($this->container),
        ];

        foreach ([MailServiceProvider::class, NewsletterServiceProvider::class, DemoServiceProvider::class] as $providerClass) {
            if (class_exists($providerClass)) {
                $this->providers[] = new $providerClass($this->container);
            }
        }
    }
}
