<?php
/**
 * Vite manifest helpers for standalone CSS entries (globals, iframe-tokens, …).
 *
 * CSS-only Vite inputs appear in manifest.json as:
 *   "src/styles/iframe-tokens.css": { "file": "iframe-tokens.HASH.css", … }
 * not as short keys with a css[] array (that shape is for JS entries).
 *
 * @package FleekDash
 */

declare(strict_types=1);

namespace FleekDash\Libs\Assets;

/**
 * @return string|null Hashed CSS filename (e.g. iframe-tokens.Abc.css)
 */
function resolveManifestEntryCssFile(?object $entry): ?string
{
    $file = null;

    if ($entry !== null) {
        $has_direct_file = !empty($entry->file)
            && is_string($entry->file)
            && str_ends_with($entry->file, '.css');

        if ($has_direct_file) {
            $file = $entry->file;
        } elseif (
            !empty($entry->css)
            && is_array($entry->css)
            && isset($entry->css[0])
            && is_string($entry->css[0])
        ) {
            $file = $entry->css[0];
        }
    }

    return $file;
}

function manifestEntryKeyMatchesViteCssSource(
    string $manifest_key,
    string $entry_name,
    string $source_relative_path
): bool {
    if ($manifest_key === $entry_name || $manifest_key === $source_relative_path) {
        return true;
    }

    $normalized_key = wp_normalize_path($manifest_key);
    $normalized_source = wp_normalize_path($source_relative_path);

    return str_ends_with($normalized_key, $normalized_source)
        || str_ends_with($normalized_key, basename($normalized_source));
}

/**
 * @return string|null Hashed CSS filename from direct manifest keys (entry name / source path)
 */
function findViteCssFilenameByManifestKeys(
    object $manifest_data,
    string $entry_name,
    string $source_relative_path,
): ?string {
    $keys = array($entry_name);
    if ($source_relative_path !== $entry_name) {
        $keys[] = $source_relative_path;
    }

    $file = null;
    foreach ($keys as $key) {
        if (!isset($manifest_data->{$key})) {
            continue;
        }
        $candidate = resolveManifestEntryCssFile($manifest_data->{$key});
        if ($candidate !== null) {
            $file = $candidate;
            break;
        }
    }

    return $file;
}

/**
 * @return string|null Hashed CSS filename from manifest keys matching the Vite source path
 */
function findViteCssFilenameBySourceKeyMatch(
    object $manifest_data,
    string $entry_name,
    string $source_relative_path,
): ?string {
    $file = null;

    foreach ($manifest_data as $manifest_key => $manifest_entry) {
        if (!is_string($manifest_key)) {
            continue;
        }
        if (!manifestEntryKeyMatchesViteCssSource($manifest_key, $entry_name, $source_relative_path)) {
            continue;
        }
        $candidate = resolveManifestEntryCssFile($manifest_entry);
        if ($candidate !== null) {
            $file = $candidate;
            break;
        }
    }

    return $file;
}

/**
 * @return string|null Hashed CSS filename from a JS entry css[] array containing the needle
 */
function findViteCssFilenameByCssArrayNeedle(object $manifest_data, string $filename_needle): ?string
{
    $file = null;

    foreach ($manifest_data as $manifest_entry) {
        if (empty($manifest_entry->css) || !is_array($manifest_entry->css)) {
            continue;
        }
        foreach ($manifest_entry->css as $css_file) {
            $matches_needle = is_string($css_file) && strpos($css_file, $filename_needle) !== false;
            if ($matches_needle) {
                $file = $css_file;
                break 2;
            }
        }
    }

    return $file;
}

/**
 * @return string|null Hashed CSS filename from any manifest entry file property containing the needle
 */
function findViteCssFilenameByFileNeedle(object $manifest_data, string $filename_needle): ?string
{
    $file = null;

    foreach ($manifest_data as $manifest_entry) {
        $candidate = resolveManifestEntryCssFile($manifest_entry);
        $matches_needle = $candidate !== null && strpos($candidate, $filename_needle) !== false;
        if ($matches_needle) {
            $file = $candidate;
            break;
        }
    }

    return $file;
}

/**
 * @return string|null Hashed CSS filename from manifest data
 */
function resolveViteCssFilenameFromManifest(
    object $manifest,
    string $entry_name,
    string $source_relative_path,
    string $filename_needle
): ?string {
    $result = null;

    if (isset($manifest->data) && is_object($manifest->data)) {
        $manifest_data = $manifest->data;
        $result = findViteCssFilenameByManifestKeys($manifest_data, $entry_name, $source_relative_path);

        if ($result === null) {
            $result = findViteCssFilenameBySourceKeyMatch($manifest_data, $entry_name, $source_relative_path);
        }
        if ($result === null) {
            $result = findViteCssFilenameByCssArrayNeedle($manifest_data, $filename_needle);
        }
        if ($result === null) {
            $result = findViteCssFilenameByFileNeedle($manifest_data, $filename_needle);
        }
    }

    return $result;
}

