<?php

declare(strict_types=1);

namespace CliqThemes\StoreLocator\Admin;

defined('ABSPATH') || exit;

use CliqThemes\StoreLocator\Support\DemoHero;
use CliqThemes\StoreLocator\Support\SettingsService;
use CliqThemes\UI\Data\DataProviderRegistry;
use CliqThemes\UI\WordPress\Integration;

/**
 * Builds and injects the admin SPA's data layer (window.CLIQTHEMES_UI): provider
 * records/columns/filters/forms, REST config, app flags, and the visual-builder
 * domain config. Also enqueues the store-locator admin extension bundle.
 *
 * Kept out of {@see \CliqThemes\StoreLocator\Plugin} so the plugin class stays a
 * thin composition root and this admin-bootstrapping concern can evolve on its
 * own.
 */
final class AdminDataLayer
{
    /**
     * @param array<string, array<string, mixed>> $providerForms
     */
    public function __construct(
        private Integration $integration,
        private DataProviderRegistry $providers,
        private array $providerForms,
        private string $pluginFile,
        private string $menuSlug
    ) {
    }

    public function inject(string $hook): void
    {
        if ($hook !== 'toplevel_page_' . $this->menuSlug) {
            return;
        }

        if (! function_exists('wp_add_inline_script')) {
            return;
        }

        // Required for the media (image/gallery) picker fields to open.
        if (function_exists('wp_enqueue_media')) {
            wp_enqueue_media();
        }

        // Provides window.wp.editor (TinyMCE) for the WYSIWYG description field.
        if (function_exists('wp_enqueue_editor')) {
            wp_enqueue_editor();
        }

        $payload = [
            'providers' => [],
        ];

        // Providers registered only for their REST routes, not shown as nav
        // items (driven by a bespoke screen instead).
        $restOnlyProviders = [];

        /**
         * Providers whose data the admin bundle reaches over REST but which get no
         * tab of their own.
         *
         * @param array<int, string> $restOnlyProviders provider identifiers
         */
        $restOnlyProviders = (array) apply_filters('cliq_sl_rest_only_providers', $restOnlyProviders);

        foreach ($this->providers->all() as $identifier => $provider) {
            if (in_array($identifier, $restOnlyProviders, true)) {
                continue;
            }

            // Records are loaded lazily by the SPA per active screen (RecordsView
            // fetches them via REST on mount), so we DON'T hydrate every provider's
            // records on page load — that fired a per-field query storm across all
            // ~7 providers on every admin page view. Inject an empty set; the
            // visible screen fetches its own page.
            $records = [];

            $form = $this->providerForms[$identifier] ?? null;
            $primaryKey = $form['primaryKey'] ?? 'id';
            // A provider may declare itself read-only (e.g. Leads): the SPA then
            // hides Add/Edit and opens records in a disabled view-only modal.
            $readOnly = ! empty($form['readOnly']);

            $payload['providers'][$identifier] = [
                'label' => $form['label'] ?? ucwords(str_replace(['-', '_'], ' ', (string) $identifier)),
                'columns' => $provider->columns(),
                'filters' => $provider->filters(),
                'records' => $records,
                'form' => $form,
                'readOnly' => $readOnly,
                'mutations' => [
                    'create' => ! $readOnly,
                    'update' => ! $readOnly,
                    'delete' => true,
                    'bulkDelete' => true,
                ],
                'primaryKey' => $primaryKey,
            ];
        }

        $payload['rest'] = [
            'namespace' => 'cliqthemes-ui/v1',
            'root' => function_exists('rest_url') ? esc_url_raw(rest_url()) : '',
            'endpoint' => function_exists('rest_url') ? esc_url_raw(rest_url('cliqthemes-ui/v1')) : '',
            'nonce' => function_exists('wp_create_nonce') ? wp_create_nonce('wp_rest') : '',
        ];

        $payload['app'] = [
            'name' => __('Store Locator', 'cliq-store-locator'),
            'poweredBy' => 'CliqThemes',
            'tagline' => __('Enterprise store management', 'cliq-store-locator'),
            'version' => defined('CLIQ_SL_VERSION') ? CLIQ_SL_VERSION : '1.0.0',
            'rootId' => $this->integration->rootId(),
            // Bespoke screens (rendered by cliq-ui) that consume the admin REST.
            // Screens rendered by cliq-ui against the admin REST. 'settings'
            // stays last whatever a feature inserts before it.
            'screens' => self::screens(),
            'adminNamespace' => 'cliq-store-locator/v1',
            // Follow the host multilingual plugin's CURRENT admin language (e.g.
            // Polylang's admin-bar flag / WPML's switcher) — the SPA scopes its
            // records to this automatically, so no separate language dropdown.
            'multilingual' => [
                'active' => \CliqThemes\StoreLocator\I18n\Multilingual::isActive(),
                'currentLang' => \CliqThemes\StoreLocator\I18n\Multilingual::current(),
            ],
            // What the admin bundle may show. A feature that the bundle can render
            // controls for declares itself here; anything absent stays hidden.
            'features' => apply_filters('cliq_sl_admin_features', []),
            'assets' => [
                'demoHero' => DemoHero::url(),
            ],
        ];

        /**
         * Filter the admin bundle's bootstrap payload.
         *
         * @param array<string, mixed> $payload
         * @param self                 $layer  the data layer assembling it
         */
        $payload = apply_filters('cliq_sl_admin_payload', $payload, $this);

        $inline = 'window.CLIQTHEMES_UI = ' . wp_json_encode($payload) . ';' .
            'window.CLIQTHEMES_UI_DATA = window.CLIQTHEMES_UI;';

        wp_add_inline_script(
            $this->integration->scriptHandle(),
            $inline,
            'before'
        );

        $this->enqueueEditionAdmin();
    }

