<?php
namespace Pay4All\Pro\Admin;

use Pay4All\Pro\PostTypes;
use Pay4All\Pro\Domain\Category;
use Pay4All\Pro\Domain\Brand;
use Pay4All\Form\Admin\Assets;

if ( ! defined( 'ABSPATH' ) ) { exit; }

/**
 * Drag-and-drop reordering of terms on the taxonomy edit screens for both
 * p4all_product_cat and p4all_product_brand. Persists an integer sort key
 * on each term ({Category|Brand}::META_ORDER) which the public filter
 * widget then honours when building its dropdowns.
 *
 * UX: whole <tr> is draggable via a "move" cursor on the Name column. On
 * drop, we POST the new order (list of term ids) to admin-ajax and let PHP
 * rewrite the meta values 10, 20, 30, … so future reorders always have
 * gaps to insert into.
 */
final class TermReorder {

	public const AJAX_ACTION = 'p4all_reorder_terms';
	public const NONCE       = 'p4all_reorder_terms_nonce';

	public static function register() : void {
		add_action( 'admin_enqueue_scripts', [ self::class, 'enqueue' ] );
		add_action( 'wp_ajax_' . self::AJAX_ACTION, [ self::class, 'handle_ajax' ] );

		// Register a dedicated "Tri" column on both taxonomies. The
		// filter `manage_edit-{taxonomy}_columns` sets the header ; the
		// filter `manage_{taxonomy}_custom_column` renders each row.
		foreach ( [ PostTypes::TAXONOMY, PostTypes::TAXONOMY_BRAND ] as $tax ) {
			add_filter( 'manage_edit-' . $tax . '_columns', [ self::class, 'add_sort_column' ] );
			add_filter( 'manage_' . $tax . '_custom_column', [ self::class, 'render_sort_column' ], 10, 3 );
		}
	}

	/**
	 * Prepend our "Tri" column so it appears BEFORE the bulk checkbox
	 * (`cb`), i.e. at the far-left edge of every row.
	 *
	 * @param array<string,string> $columns
	 * @return array<string,string>
	 */
	public static function add_sort_column( array $columns ) : array {
		return [ 'p4all_tri' => '<span class="screen-reader-text">' . esc_html__( 'Tri', 'pay4all-pro' ) . '</span>' ] + $columns;
	}

	/**
	 * Render the "☰" drag handle for the Tri column.
	 *
	 * Signature for taxonomy filter: (string $content, string $column, int $term_id) → string
	 *
	 * @param string $content
	 * @param string $column
	 * @param int    $term_id
	 * @return string
	 */
	public static function render_sort_column( $content, $column, $term_id ) : string {
		if ( $column !== 'p4all_tri' ) { return (string) $content; }
		return '<span class="p4all-drag-handle" aria-hidden="true" title="' . esc_attr__( 'Glissez pour réordonner', 'pay4all-pro' ) . '">☰</span>';
	}

	public static function enqueue( $hook ) : void {
		if ( $hook !== 'edit-tags.php' && $hook !== 'term.php' ) { return; }
		$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
		if ( ! $screen instanceof \WP_Screen ) { return; }
		if ( ! in_array( $screen->taxonomy, [ PostTypes::TAXONOMY, PostTypes::TAXONOMY_BRAND ], true ) ) {
			return;
		}
		// jQuery UI Sortable is bundled with WordPress core.
		wp_enqueue_script( 'jquery-ui-sortable' );

		$data = [
			'ajaxurl'  => admin_url( 'admin-ajax.php' ),
			'action'   => self::AJAX_ACTION,
			'nonce'    => wp_create_nonce( self::NONCE ),
			'taxonomy' => (string) $screen->taxonomy,
			'labels'   => [
				'saving'    => __( 'Enregistrement…', 'pay4all-pro' ),
				'saved'     => __( 'Ordre enregistré.', 'pay4all-pro' ),
				'error'     => __( 'Impossible d\'enregistrer l\'ordre.', 'pay4all-pro' ),
				// Split in two so the merchant can still see the "Glissez-
				// déposez" verb in bold. Concatenated in that order by the JS
				// heredoc below — translators must keep the two parts as a
				// single logical sentence.
				'drag_verb' => __( 'Glissez-déposez', 'pay4all-pro' ),
				'drag_rest' => __( 'les lignes pour choisir l\'ordre d\'affichage dans le filtre client.', 'pay4all-pro' ),
			],
		];

		wp_add_inline_style( Assets::HANDLE_CSS, self::css() );
		wp_add_inline_script( Assets::HANDLE_JS, 'window.p4allTermReorder = ' . wp_json_encode( $data ) . ';', 'before' );
		wp_add_inline_script( Assets::HANDLE_JS, self::js() );
	}

