<?php

namespace DiviBooster\GalleryBooster\GalleryOrder;

// === Constants ===
const DIVI4_MODULE_SLUG = 'et_pb_gallery';
const DIVI5_MODULE_SLUG = 'divi/gallery';
const D4_FIELD_ORDERBY  = 'gallery_orderby';

const ORDER_REVERSE              = 'dbdb_reverse';
const ORDER_ALPHA                = 'dbdb_alphabetical';
const ORDER_ALPHA_REVERSE        = 'dbdb_alphabetical_reverse';
const ORDER_BY_ID                = 'dbdb_by_id';
const ORDER_BY_ID_REVERSE        = 'dbdb_by_id_reverse';

// Bootstrap hooks
if (function_exists('add_action')) {
    // Divi 4: extend fields
    add_filter('et_pb_all_fields_unprocessed_' . DIVI4_MODULE_SLUG, __NAMESPACE__ . '\\divi4_extend_gallery_fields');

    // Divi 4: apply sorting on frontend render
    add_filter('et_pb_module_shortcode_attributes', __NAMESPACE__ . '\\divi4_sort_gallery_ids', 10, 3);

    // Divi 4: apply sorting in Visual Builder computed property (AJAX)
    add_action('wp_ajax_et_pb_process_computed_property', __NAMESPACE__ . '\\divi4_apply_vb_preview_sort', 9);

    // Divi 5: add VB-only attribute options and description extension + ensure conversion
    add_action('wp_enqueue_scripts', __NAMESPACE__ . '\\enqueue_divi5_vb_script');

    // Divi 5: reorder galleryIds before render so both front-end and VB reflect order
    add_filter('render_block_data', __NAMESPACE__ . '\\divi5_reorder_gallery_ids_in_block', 10, 2);

    // Divi 5: PHP conversion outline registration (fallback to ensure mapping exists)
    add_filter('divi.moduleLibrary.conversion.moduleConversionOutline', __NAMESPACE__ . '\\divi5_register_conversion_outline', 10, 2);
}

// === Divi 4: Extend Image Order field options and description ===
function divi4_extend_gallery_fields($fields) {
    if (!is_array($fields)) {
        return $fields;
    }
    if (isset($fields[D4_FIELD_ORDERBY]['options']) && is_array($fields[D4_FIELD_ORDERBY]['options'])) {
        $fields[D4_FIELD_ORDERBY]['options'][ORDER_REVERSE]       = esc_html__('Reverse', 'divi-booster');
        $fields[D4_FIELD_ORDERBY]['options'][ORDER_ALPHA]         = esc_html__('Alphabetical', 'divi-booster');
        $fields[D4_FIELD_ORDERBY]['options'][ORDER_ALPHA_REVERSE] = esc_html__('Alphabetical (Reverse)', 'divi-booster');
        $fields[D4_FIELD_ORDERBY]['options'][ORDER_BY_ID]         = esc_html__('By ID', 'divi-booster');
        $fields[D4_FIELD_ORDERBY]['options'][ORDER_BY_ID_REVERSE] = esc_html__('By ID (Reverse)', 'divi-booster');
    }
    if (isset($fields[D4_FIELD_ORDERBY]['description'])) {
        $fields[D4_FIELD_ORDERBY]['description'] = $fields[D4_FIELD_ORDERBY]['description'] . ' ' . esc_html__('Additional ordering methods <a href="https://divibooster.com/sorting-the-divi-gallery-images/" target="_blank">added by Divi Booster</a>.', 'divi-booster');
    }
    return $fields;
}

// === Divi 4: Front-end sorting via shortcode attributes filter ===
function divi4_sort_gallery_ids($props, $attrs, $render_slug) {
    if ($render_slug !== DIVI4_MODULE_SLUG) {
        return $props;
    }
    // Skip sorting in Visual Builder to avoid interfering with VB render pipeline (handled via AJAX hook)
    if (isset($_GET['et_fb'])) {
        return $props;
    }
    if (empty($props['gallery_ids']) || empty($props['gallery_orderby'])) {
        return $props;
    }

    $ids = normalize_id_list_to_array($props['gallery_ids']);
    if (empty($ids)) {
        return $props;
    }

    $order = (string) $props['gallery_orderby'];
    $sorted = apply_sorting($ids, $order);
    if (!empty($sorted)) {
        $props['gallery_ids'] = implode(',', $sorted);
    }

    return $props;
}

