/**
 * ArraySubs — Elementor editor control view.
 *
 * Registers the `arraysubs_ajax_select` control: a searchable, AJAX-backed
 * Select2 that queries the ArraySubs select-options REST endpoint. Each control
 * passes its endpoint config through the `arraysubs_select` model attribute,
 * e.g. { type: 'products', query: { product_type: 'subscription' } }.
 *
 * Note: `type` is the dispatcher route; any filter (e.g. subscription-only) must
 * ride on a separate key such as `product_type` so it cannot clobber the route.
 *
 * Requests prefer wp.apiFetch (its nonce middleware is refreshed by the editor
 * heartbeat, so long sessions don't break) and fall back to jQuery.ajax.
 */
( function ( $ ) {
	'use strict';

	/**
	 * Perform a select-options request, preferring wp.apiFetch for nonce safety.
	 *
	 * @param {Object} data Query params.
	 * @return {Promise} Resolves with the parsed JSON response.
	 */
	function arraysubsRequest( data ) {
		var settings = window.ArraySubsElementor || {};

		if ( window.wp && wp.apiFetch && settings.path ) {
			return wp.apiFetch( { path: settings.path + '?' + $.param( data ) } );
		}

		return Promise.resolve(
			$.ajax( {
				url: settings.restUrl,
				dataType: 'json',
				headers: { 'X-WP-Nonce': settings.nonce },
				data: data,
			} )
		);
	}

	function register() {
		if (
			typeof elementor === 'undefined' ||
			! elementor.modules ||
			! elementor.modules.controls ||
			! elementor.modules.controls.Select2
		) {
			return false;
		}

		var Select2View = elementor.modules.controls.Select2;

		var AjaxSelectView = Select2View.extend( {
			/**
			 * Endpoint config for this control instance.
			 */
			getArraySubsConfig: function () {
				return this.model.get( 'arraysubs_select' ) || {};
			},

			/**
			 * Merge AJAX options into the base Select2 config.
			 */
			getSelect2Options: function () {
				var base = Select2View.prototype.getSelect2Options.apply( this, arguments ) || {},
					cfg = this.getArraySubsConfig();

				return $.extend( {}, base, {
					minimumInputLength: 0,
					width: '100%',
					ajax: {
						delay: 250,
						data: function ( params ) {
							var data = { type: cfg.type, search: params.term || '' };
							if ( cfg.query ) {
								$.each( cfg.query, function ( key, val ) {
									data[ key ] = val;
								} );
							}
							return data;
						},
						transport: function ( params, success, failure ) {
							arraysubsRequest( params.data )
								.then( success )
								.catch( failure || function () {} );
							return { abort: function () {} };
						},
						processResults: function ( response ) {
							var rows = ( response && ( response.data || response.options ) ) || [];
							return {
								results: $.map( rows, function ( option ) {
									return { id: String( option.value ), text: option.label };
								} ),
							};
						},
						cache: true,
					},
				} );
			},

			/**
			 * After the base view is ready, backfill labels for saved values so
			 * the chips render (AJAX selects have no preloaded <option> list).
			 *
			 * The base Select2 onReady() is a deprecated no-op in Elementor 4.x,
			 * so it is intentionally not chained here.
			 */
			onReady: function () {
				this.preloadSelectedLabels();
			},

			preloadSelectedLabels: function () {
				var self = this,
					cfg = this.getArraySubsConfig(),
					value = this.getControlValue();

				value = _.isArray( value ) ? value : ( value ? [ value ] : [] );

				var missing = _.filter( value, function ( id ) {
					return id && ! self.ui.select.find( 'option[value="' + id + '"]' ).length;
				} );

				if ( ! missing.length ) {
					return;
				}

				var data = { type: cfg.type, include: missing.join( ',' ) };
				if ( cfg.query ) {
					$.each( cfg.query, function ( key, val ) {
						data[ key ] = val;
					} );
				}

				arraysubsRequest( data ).then( function ( response ) {
					var rows = ( response && ( response.data || response.options ) ) || [];
					$.each( rows, function ( index, option ) {
						if ( ! self.ui.select.find( 'option[value="' + option.value + '"]' ).length ) {
							self.ui.select.append( new Option( option.label, option.value, true, true ) );
						}
					} );
					// Refresh Select2 UI without re-saving the model.
					self.ui.select.trigger( 'change.select2' );
				} );
			},
		} );

		elementor.addControlView( 'arraysubs_ajax_select', AjaxSelectView );
		return true;
	}

	if ( ! register() ) {
		$( window ).on( 'elementor:init', register );
	}
} )( jQuery );