	private static function css() : string {
		return '.p4all-reorder-notice{margin:8px 0 12px;padding:8px 12px;background:#f6f7f7;border-left:3px solid #2271b1;font-size:13px;color:#333;max-width:780px}'
			. '.p4all-reorder-flash{margin-left:10px;color:#1b8c4a;font-weight:600;opacity:0;transition:opacity .2s}'
			. '.p4all-reorder-flash.is-visible{opacity:1}'
			. '.p4all-reorder-flash.is-error{color:#a00}'
			. 'body.taxonomy-p4all_product_cat .wp-list-table tbody tr,'
			. 'body.taxonomy-p4all_product_brand .wp-list-table tbody tr{cursor:move}'
			. '.p4all-sortable-placeholder{background:#eef4fb;outline:2px dashed #2271b1}'
			// Dedicated "Tri" column at the far-left of the row, rendered
			// by PHP via `manage_edit-{taxonomy}_columns`. Narrow, fixed
			// width — no interference with the check-column or the "select
			// all" checkbox in the header.
			. 'body.taxonomy-p4all_product_cat .wp-list-table .column-p4all_tri,'
			. 'body.taxonomy-p4all_product_brand .wp-list-table .column-p4all_tri{width:28px;padding-left:6px;padding-right:0;text-align:center}'
			. 'body.taxonomy-p4all_product_cat .wp-list-table td.column-p4all_tri,'
			. 'body.taxonomy-p4all_product_brand .wp-list-table td.column-p4all_tri{vertical-align:middle}'
			. '.p4all-drag-handle{display:inline-block;color:#999;font-size:16px;line-height:1;user-select:none;cursor:grab}'
			. '.wp-list-table tbody tr:hover .p4all-drag-handle{color:#2271b1}'
			. '.wp-list-table tbody tr.ui-sortable-helper .p4all-drag-handle{cursor:grabbing}'
			// Column reorder : WordPress lays edit-tags.php out with the
			// « Add new » form on the left (#col-left) and the term list on
			// the right (#col-right). Merchants managing dozens of categories
			// spend most of their time browsing the list, so we stack them
			// vertically with the list ON TOP and the « Ajouter une catégorie »
			// form UNDER it. Scoped to our two Pro-owned taxonomies via
			// body classes so we never touch the default post-category screen.
			. 'body.taxonomy-p4all_product_cat #col-container,'
			. 'body.taxonomy-p4all_product_brand #col-container{display:flex;flex-direction:column}'
			. 'body.taxonomy-p4all_product_cat #col-left,'
			. 'body.taxonomy-p4all_product_cat #col-right,'
			. 'body.taxonomy-p4all_product_brand #col-left,'
			. 'body.taxonomy-p4all_product_brand #col-right{width:100%;float:none}'
			. 'body.taxonomy-p4all_product_cat #col-right,'
			. 'body.taxonomy-p4all_product_brand #col-right{order:1}'
			. 'body.taxonomy-p4all_product_cat #col-left,'
			. 'body.taxonomy-p4all_product_brand #col-left{order:2;padding-top:20px}';
	}