// === Divi 4: VB preview sorting in computed property processing ===
function divi4_apply_vb_preview_sort() {
    if (empty($_POST['module_type']) || $_POST['module_type'] !== DIVI4_MODULE_SLUG) {
        return;
    }
    if (empty($_POST['depends_on']) || !is_array($_POST['depends_on'])) {
        return;
    }
    if (empty($_POST['depends_on']['gallery_ids']) || empty($_POST['depends_on']['gallery_orderby'])) {
        return;
    }

    $ids = normalize_id_list_to_array($_POST['depends_on']['gallery_ids']);
    if (empty($ids)) {
        return;
    }

    $order = (string) $_POST['depends_on']['gallery_orderby'];
    $sorted = apply_sorting($ids, $order);
    if (!empty($sorted)) {
        $_POST['depends_on']['gallery_ids'] = implode(',', $sorted);
    }
}

// === Divi 5: Enqueue Visual Builder JS to extend options and description, and ensure conversion mapping ===
function enqueue_divi5_vb_script() {
    if ((!function_exists('et_builder_d5_enabled') || !\et_builder_d5_enabled()) ||
        (!function_exists('et_core_is_fb_enabled') || !\et_core_is_fb_enabled())) {
        return;
    }
    $handle = sanitize_title('divi-booster-' . DIVI5_MODULE_SLUG . '-gallery-order');
    \wp_register_script($handle, '', array('lodash', 'divi-vendor-wp-hooks'), null, true);
    \wp_enqueue_script($handle);
    \wp_add_inline_script($handle, get_divi5_vb_inline_js());
}

function get_divi5_vb_inline_js() {
    $d5slug = wp_json_encode(DIVI5_MODULE_SLUG);
    $d4slug = wp_json_encode(DIVI4_MODULE_SLUG);
    $opt_reverse       = wp_json_encode(ORDER_REVERSE);
    $opt_alpha         = wp_json_encode(ORDER_ALPHA);
    $opt_alpha_reverse = wp_json_encode(ORDER_ALPHA_REVERSE);
    $opt_by_id         = wp_json_encode(ORDER_BY_ID);
    $opt_by_id_reverse = wp_json_encode(ORDER_BY_ID_REVERSE);
    $desc_append = wp_json_encode(__('Additional ordering methods <a href="https://divibooster.com/sorting-the-divi-gallery-images/" target="_blank">added by Divi Booster</a>.', 'divi-booster'));
    return <<<JS
(function(){
  var hooks = window.vendor && window.vendor.wp && window.vendor.wp.hooks ? window.vendor.wp.hooks : null;
  if (!hooks) return;

    // Extend Image Order options and description in module attributes
    hooks.addFilter('divi.moduleLibrary.moduleAttributes.divi.gallery', 'divi', function(attributes){
        try {
            var item = attributes
                && attributes.image
                && attributes.image.settings
                && attributes.image.settings.advanced
                && attributes.image.settings.advanced.galleryOrderby
                && attributes.image.settings.advanced.galleryOrderby.item;
            if (item && item.component && item.component.props) {
                var opts = item.component.props.options || {};
                opts[{$opt_reverse}]       = { label: 'Reverse' };
                opts[{$opt_alpha}]         = { label: 'Alphabetical' };
                opts[{$opt_alpha_reverse}] = { label: 'Alphabetical (Reverse)' };
                opts[{$opt_by_id}]         = { label: 'By ID' };
                opts[{$opt_by_id_reverse}] = { label: 'By ID (Reverse)' };
                item.component.props.options = opts;
            }
            if (item && typeof item.description === 'string') {
                item.description = item.description + ' ' + {$desc_append};
            }
        } catch (e) {}
        return attributes;
    });

  // Ensure conversion mapping exists from D4 field to D5 attr
  hooks.addFilter('divi.moduleLibrary.conversion.moduleConversionOutline', 'divi', function(outline, name){
    if (name !== {$d4slug}) return outline;
    if (!outline.module) outline.module = {};
    if (!outline.module.gallery_orderby) {
      outline.module.gallery_orderby = 'image.advanced.galleryOrderby.*';
    }
    return outline;
  });
})();
JS;
}

