/* globals GravityKit, jQuery, List */
/**
 * Setups the variable holding the Stuff in the actions folder.
 *
 * @since 1.0
 *
 * @type   {Object}
 */
GravityKit.GravityActions.Actions = GravityKit.GravityActions.Actions || {};

/**
 * Setups the main variable for this file.
 *
 * @since 1.0
 *
 * @type   {Object}
 */
GravityKit.GravityActions.Actions.Edit = {};

/**
 * Initialize the main part of this class.
 *
 * @since 1.0
 *
 * @param  {Object} $   jQuery
 * @param  {Object} obj GravityKit.GravityActions.Trigger
 *
 * @return {void}
 */
( function ( $, obj, wp ) {
	'use strict';

	/**
	 * Which action this Object is responsible for.
	 *
	 * @since 1.0
	 *
	 * @type {string}
	 */
	obj.action = 'gk-bulk-edit-entries';

	/**
	 * Local alias for the GA Hooks.
	 *
	 * @since TBD
	 *
	 * @type {(function(): Hooks)|*}
	 */
	obj.hooks = window.GravityKit.GravityActions.hooks;

	/**
	 * Selectors used for configuration and setup.
	 *
	 * @since 1.0
	 *
	 * @type {Object}
	 */
	obj.selectors = {
		entryListForm: '#entry_list_form',
		selectedFieldCheckbox: '.gk-gravityactions-edit-selected-field',
		selectedFieldCount: '.gk-gravityactions-edit-selected-field-count',
		entryCheckbox: '.gform_list_checkbox',
		listContainer: '.gv-bulk-edit-step-1-fields',
		listItem: '.gk-gravityactions-edit-field-item',
		listItemTypeParent: '.gk-gravityactions-edit-field-parent',
		listItemTypeChild: '.gk-gravityactions-edit-field-child',
		checkboxField: '.gk-actions-component-checkbox__input',
		checkboxFieldContainer: '.gk-actions-component-checkbox__input-container',
		selectUserField: '.gk-actions-component-select-user__input',
	};

	/**
	 * Handles the initialization of the manager when Document is ready.
	 *
	 * @since 1.0
	 *
	 * @return {void}
	 */
	obj.ready = () => {
		$( GravityKit.GravityActions.Trigger.selectors.entryListForm ).on( 'submitBulkAction.GravityActions/GK', obj.onActionSubmit );
		$( document ).on( 'change', obj.selectors.selectedFieldCheckbox, obj.onFieldSelect );
		$( document ).on( 'afterAjaxSuccess.GravityActions/GK', obj.setupList );
		$( document ).on( 'afterAjaxSuccess.GravityActions/GK', obj.onModalSetupFields );
		$( document ).on( 'click.GravityActions/GK', obj.selectors.checkboxFieldContainer, obj.onCheckboxClick );

		obj.hooks.addAction( 'gravitykit.gravityactions.edit_modal_setup', 'gravitykit/gravityactions', obj.setupBooleanCheckbox );
		obj.hooks.addAction( 'gravitykit.gravityactions.edit_modal_setup', 'gravitykit/gravityactions', obj.setupUserSelectFields );
	};

	/**
	 * When the action is submitted from the bulk actions dropdown we trigger this.
	 *
	 * @param {Event} event
	 * @param {Array} entries
	 * @param {Object} data
	 * @param {Event} originalEvent
	 */
	obj.onActionSubmit = ( event, entries, data, originalEvent ) => {
		if ( obj.action !== data.action ) {
			return;
		}

		const $allEntries = $( '#all_entries' );
		const isAllEntries = $allEntries.val();

		let args = {
			view: 'edit/step-1',
			entries: entries,
			form_id: data.formID,
			bulk_action: data.action,
			is_all_entries: isAllEntries,
			current_query_args: window.location.search.substring( 1 ),
		};

		args[ data.nonceName ] = data.nonce;

		GravityKit.GravityActions.Modal.request( args );
	};

	/**
	 * Triggers when the edits are completed.
	 *
	 * @since 1.3
	 *
	 * @param {numeric} formID
	 * @param {Array} entries
	 * @param {Array} selectedFields
	 * @param {String} action
	 * @param {String} nonce
	 * @param {String} nonceName
	 * @param {String} step
	 *
	 * @return {void}
	 */
	obj.onCompleteUpdating = ( formID, entries, selectedFields, action, nonce, nonceName, step = 'edit/step-complete' ) => {
		const args = {
			view: step,
			entries: entries,
			selected_fields: selectedFields,
			form_id: formID,
			bulk_action: action,
		};
		args[ nonceName ] = nonce;

		GravityKit.GravityActions.Modal.request( args );
	};

	/**
	 * Setups up the searchable List for fields.
	 *
	 * @since 1.3
	 *
	 * @return {void}
	 */
	obj.setupList = () => {
		const options = {
			listClass: 'gv-bulk-edit-step-1-fields',
			searchClass: 'gv-bulk-edit-search',
			valueNames: [
				'gk-gravityactions-edit-field-name',
				{data: [ 'id', 'filter-name', 'filter-type' ]},
			],
		};

		/**
		 * @see https://listjs.com/
		 */
		const fieldsList = new List( 'gk-gravityactions-edit-step-1', options );
	};

	/**
	 * Triggers on the field selection.
	 *
	 * @since 1.3
	 *
	 * @param {Event} event
	 *
	 * @return {void}
	 **/
	obj.onFieldSelect = function ( event ) {
		const $field = $( this );
		const $listItem = $field.parents( obj.selectors.listItem ).eq( 0 );
		const $list = $field.parents( obj.selectors.listContainer ).eq( 0 );
		const type = $listItem.is( obj.selectors.listItemTypeParent ) ? 'parent' : 'child';
		const fieldId = $listItem.data( 'id' );
		const $hiddenFields = $( '.gk-gravityactions-hidden-selected-fields' );
		const $checkboxes = $list.find( obj.selectors.selectedFieldCheckbox );

		obj.updateSelectedCount( $field );

		if ( type === 'parent' ) {
			const $children = $list.find( obj.selectors.listItemTypeChild ).filter( `[data-id="${fieldId}"]` );
			const $childrenFields = $children.find( obj.selectors.selectedFieldCheckbox );
			$childrenFields.prop( 'checked', $field.prop( 'checked' ) );

			obj.updateSelectedCount();
		} else {
			const $parent = $list.find( obj.selectors.listItemTypeParent ).filter( `[data-id="${fieldId}"]` );
			const $parentField = $parent.find( obj.selectors.selectedFieldCheckbox );
			const $siblings = $list.find( obj.selectors.listItemTypeChild ).filter( `[data-id="${fieldId}"]` );
			const $siblingFields = $siblings.find( obj.selectors.selectedFieldCheckbox );

			if ( ! $parentField.is( ':checked' ) ) {
				// In this case we avoid trigger the change event to avoid recursion.
				$parentField.prop( 'checked', true );
				obj.updateSelectedCount();
			}

			if ( $siblingFields.filter( ':checked' ).length === 0 ) {
				// In this case we avoid trigger the change event to avoid recursion.
				$parentField.prop( 'checked', false );
				obj.updateSelectedCount();
			}
		}

		$hiddenFields.prop( 'disabled', true );
		$checkboxes.filter( ':checked' ).each( ( index, checkboxField ) => {
			const $checkboxField = $( checkboxField );
			const fieldValue = $checkboxField.val();
			$hiddenFields.filter( `[value='${fieldValue}']` ).prop( 'disabled', ! $checkboxField.is( ':checked' ) );
		} );
	};

	/**
	 * Updates the selected count of fields on the second Step of the Editing process.
	 *
	 * @since 1.3
	 *
	 * @return {void}
	 */
	obj.updateSelectedCount = () => {
		const $fields = $( obj.selectors.selectedFieldCheckbox );
		const $fieldsCount = $( obj.selectors.selectedFieldCount );
		const noneSelected = $fieldsCount.data( 'noneSelected' );
		const oneSelected = $fieldsCount.data( 'oneSelected' );
		const multipleSelected = $fieldsCount.data( 'multipleSelected' );
		const checkedCount = $fields.filter( ':checked' ).length;

		if ( 0 === checkedCount ) {
			$fieldsCount.text( noneSelected );
		} else if ( 1 === checkedCount ) {
			$fieldsCount.text( oneSelected );
		} else {
			$fieldsCount.text( multipleSelected.replace( '%s', checkedCount ) );
		}
	};

	/**
	 * Bound to the click of the checkbox.
	 *
	 * @since 1.3
	 *
	 * @param {Event} event DOM Event associated with the setup of fields.
	 *
	 * @return {void}
	 */
	obj.onCheckboxClick = ( event ) => {
		const $container = $( event.currentTarget );
		obj.toggleBooleanField( $container );
	};

	/**
	 * Runs when the Modal finishes loading an HTML request.
	 *
	 * @since 1.3
	 *
	 * @param {Event} event DOM Event associated with the setup of fields.
	 *
	 * @return {void}
	 */
	obj.onModalSetupFields = ( event ) => {
		const $modal = $( event.target );

		obj.hooks.doAction( 'gravitykit.gravityactions.edit_modal_setup', $modal, event );
	};

	/**
	 * Configures the User Select fields.
	 *
	 * @since 1.3
	 *
	 * @param {jQuery} $area Area to set up the fields in, normally the modal.
	 * @param {Event}  event The DOM Event associated with setup.
	 *
	 * @return {void}
	 */
	obj.setupUserSelectFields = ( $area, event ) => {
		const $fields = $area.find( obj.selectors.selectUserField );

		$fields.each( ( index, field ) => {
			const $field = $( field );

			$field.selectWoo( obj.getUserSelectWooOptions( $field ) );
		} );
	};

	/**
	 * Fetches the SelectWoo options for a given field that will do User Search..
	 *
	 * @since 1.3
	 *
	 * @param {jQuery} $field
	 *
	 * @returns {Object}
	 */
	obj.getUserSelectWooOptions = ( $field ) => {
		return {
			allowClear: true,
			placeholder: $field.data( 'placeholder' ),
			minimumInputLength: 1,
			dropdownAutoWidth: true,
			ajax: {
				url: $field.data( 'ajaxUrl' ),
				dataType: 'json',
				type: 'POST',
				delay: 250,
				cache: true,
				processResults: ( data ) => { // parse the results into the format expected by Select2.
					// since we are using custom formatting functions we do not need to alter the remote JSON data
					return {
						results: data.data.results
					};
				},
				data: ( params ) => {
					const query = {
						search: params.term,
						action: 'gk-gravityactions/get_users',
						nonce: $field.data( 'nonce' ),
					};
					return query;
				},
			},
		};
	};

	/**
	 * Gets the String for the SVG used for the Boolean Checkbox
	 *
	 * @since 1.3
	 *
	 * @return {String}
	 */
	obj.getBooleanCheckboxSVG = () => {
		const SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" role="presentation" class="components-checkbox-control__checked" aria-hidden="true" focusable="false"><path d="M16.7 7.1l-6.3 8.5-3.3-2.5-.9 1.2 4.5 3.4L17.9 8z"></path></svg>`;
		return obj.hooks.applyFilters( 'gravitykit.gravityactions.get_checkbox_svg', SVG );
	};

	/**
	 * Toggles the boolean field based on a container.
	 *
	 * @since 1.3
	 *
	 * @param {Event} $container
	 *
	 * @return {void}
	 */
	obj.toggleBooleanField = ( $container ) => {
		const $field = $container.find( obj.selectors.checkboxField );

		if ( $field.length === 0 ) {
			return;
		}

		if ( $field.is( ':checked' ) ) {
			$container.append( obj.getBooleanCheckboxSVG() );
		} else {
			$container.find( 'svg' ).remove();
		}
	};

	/**
	 * Configures the Boolean checkboxes.
	 *
	 * @since 1.3
	 *
	 * @param {jQuery} $area
	 * @param {Event} event
	 *
	 * @return {void}
	 */
	obj.setupBooleanCheckbox = ( $area, event ) => {
		const $containers = $area.find( obj.selectors.checkboxFieldContainer );

		$containers.each( ( index, container ) => {
			const $container = $( container );
			obj.toggleBooleanField( $container );
		} );
	};

	// Configure on document ready.
	$( obj.ready );
} )( jQuery, GravityKit.GravityActions.Actions.Edit, window.wp );