	private static function js() : string {
		return <<<'JS'
		(function ($) {
			if (typeof window.p4allTermReorder === 'undefined') { return; }
			var cfg = window.p4allTermReorder;
			function bootstrap() {
				var $tbody = $('.wp-list-table tbody#the-list');
				if (!$tbody.length || $tbody.data('p4allInit')) { return; }
				$tbody.data('p4allInit', 1);

				// Reorder notice above the list so the merchant knows the
				// rows are draggable. Only injected once, and only if we
				// managed to find the table.
				var $wrap = $('.wp-list-table').first().before(
					'<div class="p4all-reorder-notice">'
					+ '↕ <strong>' + cfg.labels.drag_verb + '</strong> ' + cfg.labels.drag_rest
					+ '<span class="p4all-reorder-flash" role="status"></span>'
					+ '</div>'
				);
				var $flash = $('.p4all-reorder-flash');

				// Drag handle is rendered server-side by PHP as its own
				// "Tri" column (see manage_{taxonomy}_custom_column filter
				// above), so no JS injection needed here.

				function flash(msg, isError) {
					$flash.text(msg).toggleClass('is-error', !!isError).addClass('is-visible');
					clearTimeout(flash.__t);
					flash.__t = setTimeout(function () { $flash.removeClass('is-visible'); }, 1800);
				}

				$tbody.sortable({
					axis: 'y',
					cursor: 'move',
					placeholder: 'p4all-sortable-placeholder',
					helper: function (e, tr) {
						// Preserve column widths while dragging (otherwise
						// the helper row collapses to its content width).
						var $orig = tr;
						var $clone = tr.clone();
						$clone.children().each(function (i) {
							$(this).width($orig.children().eq(i).outerWidth());
						});
						return $clone;
					},
					update: function () {
						var ids = [];
						$tbody.children('tr').each(function () {
							var m = /tag-(\d+)/.exec(this.id || '');
							if (m) { ids.push(parseInt(m[1], 10)); }
						});
						flash(cfg.labels.saving, false);
						$.post(cfg.ajaxurl, {
							action:   cfg.action,
							_nonce:   cfg.nonce,
							taxonomy: cfg.taxonomy,
							ids:      ids
						}).done(function (resp) {
							if (resp && resp.success) {
								flash(cfg.labels.saved, false);
							} else {
								flash(cfg.labels.error, true);
							}
						}).fail(function () {
							flash(cfg.labels.error, true);
						});
					}
				});
			}
			$(document).ready(bootstrap);
		})(jQuery);
JS;
	}

	public static function handle_ajax() : void {
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( [ 'message' => 'forbidden' ], 403 );
		}
		if ( ! isset( $_POST['_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( (string) $_POST['_nonce'] ) ), self::NONCE ) ) {
			wp_send_json_error( [ 'message' => 'nonce' ], 400 );
		}
		$taxonomy = isset( $_POST['taxonomy'] ) ? sanitize_key( wp_unslash( (string) $_POST['taxonomy'] ) ) : '';
		if ( ! in_array( $taxonomy, [ PostTypes::TAXONOMY, PostTypes::TAXONOMY_BRAND ], true ) ) {
			wp_send_json_error( [ 'message' => 'taxonomy' ], 400 );
		}
		// phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- per-element cast below.
		$ids = ( isset( $_POST['ids'] ) && is_array( $_POST['ids'] ) )
			? array_map( 'intval', wp_unslash( $_POST['ids'] ) )
			: [];
		// phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		if ( empty( $ids ) ) {
			wp_send_json_error( [ 'message' => 'empty' ], 400 );
		}
		$meta_key = $taxonomy === PostTypes::TAXONOMY ? Category::META_ORDER : Brand::META_ORDER;
		// Rewrite the order meta in multiples of 10 so future single-item
		// inserts still have room to slot in without rewriting everything.
		$order = 10;
		foreach ( $ids as $term_id ) {
			if ( $term_id > 0 ) {
				update_term_meta( (int) $term_id, $meta_key, $order );
				$order += 10;
			}
		}
		wp_send_json_success( [ 'count' => count( $ids ) ] );
	}
}