// === Divi 5: Reorder galleryIds within block attrs before render ===
function divi5_reorder_gallery_ids_in_block($parsed_block, $source_block = null) {
    $name = isset($parsed_block['blockName']) ? $parsed_block['blockName'] : '';
    if ($name !== DIVI5_MODULE_SLUG) {
        return $parsed_block;
    }
    $attrs = isset($parsed_block['attrs']) && is_array($parsed_block['attrs']) ? $parsed_block['attrs'] : array();

    // Determine order selection
    $order = divi5_get_gallery_orderby_from_attrs($attrs);
    if (!in_array($order, array(ORDER_REVERSE, ORDER_ALPHA, ORDER_ALPHA_REVERSE, ORDER_BY_ID, ORDER_BY_ID_REVERSE), true)) {
        return $parsed_block; // Default or Random: do nothing
    }

    // Extract current gallery IDs
    $ids_info = divi5_get_gallery_ids_from_attrs($attrs);
    $ids = $ids_info['ids'];
    if (empty($ids)) {
        return $parsed_block;
    }

    // Apply sorting
    $sorted = apply_sorting($ids, $order);

    // Write back into attrs preserving original type
    $parsed_block['attrs'] = divi5_set_gallery_ids_into_attrs($attrs, $sorted, $ids_info);
    return $parsed_block;
}

function divi5_register_conversion_outline($conversion_outline, $module_name) {
    if ($module_name !== DIVI5_MODULE_SLUG) {
        return $conversion_outline;
    }
    if (!isset($conversion_outline['module']) || !is_array($conversion_outline['module'])) {
        $conversion_outline['module'] = array();
    }
    // Ensure mapping exists (keeps D4 -> D5 value transfer for our custom order values)
    $conversion_outline['module']['gallery_orderby'] = 'image.advanced.galleryOrderby.*';
    return $conversion_outline;
}

// === Shared Utilities ===

// Normalize a comma-separated id list (string) into an array of unique positive ints, preserving original order
function normalize_id_list_to_array($ids_string) {
    if (!is_string($ids_string)) {
        return array();
    }
    $parts = array_map('trim', explode(',', $ids_string));
    $seen = array();
    $result = array();
    foreach ($parts as $p) {
        if ($p === '') continue;
        $n = absint($p);
        if ($n > 0 && !isset($seen[$n])) {
            $seen[$n] = true;
            $result[] = $n;
        }
    }
    return $result;
}

// Fetch titles for given attachment IDs in a single query; returns [id => title]
function fetch_attachment_titles_map(array $ids) {
    $ids = array_values(array_unique(array_map('absint', $ids)));
    $map = array();
    if (empty($ids)) return $map;

    $posts = get_posts(array(
        'post_type'      => 'attachment',
        'post_status'    => 'inherit',
        'post__in'       => $ids,
        'posts_per_page' => -1,
        'orderby'        => 'post__in',
        'fields'         => 'ids',
    ));
    foreach ($ids as $id) {
        // get_the_title uses cache; safe to call even if not in $posts due to permissions
        $map[$id] = (string) get_the_title($id);
    }
    return $map;
}

// Core sorting logic used by both D4 and D5
function apply_sorting(array $ids, $order) {
    $order = (string) $order;
    if (empty($ids)) return $ids;

    if ($order === ORDER_REVERSE) {
        return array_values(array_reverse($ids));
    }

    if ($order === ORDER_BY_ID) {
        $sorted = $ids;
        sort($sorted, SORT_NUMERIC);
        return array_values($sorted);
    }

    if ($order === ORDER_BY_ID_REVERSE) {
        $sorted = $ids;
        rsort($sorted, SORT_NUMERIC);
        return array_values($sorted);
    }

    if ($order === ORDER_ALPHA || $order === ORDER_ALPHA_REVERSE) {
        $titles = fetch_attachment_titles_map($ids);
        // stable sort: preserve original order for ties
        $index_map = array();
        foreach ($ids as $i => $id) { $index_map[$id] = $i; }
        $sorted = $ids;
        usort($sorted, function($a, $b) use ($titles, $index_map, $order) {
            $ta = isset($titles[$a]) ? $titles[$a] : '';
            $tb = isset($titles[$b]) ? $titles[$b] : '';
            // case-insensitive compare
            $ca = function_exists('mb_strtolower') ? mb_strtolower($ta) : strtolower($ta);
            $cb = function_exists('mb_strtolower') ? mb_strtolower($tb) : strtolower($tb);
            if ($ca === $cb) {
                // stable fallback to original selection order
                return ($index_map[$a] < $index_map[$b]) ? -1 : (($index_map[$a] > $index_map[$b]) ? 1 : 0);
            }
            $cmp = ($ca < $cb) ? -1 : 1;
            if ($order === ORDER_ALPHA_REVERSE) $cmp = -$cmp;
            return $cmp;
        });
        return array_values($sorted);
    }

    // default / unsupported: return original
    return $ids;
}

