<?php
/**
 * Copyright (C) 2026 Plugixa (https://plugixa.com)
 * License: GNU General Public License v2 or later
 * License URI: http://www.gnu.org/licenses/gpl-2.0.txt
 */

namespace PlugixaRecipe\Admin;

defined( 'ABSPATH' ) || exit;

/**
 * Assets Class - Handles React app assets (HMR dev / production build)
 */
class Assets {

    private string $plugin_path;
    private string $plugin_url;
    private string $version;

    public function __construct(string $plugin_path, string $plugin_url, string $version) {
        $this->plugin_path = $plugin_path;
        $this->plugin_url = $plugin_url;
        $this->version = $version;
    }

    public function enqueue_app_assets(): void {
        $hmr_file = $this->plugin_path . 'includes/admin/class-hmr.php';

        if (file_exists($hmr_file)) {
            require_once $hmr_file;
            $hmr = new HMR($this->plugin_path);
            $hmr->enqueue_assets();
        } else {
            $this->enqueue_prod_assets();
        }

        $this->enqueue_admin_fixes();
        $this->enqueue_bundled_fonts();
        $this->enqueue_wp_media_editor();
        $this->inject_config();
    }

    private function enqueue_wp_media_editor(): void {
        wp_enqueue_media();
        wp_enqueue_editor();
        wp_enqueue_style('editor-buttons');
    }

    private function enqueue_admin_fixes(): void {
        $css_file = $this->plugin_path . 'assets/backend/css/admin-fixes.css';

        if (file_exists($css_file)) {
            wp_enqueue_style(
                'plugixa-recipe-admin-fixes',
                $this->plugin_url . 'assets/backend/css/admin-fixes.css',
                [],
                $this->version
            );
        }
    }

    /**
     * Load the self-hosted font faces (assets/frontend/fonts/) so the View font
     * picker can preview families locally without contacting any external CDN.
     */
    private function enqueue_bundled_fonts(): void {
        $css_file = $this->plugin_path . 'assets/frontend/fonts/fonts.css';

        if (file_exists($css_file)) {
            wp_enqueue_style(
                'plugixa-recipe-fonts',
                $this->plugin_url . 'assets/frontend/fonts/fonts.css',
                [],
                $this->version
            );
        }
    }

    private function enqueue_prod_assets(): void {
        // The premium overlay ships its own admin bundle (built with the pro modules
        // registered into the @pro registry); when present it supersedes the free one.
        // pro/ is stripped from the free build, so the free ZIP falls back to
        // assets/backend/build automatically.
        $build_rel = is_dir($this->plugin_path . 'pro/assets/backend/build')
            ? 'pro/assets/backend/build/'
            : 'assets/backend/build/';

        $build_path = $this->plugin_path . $build_rel;
        $build_url  = $this->plugin_url . $build_rel;

        $manifest_file = $build_path . '.vite/manifest.json';
        $entry_file = 'js/main.js';
        $chunks = [];

        if (file_exists($manifest_file)) {
            $manifest = json_decode(file_get_contents($manifest_file), true);

            $entry_key = isset($manifest['src/main.tsx']) ? 'src/main.tsx' : 'src/main.jsx';

            // Read hashed entry filename from manifest
            if (isset($manifest[$entry_key]['file'])) {
                $entry_file = $manifest[$entry_key]['file'];
            }

            if (isset($manifest[$entry_key]['imports'])) {
                foreach ($manifest[$entry_key]['imports'] as $import) {
                    if (isset($manifest[$import]['file'])) {
                        $chunks[] = $manifest[$import]['file'];
                    }
                }
            }
        }

        add_action('admin_head', function() use ($build_url, $chunks) {
            foreach ($chunks as $chunk) {
                echo '<link rel="modulepreload" href="' . esc_url($build_url . $chunk) . '">' . "\n";
            }
        });

        // Admin app CSS (Tailwind pxr utilities). Vite emits it as a fixed
        // un-hashed name (cssCodeSplit:false) and the manifest entry does not
        // reference it, so it must be enqueued explicitly. The dev server
        // injects this CSS via JS, which is why HMR mode doesn't need it.
        $css_file = $build_path . 'css/main.css';
        if (file_exists($css_file)) {
            wp_enqueue_style(
                'plugixa-recipe-app',
                $build_url . 'css/main.css',
                [],
                (string) filemtime($css_file)
            );
        }

        // Use null version — the hashed filename provides cache busting
        wp_enqueue_script_module(
            'plugixa-recipe-app',
            $build_url . $entry_file,
            [],
            null
        );
    }

    private function get_initial_route(): string {
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only page routing, no state change.
        $page = isset($_GET['page']) ? sanitize_text_field(wp_unslash($_GET['page'])) : 'plugixa-recipe';

        $routes = [
            'plugixa-recipe'              => '/dashboard',
            'plugixa-recipe-recipes'      => '/recipes',
            'plugixa-recipe-categories'   => '/categories',
            'plugixa-recipe-cuisines'     => '/cuisines',
            'plugixa-recipe-reviews'      => '/reviews',
            'plugixa-recipe-collections'  => '/collections',
            'plugixa-recipe-views'        => '/views',
            'plugixa-recipe-shortcodes'   => '/shortcodes',
            'plugixa-recipe-settings'     => '/settings',
        ];

        return $routes[$page] ?? '/dashboard';
    }