    /**
     * Enqueues the store-locator admin extension bundle. It declares the
     * cliq-ui framework bundle as a dependency, so it evaluates AFTER cliq-ui
     * (which creates the window.cliqUI registry) but BEFORE cliq-ui's domReady
     * mount — registering the plugin's domain screens on the framework's
     * extension registry just in time for the app to render them.
     */
    /**
     * The admin screens this site has.
     *
     * @return array<int, string>
     */
    private static function screens(): array
    {
        /**
         * Filter the admin screens, in menu order.
         *
         * @param array<int, string> $screens
         */
        $screens = apply_filters('cliq_sl_admin_screens', ['dashboard']);
        $screens = array_values(array_filter(array_map('strval', (array) $screens)));
        $screens = array_values(array_diff($screens, ['settings']));
        $screens[] = 'settings';

        return $screens;
    }
    /** Enqueue this plugin's own administration bundle. */
    private function enqueueEditionAdmin(): void
    {
        if (! function_exists('wp_enqueue_script')) {
            return;
        }

        $scriptHandle = 'cliq-sl-locator-admin';
        $buildDir = plugin_dir_path($this->pluginFile) . 'resources/locator-admin/build';
        $buildUrl = plugins_url('resources/locator-admin/build', $this->pluginFile);
        $assetFile = $buildDir . '/index.asset.php';
        if (! file_exists($assetFile)) {
            return;
        }

        $asset = require $assetFile;
        $deps = array_values(array_unique(array_merge(
            $asset['dependencies'] ?? [],
            [$this->integration->scriptHandle()]
        )));
        wp_enqueue_script(
            $scriptHandle,
            $buildUrl . '/index.js',
            $deps,
            $asset['version'] ?? CLIQ_SL_VERSION,
            true
        );
        if (function_exists('wp_set_script_translations')) {
            wp_set_script_translations(
                $scriptHandle,
                'cliq-store-locator',
                plugin_dir_path($this->pluginFile) . 'languages'
            );
        }

        $styleFile = $buildDir . '/style-index.css';
        if (file_exists($styleFile) && function_exists('wp_enqueue_style')) {
            $styleDeps = [$this->integration->styleHandle()];
            if (file_exists($buildDir . '/index.css')) {
                $vendorHandle = $scriptHandle . '-vendor';
                wp_register_style($vendorHandle, $buildUrl . '/index.css', [], $asset['version'] ?? CLIQ_SL_VERSION);
                $styleDeps[] = $vendorHandle;
            }
            wp_enqueue_style(
                $scriptHandle,
                $buildUrl . '/style-index.css',
                $styleDeps,
                $asset['version'] ?? CLIQ_SL_VERSION
            );
        }
    }
}