// === Divi 5 helpers to read/write attrs ===
function divi5_get_gallery_orderby_from_attrs($attrs) {
    // image.advanced.galleryOrderby can be nested as [desktop][value] or [value] or raw string
    if (isset($attrs['image']['advanced']['galleryOrderby']['desktop']['value'])) {
        return (string) $attrs['image']['advanced']['galleryOrderby']['desktop']['value'];
    }
    if (isset($attrs['image']['advanced']['galleryOrderby']['value'])) {
        return (string) $attrs['image']['advanced']['galleryOrderby']['value'];
    }
    if (isset($attrs['image']['advanced']['galleryOrderby']) && is_string($attrs['image']['advanced']['galleryOrderby'])) {
        return (string) $attrs['image']['advanced']['galleryOrderby'];
    }
    return '';
}

function divi5_get_gallery_ids_from_attrs($attrs) {
    $result = array(
        'ids'        => array(),
        'format'     => 'array', // 'array' | 'string'
        'path'       => 'desktop', // 'desktop' | 'flat'
    );
    // desktop array form
    if (isset($attrs['image']['advanced']['galleryIds']['desktop']['value'])) {
        $val = $attrs['image']['advanced']['galleryIds']['desktop']['value'];
        if (is_array($val)) {
            $result['ids'] = array_values(array_unique(array_map('absint', $val)));
            $result['format'] = 'array';
            $result['path'] = 'desktop';
            return $result;
        }
        if (is_string($val)) {
            $result['ids'] = normalize_id_list_to_array($val);
            $result['format'] = 'string';
            $result['path'] = 'desktop';
            return $result;
        }
    }
    // flat value
    if (isset($attrs['image']['advanced']['galleryIds']['value'])) {
        $val = $attrs['image']['advanced']['galleryIds']['value'];
        if (is_array($val)) {
            $result['ids'] = array_values(array_unique(array_map('absint', $val)));
            $result['format'] = 'array';
            $result['path'] = 'flat';
            return $result;
        }
        if (is_string($val)) {
            $result['ids'] = normalize_id_list_to_array($val);
            $result['format'] = 'string';
            $result['path'] = 'flat';
            return $result;
        }
    }
    // direct string/array fallback (non-standard)
    if (isset($attrs['image']['advanced']['galleryIds'])) {
        $val = $attrs['image']['advanced']['galleryIds'];
        if (is_array($val)) {
            $result['ids'] = array_values(array_unique(array_map('absint', $val)));
            $result['format'] = 'array';
            $result['path'] = 'raw';
            return $result;
        }
        if (is_string($val)) {
            $result['ids'] = normalize_id_list_to_array($val);
            $result['format'] = 'string';
            $result['path'] = 'raw';
            return $result;
        }
    }
    return $result;
}

function divi5_set_gallery_ids_into_attrs($attrs, array $ids, array $info) {
    if ($info['format'] === 'array') {
        if ($info['path'] === 'desktop') {
            $attrs['image']['advanced']['galleryIds']['desktop']['value'] = array_values($ids);
        } elseif ($info['path'] === 'flat') {
            $attrs['image']['advanced']['galleryIds']['value'] = array_values($ids);
        } else { // raw
            $attrs['image']['advanced']['galleryIds'] = array_values($ids);
        }
    } else { // string format
        $csv = implode(',', array_map('intval', $ids));
        if ($info['path'] === 'desktop') {
            $attrs['image']['advanced']['galleryIds']['desktop']['value'] = $csv;
        } elseif ($info['path'] === 'flat') {
            $attrs['image']['advanced']['galleryIds']['value'] = $csv;
        } else { // raw
            $attrs['image']['advanced']['galleryIds'] = $csv;
        }
    }
    return $attrs;
}

// Created at 1759292571.