    private function inject_config(): void {
        $current_user = wp_get_current_user();

        $config = [
            'initialRoute' => $this->get_initial_route(),
            'apiUrl'       => esc_url_raw(rest_url('plugixarecipe/v1/')),
            'adminUrl'     => esc_url(admin_url()),
            'pluginUrl'    => esc_url($this->plugin_url),
            'nonce'        => wp_create_nonce('wp_rest'),
            'userId'       => get_current_user_id(),
            'siteName'     => get_bloginfo('name'),
            'permissions'  => $this->get_user_permissions(),
            'settings'     => $this->get_plugin_settings(),
            'i18n'         => $this->get_translations(),
            'currentUser'  => [
                'id'           => $current_user->ID,
                'display_name' => $current_user->display_name,
                'email'        => $current_user->user_email,
                'login'        => $current_user->user_login,
            ],
            // Presence of the pro/ overlay is the single source of truth for "is this the
            // premium build". There is no runtime licence object: the free build ships no
            // pro UI to gate, so this flag is purely informational (it lets a screen skip
            // a query whose route does not exist in free).
            'isPro'        => is_dir(PLUGIXARECIPE_PATH . 'pro/'),
        ];

        add_action('admin_head', function () use ($config) {
            wp_print_inline_script_tag(
                'window.PlugixaRecipeConfig = ' . wp_json_encode($config) . ';'
            );
        });
    }

    private function get_user_permissions(): array {
        return [
            'manage_options'          => current_user_can('manage_options'),
            'plugixa_recipe_manage'   => current_user_can('plugixa_recipe_manage'),
            'plugixa_recipe_edit'     => current_user_can('plugixa_recipe_edit'),
            'plugixa_recipe_delete'   => current_user_can('plugixa_recipe_delete'),
            'plugixa_recipe_publish'  => current_user_can('plugixa_recipe_publish'),
            'plugixa_recipe_settings' => current_user_can('plugixa_recipe_settings'),
        ];
    }

    private function get_plugin_settings(): array {
        $defaults = [
            'items_per_page'     => 20,
            'default_status'     => 'draft',
            'default_servings'   => 4,
            'measurement_system' => 'metric',
            'show_nutrition'     => true,
            'show_reviews'       => true,
            'enable_print'       => true,
            'enable_favorites'   => true,
            'enable_shopping_lists' => true,
            'date_format'        => 'M j, Y',
            'excerpt_length'     => 150,
        ];

        return array_merge($defaults, (array) get_option('plugixarecipe_settings', []));
    }

    private function get_translations(): array {
        $strings = [
            'save'             => __('Save', 'plugixarecipe'),
            'cancel'           => __('Cancel', 'plugixarecipe'),
            'delete'           => __('Delete', 'plugixarecipe'),
            'edit'             => __('Edit', 'plugixarecipe'),
            'add'              => __('Add', 'plugixarecipe'),
            'search'           => __('Search', 'plugixarecipe'),
            'loading'          => __('Loading...', 'plugixarecipe'),
            'error'            => __('An error occurred', 'plugixarecipe'),
            'success'          => __('Success', 'plugixarecipe'),
            'confirm_delete'   => __('Are you sure you want to delete?', 'plugixarecipe'),
            'no_results'       => __('No results found', 'plugixarecipe'),
            'dashboard'        => __('Dashboard', 'plugixarecipe'),
            'recipes'          => __('Recipes', 'plugixarecipe'),
            'categories'       => __('Categories', 'plugixarecipe'),
            'cuisines'         => __('Cuisines', 'plugixarecipe'),
            'reviews'          => __('Reviews', 'plugixarecipe'),
            'collections'      => __('Collections', 'plugixarecipe'),
            'settings'         => __('Settings', 'plugixarecipe'),
            'add_recipe'       => __('Add Recipe', 'plugixarecipe'),
            'edit_recipe'      => __('Edit Recipe', 'plugixarecipe'),
            'ingredients'      => __('Ingredients', 'plugixarecipe'),
            'instructions'     => __('Instructions', 'plugixarecipe'),
            'nutrition'        => __('Nutrition', 'plugixarecipe'),
            'rating'           => __('Rating', 'plugixarecipe'),
            'status'           => __('Status', 'plugixarecipe'),
            'draft'            => __('Draft', 'plugixarecipe'),
            'published'        => __('Published', 'plugixarecipe'),
            'pending'          => __('Pending', 'plugixarecipe'),
            'archived'         => __('Archived', 'plugixarecipe'),
            'featured'         => __('Featured', 'plugixarecipe'),
            'views'            => __('Views', 'plugixarecipe'),
            'shortcodes'       => __('Shortcodes', 'plugixarecipe'),
        ];

        /**
         * Filter the admin i18n string map.
         *
         * Premium features live under pro/ and are stripped from the free build, so the
         * free build ships none of their labels. The keys the premium overlay adds here
         * match the `labelKey` values its React modules register into the proRegistry —
         * so in the free build the key is never registered (JS) and the string is never
         * emitted (PHP). Both halves vanish together.
         *
         * @param array<string,string> $strings
         */
        return apply_filters('plugixarecipe_admin_i18n_strings', $strings);
    }
}
