<?php

namespace SoftminalB2B\Vite;

class Vite
{
    private static string $devServerUrl = '';
    private static string $pluginDir = '';
    private static string $pluginUrl = '';
    private static string $version = '1.0.0';
    private static ?array $manifest = null;
    private static array $moduleHandles = [];
    private static bool $filterRegistered = false;

    /**
     * Point this helper at one plugin.
     *
     * Every plugin built from the scaffold bundles this file under the same
     * class name, so PHP loads exactly one copy and all of them share these
     * static properties. Whichever plugin configures last would otherwise own
     * them, and the others would silently resolve `.dev` and their manifest
     * against the wrong directory and enqueue nothing at all.
     *
     * Two consequences, both handled here:
     *  - Callers must configure() immediately before enqueueing, not once at
     *    load time, because another plugin may configure in between.
     *  - The manifest cache belongs to whichever plugin was configured, so it
     *    is dropped whenever the target directory changes.
     */
    public static function configure(array $config): void
    {
        $pluginDir = trailingslashit($config['pluginDir'] ?? '');

        if ($pluginDir !== self::$pluginDir) {
            self::$manifest = null;
        }

        self::$devServerUrl = rtrim($config['devServerUrl'] ?? 'http://localhost:5173', '/');
        self::$pluginDir    = $pluginDir;
        self::$pluginUrl    = trailingslashit($config['pluginUrl'] ?? '');
        self::$version      = $config['version'] ?? '1.0.0';
    }

    public static function isDev(): bool
    {
        return file_exists(self::$pluginDir . '.dev');
    }

    public static function enqueueAsset(string $handle, string $entry, array $deps = [], bool $inFooter = true): bool
    {
        if (self::isDev()) {
            self::enqueueViteClient();
            $deps[] = 'vite-client';
        }

        $url = self::resolveUrl($entry);
        if (!$url) {
            return false;
        }

        self::ensureFilter();
        wp_enqueue_script($handle, $url, $deps, self::isDev() ? null : self::$version, $inFooter);

        if (!in_array($handle, self::$moduleHandles, true)) {
            self::$moduleHandles[] = $handle;
        }

        if (!self::isDev()) {
            $manifest = self::getManifest();
            if (!empty($manifest[$entry]['css'])) {
                foreach ($manifest[$entry]['css'] as $i => $cssFile) {
                    $cssHandle = $handle . '-css' . ($i > 0 ? '-' . $i : '');
                    wp_enqueue_style($cssHandle, self::$pluginUrl . 'assets/' . $cssFile, [], self::$version);
                }
            }
        }

        return true;
    }

    public static function enqueueStyle(string $handle, string $entry, array $deps = []): bool
    {
        $url = self::resolveUrl($entry);
        if (!$url) {
            return false;
        }

        wp_enqueue_style($handle, $url, $deps, self::isDev() ? null : self::$version);
        return true;
    }

    public static function localize(string $handle, string $objectName, array $data): void
    {
        wp_localize_script($handle, $objectName, $data);
    }

    private static function enqueueViteClient(): void
    {
        $handle = 'vite-client';

        if (in_array($handle, self::$moduleHandles, true)) {
            return;
        }

        self::ensureFilter();
        // Dev-only HMR client, enqueued solely when the Vite dev server is
        // running; it is never part of a production build, so it carries no version.
        // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
        wp_enqueue_script($handle, self::$devServerUrl . '/@vite/client', [], null, true);
        self::$moduleHandles[] = $handle;
    }

    private static function resolveUrl(string $entry): ?string
    {
        if (self::isDev()) {
            return self::$devServerUrl . '/' . $entry;
        }

        $manifest = self::getManifest();
        if (empty($manifest) || !isset($manifest[$entry])) {
            return null;
        }

        return self::$pluginUrl . 'assets/' . $manifest[$entry]['file'];
    }

    private static function getManifest(): ?array
    {
        if (self::$manifest !== null) {
            return self::$manifest;
        }

        $path = self::$pluginDir . 'assets/.vite/manifest.json';
        if (!file_exists($path)) {
            $path = self::$pluginDir . 'assets/manifest.json';
        }

        if (!file_exists($path)) {
            return null;
        }

        self::$manifest = json_decode(file_get_contents($path), true);
        return self::$manifest ?: null;
    }

    private static function ensureFilter(): void
    {
        if (self::$filterRegistered) {
            return;
        }

        add_filter('script_loader_tag', [self::class, 'filterScriptModule'], 10, 2);
        self::$filterRegistered = true;
    }

    public static function filterScriptModule(string $tag, string $handle): string
    {
        if (in_array($handle, self::$moduleHandles, true)) {
            return str_replace('<script ', '<script type="module" ', $tag);
        }
        return $tag;
    }
}