/**
 * Resolve CSS URL from production manifest.json only (bypasses vite-dev-server.json).
 *
 * @param string $manifest_dir Absolute path to the directory containing manifest.json
 *
 * @return string|null Public plugin URL to hashed CSS in assets/
 */
function resolveViteCssUrlFromProductionManifestFile(
    string $manifest_dir,
    string $entry_name,
    string $source_relative_path,
    string $filename_needle,
): ?string {
    $normalized_dir = wp_normalize_path(rtrim($manifest_dir, '/\\'));
    $manifest_path = $normalized_dir . '/' . MANIFEST_JSON_FILENAME;
    $resolved = null;

    $can_read_manifest = is_file($manifest_path) && is_readable($manifest_path);
    if ($can_read_manifest) {
        $decoded = wp_json_file_decode($manifest_path);
        if ($decoded) {
            $decoded = apply_filters('vite_for_wp__manifest_data', $decoded, $manifest_dir, $manifest_path);
            $wrapper = (object) array(
                'data' => $decoded,
                'dir' => $manifest_dir,
                'is_dev' => false,
            );
            $resolved = resolveViteCssUrlFromManifest(
                $wrapper,
                $entry_name,
                $source_relative_path,
                $filename_needle,
            );
        }
    }

    if ($resolved === null) {
        $resolved = resolveViteCssUrlFromGlobFallback($normalized_dir, $filename_needle);
    }

    return $resolved;
}

/**
 * @return string|null Public plugin URL to hashed CSS in assets/
 */
function resolveViteCssUrlFromManifest(
    object $manifest,
    string $entry_name,
    string $source_relative_path,
    string $filename_needle
): ?string {
    $file = resolveViteCssFilenameFromManifest(
        $manifest,
        $entry_name,
        $source_relative_path,
        $filename_needle,
    );

    $url = null;
    if ($file !== null) {
        $url = globalsStylesPublicUrl($file);
    }

    return $url;
}

/**
 * @return string|null Absolute disk path to built CSS in assets/
 */
function resolveViteCssDiskPathProduction(
    string $manifest_dir,
    string $entry_name,
    string $source_relative_path,
    string $filename_needle
): ?string {
    $normalized_dir = wp_normalize_path(rtrim($manifest_dir, '/\\'));
    $manifest_path = $normalized_dir . '/' . MANIFEST_JSON_FILENAME;
    $disk_path = null;

    $can_read_manifest = is_file($manifest_path) && is_readable($manifest_path);
    if ($can_read_manifest) {
        $decoded = wp_json_file_decode($manifest_path);
        if ($decoded) {
            $decoded = apply_filters('vite_for_wp__manifest_data', $decoded, $manifest_dir, $manifest_path);
            $wrapper = (object) array(
                'data' => $decoded,
                'dir' => $manifest_dir,
                'is_dev' => false,
            );
            $file = resolveViteCssFilenameFromManifest(
                $wrapper,
                $entry_name,
                $source_relative_path,
                $filename_needle,
            );
            if ($file !== null) {
                $candidate = $normalized_dir . '/' . $file;
                if (is_readable($candidate)) {
                    $disk_path = $candidate;
                }
            }
        }
    }

    if ($disk_path === null) {
        $disk_path = globViteCssFallbackPath($normalized_dir, $filename_needle);
    }

    return $disk_path;
}

/**
 * @return string|null Absolute disk path via glob when manifest lookup fails
 */
function globViteCssFallbackPath(string $manifest_dir, string $filename_needle): ?string
{
    $pattern = wp_normalize_path(rtrim($manifest_dir, '/\\') . '/' . $filename_needle . '*.css');
    $matches = glob($pattern);
    $disk_path = null;

    if ($matches !== false && $matches !== array()) {
        $candidate = wp_normalize_path($matches[0]);
        if (is_readable($candidate)) {
            $disk_path = $candidate;
        }
    }

    return $disk_path;
}

/**
 * @return string|null Public plugin URL via glob when manifest lookup fails
 */
function resolveViteCssUrlFromGlobFallback(string $manifest_dir, string $filename_needle): ?string
{
    $disk_path = globViteCssFallbackPath($manifest_dir, $filename_needle);
    $url = null;

    if ($disk_path !== null) {
        $url = globalsStylesPublicUrl(basename($disk_path));
    }

    return $url;
}
