<?php
/**
 * Media Folder Taxonomy
 *
 * Registers a hierarchical `media_folder` taxonomy on the `attachment` post type
 * so that media items can be organized into folders.
 *
 * The taxonomy is exposed via the WordPress REST API automatically
 * at /wp-json/wp/v2/media_folder.
 *
 * @package FleekDash\Taxonomies
 * @since   2.3.2
 */

namespace FleekDash\Taxonomies;

defined('ABSPATH') || exit;

class MediaFolderTaxonomy
{
    public const TAXONOMY = 'media_folder';
    public const POST_TYPE = 'attachment';

    private const RECOUNT_OPTION = 'fleekdash_media_folder_recount_v1';

    /**
     * Bootstrap: hook into WordPress init.
     */
    public static function init(): void
    {
        \add_action('init', [self::class, 'register'], 5);
        \add_action('init', [self::class, 'registerTermMeta'], 8);
        \add_action('init', [self::class, 'maybeRecountTerms'], 20);
        \add_filter('rest_attachment_query', [self::class, 'filterUnsortedMedia'], 10, 2);
        \add_action('rest_api_init', [self::class, 'registerMediaFolderRestField']);
        \add_action('rest_api_init', [self::class, 'registerMenuOrderRestField']);
    }

    /**
     * Register term meta for folder order (menu_order).
     */
    public static function registerTermMeta(): void
    {
        if (!\taxonomy_exists(self::TAXONOMY)) {
            return;
        }
        \register_term_meta(self::TAXONOMY, 'menu_order', [
            'type'              => 'integer',
            'description'       => 'Order among siblings (0-based).',
            'single'            => true,
            'default'           => 0,
            'sanitize_callback' => 'absint',
            'show_in_rest'      => true,
            'auth_callback'     => function () {
                return \current_user_can('upload_files');
            },
        ]);
    }

    /**
     * Expose menu_order on media_folder term REST responses (for ordering in UI).
     */
    public static function registerMenuOrderRestField(): void
    {
        \register_rest_field(
            self::TAXONOMY,
            'menu_order',
            [
                'get_callback'    => function ($term) {
                    $id = \is_array($term) ? ($term['id'] ?? 0) : ($term->term_id ?? 0);
                    $value = \get_term_meta((int) $id, 'menu_order', true);
                    return $value !== '' && $value !== false ? (int) $value : 0;
                },
                'update_callback' => null,
                'schema'          => [
                    'description' => 'Order among siblings.',
                    'type'        => 'integer',
                    'context'     => ['view', 'edit', 'embed'],
                ],
            ]
        );
    }

    /**
     * Expose media_folder (term IDs) on attachment REST responses.
     * WordPress does not include taxonomy terms in attachment responses by default.
     */
    public static function registerMediaFolderRestField(): void
    {
        \register_rest_field(
            self::POST_TYPE,
            'media_folder',
            [
                'get_callback'    => [self::class, 'getMediaFolderTermIds'],
                'update_callback' => [self::class, 'updateMediaFolderTermIds'],
                'schema'         => [
                    'description' => 'Media folder term IDs (first is primary folder).',
                    'type'        => 'array',
                    'items'       => ['type' => 'integer'],
                    'context'     => ['view', 'edit', 'embed'],
                ],
            ]
        );
    }

    /**
     * @param array|\WP_Post $attachment Attachment data (array) or post object.
     * @return int[] Term IDs for media_folder taxonomy.
     */
    public static function getMediaFolderTermIds($attachment): array
    {
        $post_id = 0;
        if (\is_array($attachment) && isset($attachment['id'])) {
            $post_id = (int) $attachment['id'];
        } elseif (\is_object($attachment) && isset($attachment->ID)) {
            $post_id = (int) $attachment->ID;
        }
        if ($post_id < 1) {
            return [];
        }
        $terms = \wp_get_object_terms($post_id, self::TAXONOMY);
        if (\is_wp_error($terms) || ! \is_array($terms)) {
            return [];
        }
        $ids = [];
        foreach ($terms as $term) {
            if (isset($term->term_id)) {
                $ids[] = (int) $term->term_id;
            }
        }
        return $ids;
    }

    /**
     * @param int[]   $term_ids Term IDs to assign.
     * @param \WP_Post $post    Attachment post.
     */
    public static function updateMediaFolderTermIds($term_ids, $post): void
    {
        $post_id = $post->ID ?? 0;
        if ($post_id < 1) {
            return;
        }
        if (! \is_array($term_ids)) {
            $term_ids = [];
        }
        $term_ids = array_map('intval', array_filter($term_ids, 'is_numeric'));
        \wp_set_object_terms($post_id, $term_ids, self::TAXONOMY);
    }

    /**
     * When media_folder_unsorted=1 is passed, show only items not in any folder.
     *
     * @param array            $args    WP_Query arguments.
     * @param \WP_REST_Request $request REST request.
     * @return array
     */
    public static function filterUnsortedMedia($args, $request)
    {
        $unsorted = $request->get_param('media_folder_unsorted');
        if ($unsorted !== '1' && $unsorted !== 1 && $unsorted !== true) {
            return $args;
        }

        $tax_query   = isset($args['tax_query']) && is_array($args['tax_query']) ? $args['tax_query'] : [];
        $tax_query[] = [
            'taxonomy' => self::TAXONOMY,
            'operator' => 'NOT EXISTS',
        ];
        $args['tax_query'] = $tax_query;

        return $args;
    }

    /**
     * One-time recount of all media_folder term counts.
     *
     * Needed because the taxonomy was previously registered without
     * update_count_callback, so WordPress used the default post-count
     * logic which ignores attachments (post_status = 'inherit').
     */
    public static function maybeRecountTerms(): void
    {
        if (\get_option(self::RECOUNT_OPTION)) {
            return;
        }

        $terms = \get_terms([
            'taxonomy'   => self::TAXONOMY,
            'hide_empty' => false,
            'fields'     => 'ids',
        ]);

        if (!empty($terms) && !\is_wp_error($terms)) {
            \wp_update_term_count_now($terms, self::TAXONOMY);
        }

        \update_option(self::RECOUNT_OPTION, '1', true);
    }

    /**
     * Register the media_folder taxonomy.
     */
    public static function register(): void
    {
        if (\taxonomy_exists(self::TAXONOMY)) {
            return;
        }

        $labels = [
            'name'              => 'Media Folders',
            'singular_name'     => 'Media Folder',
            'search_items'      => 'Search Folders',
            'all_items'         => 'All Folders',
            'parent_item'       => 'Parent Folder',
            'parent_item_colon' => 'Parent Folder:',
            'edit_item'         => 'Edit Folder',
            'update_item'       => 'Update Folder',
            'add_new_item'      => 'Add New Folder',
            'new_item_name'     => 'New Folder Name',
            'menu_name'         => 'Folders',
        ];

        $args = [
            'labels'                => $labels,
            'hierarchical'          => true,
            'public'                => false,
            'show_ui'               => false,
            'show_admin_column'     => false,
            'show_in_nav_menus'     => false,
            'show_tagcloud'         => false,
            'show_in_rest'          => true,
            'rest_base'             => 'media_folder',
            'query_var'             => false,
            'rewrite'               => false,
            'update_count_callback' => '_update_generic_term_count',
            'capabilities'          => [
                'manage_terms' => 'upload_files',
                'edit_terms'   => 'upload_files',
                'delete_terms' => 'upload_files',
                'assign_terms' => 'upload_files',
            ],
        ];

        \register_taxonomy(self::TAXONOMY, self::POST_TYPE, $args);
    }
}
