<?php
/**
 * Media Folder Native Integration
 *
 * Injects the FleekDash folder sidebar into the WordPress native Media Library modal
 * (wp.media) so that folders are visible when opening the media picker from Gutenberg
 * blocks, WooCommerce, Kadence, and other contexts.
 *
 * The same media_folder taxonomy is used; data stays in sync with FleekDash React Media.
 *
 * @package FleekDash\Media
 * @since   2.4.3
 */

namespace FleekDash\Media;

use FleekDash\Taxonomies\MediaFolderTaxonomy;
use FleekDash\Services\MediaFolderService;
use FleekDash\Services\MediaFolderUserColorsService;

defined('ABSPATH') || exit;

class MediaFolderNativeIntegration
{
    public const SCRIPT_HANDLE = 'fleekdash-media-folder-sidebar';

    public const STYLE_HANDLE = 'fleekdash-media-folder-sidebar-css';

    /** @var bool Prevents duplicate enqueue when wp_enqueue_media runs multiple times per request. */
    private static bool $assetsEnqueued = false;

    /**
     * Bootstrap: hooks for enqueue and AJAX filter.
     */
    public static function init(): void
    {
        \add_action('wp_enqueue_media', [self::class, 'enqueue_assets'], 20);
        \add_filter('ajax_query_attachments_args', [self::class, 'filter_attachments_by_folder'], 10, 1);
    }

    /**
     * Enqueue JS and CSS when wp.media is used.
     * Runs on wp_enqueue_media - so only when a page actually opens the media modal.
     */
    public static function enqueue_assets(): void // NOSONAR - WordPress uses snake_case for function names
    {
        if (self::$assetsEnqueued) {
            return;
        }

        if (!\current_user_can('upload_files')) {
            return;
        }

        if (!\taxonomy_exists(MediaFolderTaxonomy::TAXONOMY)) {
            return;
        }

        $service = new MediaFolderService();
        $stats   = $service->getFolderStats(null);
        if (empty($stats) || !isset($stats['folders']) || !\is_array($stats['folders'])) {
            return;
        }
        $folders = self::build_folder_tree($stats['folders']);

        $css_url = \FleekDash\Libs\Assets\getCssUrl('media-folder-sidebar');
        $js_url  = \FleekDash\Libs\Assets\getJsUrl('media-folder-sidebar');
        $ver_css = \FleekDash\Libs\Assets\getCssVersion('media-folder-sidebar');

        \wp_enqueue_style(
            self::STYLE_HANDLE,
            $css_url,
            ['media-views'],
            $ver_css
        );

        \wp_enqueue_script(
            self::SCRIPT_HANDLE,
            $js_url,
            ['media-views'],
            FLEEKDASH_VERSION,
            true
        );

        $rest_url = \rest_url('wp/v2/media_folder');
        $nonce    = \wp_create_nonce('wp_rest');

        $unsorted_count = isset($stats['unsorted']) ? (int) $stats['unsorted'] : 0;
        $total_media    = isset($stats['total']) ? (int) $stats['total'] : 0;

        $colors_service = new MediaFolderUserColorsService();
        $folder_colors  = $colors_service->getMapForCurrentUser();

        \wp_localize_script(self::SCRIPT_HANDLE, 'fleekdashMediaFolders', [
            'folders'    => $folders,
            'unsortedCount' => $unsorted_count,
            'totalMediaCount' => $total_media,
            'folderColors' => $folder_colors,
            'restUrl'    => $rest_url,
            'nonce'      => $nonce,
            'i18n'       => [
                'allMedia'    => \__('All Media', 'fleekdash'),
                'unsorted'    => \__('Unsorted', 'fleekdash'),
                'folders'     => \__('Folders', 'fleekdash'),
                'noFolders'   => \__('No folders yet', 'fleekdash'),
                'createInFleekDash' => \__('Create folders in FleekDash Media', 'fleekdash'),
            ],
        ]);

        self::$assetsEnqueued = true;
    }

    /**
     * Filter AJAX attachments query by media_folder taxonomy.
     * The JS sends media_folder (term id) or media_folder_unsorted (1) in the query.
     * WordPress strips non-whitelisted params, so we read from $_REQUEST['query'].
     *
     * @param array $query Query args for WP_Query.
     * @return array
     */
    public static function filter_attachments_by_folder(array $query): array // NOSONAR - WordPress uses snake_case
    {
        if (!\current_user_can('upload_files')) {
            return $query;
        }

        if (!isset($_REQUEST['query']) || !\is_array($_REQUEST['query'])) {
            return $query;
        }

        $request_query = \wp_unslash($_REQUEST['query']); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
        $folder_id     = isset($request_query['media_folder']) ? \absint($request_query['media_folder']) : 0;
        $unsorted      = !empty($request_query['media_folder_unsorted']);

        if ($folder_id > 0) {
            $tax_query   = isset($query['tax_query']) && \is_array($query['tax_query']) ? $query['tax_query'] : [];
            $tax_query[] = [
                'taxonomy' => MediaFolderTaxonomy::TAXONOMY,
                'field'    => 'term_id',
                'terms'    => $folder_id,
            ];
            $query['tax_query'] = $tax_query;
        } elseif ($unsorted) {
            $tax_query   = isset($query['tax_query']) && \is_array($query['tax_query']) ? $query['tax_query'] : [];
            $tax_query[] = [
                'taxonomy' => MediaFolderTaxonomy::TAXONOMY,
                'operator' => 'NOT EXISTS',
            ];
            $query['tax_query'] = $tax_query;
        }

        return $query;
    }

    /**
     * Build hierarchical folder tree from flat stats.
     *
     * @param array<int, array{id: int, name: string, parent: int, count: int, menu_order: int}> $flat
     * @return array<int, array>
     */
    private static function build_folder_tree(array $flat): array // NOSONAR - WordPress uses snake_case
    {
        $map = [];
        foreach ($flat as $f) {
            $map[(int) $f['id']] = [
                'id'         => (int) $f['id'],
                'name'       => (string) $f['name'],
                'parent'     => (int) $f['parent'],
                'count'      => (int) $f['count'],
                'menu_order' => (int) ($f['menu_order'] ?? 0),
                'children'   => [],
            ];
        }

        foreach ($map as $id => $node) {
            if ($node['parent'] !== 0 && isset($map[$node['parent']])) {
                $map[$node['parent']]['children'][] = &$map[$id];
            }
        }

        $roots = \array_filter($map, function ($n) {
            return $n['parent'] === 0;
        });

        $sortFn = function ($a, $b) {
            if ($a['menu_order'] !== $b['menu_order']) {
                return $a['menu_order'] <=> $b['menu_order'];
            }
            return $a['id'] <=> $b['id'];
        };

        $sortTree = function (array &$nodes) use (&$sortTree, $sortFn) {
            \usort($nodes, $sortFn);
            foreach ($nodes as &$n) {
                if (!empty($n['children'])) {
                    $sortTree($n['children']);
                }
            }
        };
        $sortTree($roots);

        return \array_values($roots);
    }
}
