/**
 * List Table Drag and Drop
 * Handles drag and drop reordering for list tables
 *
 * @package Framework
 */

(function ($) {
	'use strict';

	/**
	 * Initialize drag and drop for list tables
	 */
	class NxtfDraggable {
		constructor() {
			this.init();
		}

		/**
		 * Initialize all draggable tables on the page
		 */
		init() {
			const tables = document.querySelectorAll('table[data-draggable="true"]');

			tables.forEach((table) => {
				this.initTable(table);
			});
		}

		/**
		 * Initialize a single table for drag and drop
		 *
		 * @param {HTMLElement} table The table element
		 */
		initTable(table) {
			const tableId = table.dataset.tableId;
			const nonce = table.dataset.nonce;
			const tbody = table.querySelector('tbody#the-list');
			let currentOrderedIds = [];
			let originalOrderedIds = [];

			if (!tbody || !tableId || !nonce) {
				console.warn('Draggable: Missing required data attributes');
				return;
			}

			if (this.hasRestrictedView()) {
				this.checkFilters(table);
				return;
			}

			currentOrderedIds = this.collectOrderedIds(tbody);
			originalOrderedIds = currentOrderedIds.slice();

			// Check if Sortable library is available
			if (typeof Sortable === 'undefined') {
				console.error('Draggable: SortableJS library not found');
				return;
			}

			// Initialize Sortable
			const sortable = new Sortable(tbody, {
				animation: 150,
				handle: '.nxtf-drag-handle',
				ghostClass: 'nxtf-sortable-ghost',
				chosenClass: 'nxtf-sortable-chosen',
				dragClass: 'nxtf-sortable-drag',
				forceFallback: true,
				fallbackClass: 'nxtf-sortable-fallback',
				scroll: true,
				bubbleScroll: true,

				onStart: (evt) => {
					originalOrderedIds = currentOrderedIds.slice();

					// Add dragging class to body
					document.body.classList.add('nxtf-dragging');
				},

				onEnd: (evt) => {
					// Remove dragging class from body
					document.body.classList.remove('nxtf-dragging');

					// Only update if position actually changed
					if (evt.oldIndex !== evt.newIndex) {
						this.updateOrder(
							tbody,
							tableId,
							nonce,
							originalOrderedIds,
							(orderedIds) => {
								currentOrderedIds = orderedIds.slice();
							},
							() => {
								this.restoreOrder(tbody, originalOrderedIds);
								currentOrderedIds = originalOrderedIds.slice();
							}
						);
					}
				},
			});

			// Show message if search/sort is active
			this.checkFilters(table);
		}

		/**
		 * Check if filters are active and show message
		 *
		 * @param {HTMLElement} table The table element
		 */
		checkFilters(table) {
			const hasFilters = this.hasRestrictedView();

			if (hasFilters) {
				const wrapper = table.closest('.wp-list-table');
				if (wrapper && !wrapper.querySelector('.nxtf-drag-notice')) {
					const notice = document.createElement('div');
					notice.className = 'nxtf-drag-notice nxtf-drag-notice--info';

					// Create paragraph with text content (XSS safe)
					const paragraph = document.createElement('p');
					paragraph.className = 'nxtf-drag-notice-text';

					const strong = document.createElement('strong');
					strong.className = 'nxtf-drag-notice-label';
					strong.textContent = 'Notice:';

					paragraph.appendChild(strong);
					paragraph.appendChild(document.createTextNode(' Drag and drop is disabled while search or sorting is active. Clear filters to reorder items.'));

					notice.appendChild(paragraph);
					wrapper.parentNode.insertBefore(notice, wrapper);
				}
			}
		}

		/**
		 * Whether the current table view has active search or column sorting.
		 *
		 * @returns {boolean}
		 */
		hasRestrictedView() {
			const urlParams = new URLSearchParams(window.location.search);
			return urlParams.has('s') || urlParams.has('orderby');
		}

		/**
		 * Update the order via Ajax
		 *
		 * @param {HTMLElement} tbody The table body element
		 * @param {string} tableId The table identifier
		 * @param {string} nonce The security nonce
		 */
		updateOrder(tbody, tableId, nonce, originalOrderedIds, onSuccess, onFailure) {
			const orderedIds = this.collectOrderedIds(tbody);

			if (orderedIds.length === 0) {
				console.warn('Draggable: No items found to reorder');
				return;
			}

			// Show loading indicator
			this.showLoading(tbody);

			// Send Ajax request
			$.ajax({
				url: ajaxurl, // WordPress Ajax URL (globally available in admin)
				type: 'POST',
				data: {
					action: tbody.closest('table').dataset.ajaxAction,
					table_id: tableId,
					original_ordered_ids: originalOrderedIds,
					ordered_ids: orderedIds,
					nonce: nonce,
				},
				success: (response) => {
					this.hideLoading(tbody);

					if (response.success) {
						if (typeof onSuccess === 'function') {
							onSuccess(orderedIds);
						}
						this.showNotice('success', response.data.message || 'Order updated successfully.');
					} else {
						if (typeof onFailure === 'function') {
							onFailure();
						}
						this.showNotice('error', response.data.message || 'Failed to update order.');
						// Optionally reload to revert visual changes
						// location.reload();
					}
				},
				error: (xhr, status, error) => {
					this.hideLoading(tbody);
					if (typeof onFailure === 'function') {
						onFailure();
					}
					const responseMessage = xhr.responseJSON && xhr.responseJSON.data && xhr.responseJSON.data.message
						? xhr.responseJSON.data.message
						: null;
					const fallbackError = error || status || 'Unknown error';
					this.showNotice(
						'error',
						responseMessage || ('An error occurred while updating order: ' + fallbackError)
					);
					console.error('Draggable Error:', xhr, status, error);
				},
			});
		}

		/**
		 * Collect ordered row IDs from the current table body.
		 *
		 * @param {HTMLElement} tbody The table body element
		 * @returns {string[]}
		 */
		collectOrderedIds(tbody) {
			const rows = tbody.querySelectorAll('tr[data-item-id]');
			return Array.from(rows)
				.filter((row) => {
					return !row.classList.contains('sortable-fallback') &&
						!row.classList.contains('nxtf-sortable-fallback') &&
						!row.classList.contains('sortable-ghost') &&
						!row.classList.contains('nxtf-sortable-ghost');
				})
				.map((row) => row.dataset.itemId);
		}

		/**
		 * Restore the row order in the DOM.
		 *
		 * @param {HTMLElement} tbody The table body element
		 * @param {string[]} orderedIds Row IDs in the order to restore
		 */
		restoreOrder(tbody, orderedIds) {
			const rows = Array.from(tbody.querySelectorAll('tr[data-item-id]'));
			const rowsById = new Map(rows.map((row) => [row.dataset.itemId, row]));

			orderedIds.forEach((id) => {
				const row = rowsById.get(id);
				if (row) {
					tbody.appendChild(row);
				}
			});
		}

		/**
		 * Show loading indicator
		 *
		 * @param {HTMLElement} tbody The table body element
		 */
		showLoading(tbody) {
			tbody.style.opacity = '0.6';
			tbody.style.pointerEvents = 'none';

			// Add spinner to table
			const table = tbody.closest('table');
			if (table && !table.querySelector('.nxtf-drag-spinner')) {
				const spinner = document.createElement('div');
				spinner.className = 'nxtf-drag-spinner';

				// Create spinner element safely
				const spinnerIcon = document.createElement('span');
				spinnerIcon.className = 'spinner is-active';
				spinner.appendChild(spinnerIcon);

				table.style.position = 'relative';
				table.appendChild(spinner);
			}
		}

		/**
		 * Hide loading indicator
		 *
		 * @param {HTMLElement} tbody The table body element
		 */
		hideLoading(tbody) {
			tbody.style.opacity = '';
			tbody.style.pointerEvents = '';

			// Remove spinner
			const table = tbody.closest('table');
			if (table) {
				const spinner = table.querySelector('.nxtf-drag-spinner');
				if (spinner) {
					spinner.remove();
				}
			}
		}

		/**
		 * Show a notice message
		 *
		 * @param {string} type The notice type (success, error, warning, info)
		 * @param {string} message The message to display
		 */
		showNotice(type, message) {
			// Remove existing notices
			const existingNotices = document.querySelectorAll('.nxtf-drag-notice-message');
			existingNotices.forEach((notice) => notice.remove());

			// Type-specific notice modifiers
			const typeClasses = {
				success: 'nxtf-drag-notice-message--success',
				error: 'nxtf-drag-notice-message--error',
				warning: 'nxtf-drag-notice-message--warning',
				info: 'nxtf-drag-notice-message--info',
			};

			// Create new notice (XSS safe - using DOM methods instead of innerHTML)
			const notice = document.createElement('div');
			notice.className = `nxtf-drag-notice-message ${typeClasses[type] || typeClasses.info}`;

			// Create message paragraph with safe text content
			const messageParagraph = document.createElement('p');
			messageParagraph.className = 'nxtf-drag-notice-text';
			messageParagraph.textContent = message; // XSS safe - auto-escapes

			// Create dismiss button
			const dismissButton = document.createElement('button');
			dismissButton.type = 'button';
			dismissButton.className = 'nxtf-drag-notice-dismiss';
			dismissButton.setAttribute('aria-label', 'Dismiss');

			const dismissIcon = document.createElement('span');
			dismissIcon.className = 'dashicons dashicons-no-alt nxtf-drag-notice-dismiss-icon';
			dismissButton.appendChild(dismissIcon);

			// Assemble the notice
			notice.appendChild(messageParagraph);
			notice.appendChild(dismissButton);

			// Insert at top of content
			const contentWrap =
				document.querySelector('.nxtf-page-wrapper') ||
				document.querySelector('.wrap') ||
				document.querySelector('#wpbody-content');
			if (contentWrap) {
				contentWrap.insertBefore(notice, contentWrap.firstChild);

				// Auto-dismiss after 3 seconds
				setTimeout(() => {
					notice.style.opacity = '0';
					setTimeout(() => notice.remove(), 300);
				}, 3000);

				// Add dismiss button functionality
				dismissButton.addEventListener('click', () => {
					notice.remove();
				});
			}
		}
	}

	/**
	 * Initialize on document ready
	 */
	$(function () {
		new NxtfDraggable();
	});
})(jQuery);
