/**
 * Available spaces:
 *
 * _window.$usb - Basic object for mounting and initializing all extensions of the builder
 * _window.$usbcore - Auxiliary functions for the builder and its extensions
 * _window.$usof - UpSolution CSS Framework
 * _window.$ush - US Helper Library
 *
 * Note: Double underscore `__funcname` is introduced for functions that are created through `$ush.debounce(...)`.
 */
! function( $, undefined ) {

	const _window = window;

	if ( ! _window.$usb ) {
		return;
	}

	_window.$ush = _window.$ush || {};
	_window.$usbcore = _window.$usbcore || {};

	/**
	 * @type {{}} Private temp data
	 */
	var _$tmp = {
		fieldsets: {}, // fieldset for settings group
		isProcessSave: false, // the AJAX process of save data on the backend
		savedLiveOptions: {}, // save the last saved "Site Settings"
	};

	/**
	 * @class Site Settings - Site settings functionality (Theme Settings)
	 */
	function SiteSettings( container ) {
		const self = this;

		/**
		 * @type {String} Selected setting group id
		 */
		self.selectedGroupId;

		/**
		 * @type {USOF Fieldset} Active fieldset object
		 */
		self.activeFieldset;

		/**
		 * @type {Node} Active fieldset node
		 */
		self.$activeFieldset;

		// If you reload or leave the page, clear cookie data for preview
		if ( $usb.urlManager.hasParam( 'action', $usb.config( 'actions.site_settings' ) ) ) {
			_window.onbeforeunload = function( e ) {
				$usb.postMessage( 'clearCookies' );
			};
		}

		// Bindable events
		self._events = {
			changeField: self._changeField.bind( self ),
			clickGoToBack: self._clickGoToBack.bind( self ),
			clickGroupId: self._clickGroupId.bind( self ),
			confirmExit: self._confirmExit.bind( self ),
			handlerClearBody: self._handlerClearBody.bind( self ),
			iframeReady: self._iframeReady.bind( self ),
			saveChanges: self._saveChanges.bind( self ),
			urlManager: self._urlManager.bind( self ),
		};

		$( () => {

			// Elements
			self.$container = $( container );
			self.$menu = $( '.usb-panel-site-settings-menu', $usb.$panel );

			// Actions
			self.$actionGoToBack = $( '.usb_action_go_to_back', $usb.$panel );

			// Load fieldsets
			var $fieldsets = $( '#usb-site-settings-fieldsets', self.$container );
			if ( $fieldsets.is( '[onclick]' ) ) {
				_$tmp.fieldsets = $fieldsets[0].onclick() || {};
				$fieldsets.remove();
			}

			// Events
			$usb.$panel
				.on( 'click', '[data-group-id]', self._events.clickGroupId )
				.on( 'click', '.usb_action_go_to_back', self._events.clickGoToBack )
				.on( 'click', 'a:not([target=_blank])', self._events.confirmExit );

			// Run URL manager after ready
			self._urlManager( $usb.urlManager.getDataOfChange() );
		});

		// Private events
		$usb
			.on( 'iframeReady', self._events.iframeReady )
			.on( 'panel.clearBody', self._events.handlerClearBody )
			.on( 'panel.saveChanges', self._events.saveChanges )
			.on( 'hotkeys.ctrl+s', self._events.saveChanges )
			.on( 'urlManager.changed', self._events.urlManager );
	}

	/**
	 * @type {Prototype}
	 */
	const prototype = SiteSettings.prototype;

	// Private events
	$.extend( prototype, {
		/**
		 * Handler of change or move event on the history stack
		 *
		 * @event handler
		 * @param {{}|undefined} state Data object associated with history and current location
		 */
		_urlManager: function( state ) {
			const self = this;
			var _siteSettings = $usb.config( 'actions.site_settings' );

			if ( ! self.isReady() ) {
				return;
			}
			// Show "Site Settings"
			if ( state.setParams.action === _siteSettings ) {
				// Initialize settings group by id
				var groupId = state.setParams.group;
				if ( groupId && self.selectedGroupId !== groupId ) {
					// Initialize after an iframe is ready
					if ( ! $usb.iframeIsReady ) {
						// Show preloader in panel until preview and data from iframe are loaded
						$usb.panel.showPreloader();
						$usb.one( 'iframeReady', self.initFieldset.bind( self, groupId ) );
					} else {
						self.initFieldset( groupId );
					}
				}
				else if ( ! groupId ) {
					self.showGeneralMenu(); // show general menu
				}

				// Control of the "Go Back" button
				self.$actionGoToBack
					.toggleClass( 'disabled', ! state.setParams.group );
			}
			// Hide "Settings Menu"
			else if ( state.oldParams.action === _siteSettings ) {
				self._handlerClearBody(); // clear panel body
				_$tmp.savedLiveOptions = {}; // remove saved data

				// Remove related params
				$usb.urlManager.removeParam( 'group' ).push();
			}
		},

		/**
		 * Shows the browser confirmation before exit (e.g. clicking on links without target="_blank")
		 *
		 * @event handler
		 * @param {Event} e
		 */
		_confirmExit: function( e ) {
			const message = $usb.getTextTranslation( 'page_leave_warning' );
			if ( this.isChanged() && ! confirm( message ) ) {
				e.preventDefault();
				return;
			}
		},

		/**
		 * Iframe ready event handler
		 *
		 * @event handler
		 */
		_iframeReady: function() {
			const self = this;
			if (
				! $usb.iframeIsReady
				|| ! $usb.urlManager.hasParam( 'action', $usb.config( 'actions.site_settings' ) )
			) {
				return;
			}

			const iframeWindow = $usb.iframe.contentWindow;

			/**
			 * @type {{}} Current site settings (import from iframe)
			 */
			self.liveOptions = $ush.clone( ( iframeWindow.usGlobalData || {} ).liveOptions || {} );

			// Set first saved live options
			if ( $.isEmptyObject( _$tmp.savedLiveOptions ) ) {
				_$tmp.savedLiveOptions = $ush.clone( self.liveOptions );
			}

			// Hide preloader in panel
			$usb.panel.hidePreloader();
		},

		/**
		 * Clear the panel body
		 *
		 * @event handler
		 */
		_handlerClearBody: function() {
			this._destroyFieldset();
			$usb.postMessage( 'clearCookies' );
		}
	} );

	// Site Settings API
	$.extend( prototype, {

		/**
		 * Determines if ready
		 *
		 * @return {Boolean} True if ready, False otherwise
		 */
		isReady: function() {
			return ! $ush.isUndefined( this.$container );
		},

		/**
		 * Determines if settings changed
		 *
		 * @return {Boolean} True if settings changed, False otherwise
		 */
		isChanged: function() {
			return ! $ush.comparePlainObject( _$tmp.savedLiveOptions, this.liveOptions || {} );
		},

		/**
		 * Determines if process save
		 *
		 * @return {Boolean} True if process save, False otherwise
		 */
		isProcessSave: function() {
			return _$tmp.isProcessSave;
		},

		/**
		 * Show the general menu of setup groups
		 */
		showGeneralMenu: function() {
			const self = this;

			if ( ! self.isReady() ) {
				return;
			}

			// Panel preparation
			$usb.panel.setTitle( 'site_settings', /* isTranslationKey */true );
			$usb.trigger( 'panel.clearBody' );
			self.$menu.removeClass( 'hidden' );
		},

		/**
		 * Handler for clicking on a settings group
		 *
		 * @event handler
		 * @param {Event} e
		 */
		_clickGroupId: function( e ) {
			const groupId = $usbcore.$attr( e.currentTarget, 'data-group-id' );
			if ( groupId ) {
				$usb.urlManager.setParam( 'group', groupId ).push();
			}
		},

		/**
		 * Click go to back
		 *
		 * @event handler
		 */
		_clickGoToBack: function() {
			$usb.urlManager.removeParam( 'group' ).push();
		},

		/**
		 * Initialize fieldset for a settings group by name
		 *
		 * @param {String} groupId The group id
		 */
		initFieldset: function( groupId ) {
			const self = this;
			if ( $ush.isUndefined( _$tmp.fieldsets[ groupId ] ) ) {
				return;
			}
			self.$menu.addClass( 'hidden' );

			// Set shortcode title to header title
			var groupTitle = $usb.config( `site.group_titles.${groupId}` );
			if ( groupTitle ) {
				$usb.panel.setTitle( groupTitle );
			}

			// Set value to variables
			self.$activeFieldset = $( _$tmp.fieldsets[ groupId ] );
			// Note: Add html before field initialization so that all data is loaded,
			// for example: 'window.$usof.colorList'
			$usb.$panelBody.prepend( self.$activeFieldset );

			self.selectedGroupId = groupId;
			// TODO: Replace $usof.GroupParams with $usof.Form
			self.activeFieldset = new $usof.GroupParams( self.$activeFieldset );

			// Set value to fieldsets
			self.$activeFieldset.addClass( 'inited usof-container' );
			self.activeFieldset.setValues( self.liveOptions || {}, /* quiet mode */true );

			// Initialization check and watch on field events
			for ( var fieldId in self.activeFieldset.fields ) {
				self.activeFieldset.fields[ fieldId ]
					.on( 'change', self._events.changeField )
					// Responsive screen change handler in the $usof.field
					.on( 'syncResponsiveState', ( _, screenName ) => {
						// Set a responsive screen from $usof the field
						if ( $usb.find( 'preview' ) ) {
							$usb.preview.fieldSetResponsiveScreen( screenName );
						}
					} );
			}
		},

		/**
		 * Destroy initialized fieldset
		 */
		_destroyFieldset: function() {
			const self = this;

			if ( ! self.selectedGroupId ) {
				return;
			}

			if ( self.$activeFieldset instanceof $ ) {
				self.$activeFieldset.remove();
			}

			self.$menu.addClass( 'hidden' );

			self.activeFieldset = null;
			self.selectedGroupId = null;
		},

		/**
		 * Field changes for a fieldsets
		 *
		 * @event handler
		 * @param {$usof.field|$usof.Group} usofField
		 * @param {*} _ The usofField value
		 */
		_changeField: function( usofField ) {
			const self = this;

			// If the param does not exist, then exit
			if ( $ush.isUndefined( self.liveOptions[ usofField.name ] ) ) {
				return;
			}

			const isGroup = usofField instanceof $usof.Group;
			const isField = usofField instanceof $usof.field;

			// If the object is not a field or a group then exit the method
			if ( ! ( isField || isGroup ) ) {
				return;
			}

			var // Get new param value
				value = usofField.getValue(),
				// Get field type
				fieldType = ( isField ? usofField.type : 'group' ),
				// Get usb-params from field or group
				usbParams = usofField[ isField ? '$row' : '$field' ].data( 'usb-params' ) || {},
				// The get and normalization of instructions
				instructions = $usb._normalizeInstructions( usbParams['usb_preview'] );

			// TODO: Fix from forced cast to data type as responsive fields are built on string format
			if ( fieldType == 'typography_options' ) {
				value = $ush.toPlainObject( value );
			}

			// If the value has not changed, then exit
			var oldValue = self.liveOptions[ usofField.name ];
			if ( $.isPlainObject( value ) ) {
				if ( $ush.comparePlainObject( oldValue, value ) ) {
					return;
				}
			} else if ( oldValue === value ) {
				return;
			}

			// Set new value
			self.liveOptions[ usofField.name ] = value;

			// Apply the changes to the preview page
			if ( instructions ) {
				// Get changed data
				var _changed = JSON.stringify( $usbcore.diffPlainObject( self.liveOptions, _$tmp.savedLiveOptions ) );

				$usb.postMessage( 'onPreviewChange', [
					instructions, // instructions to update previews
					value, // values of the field to be updated
					fieldType, // fieldType $usof.field type
					/* additional data */{
						changed: $ush.base64Encode( _changed ), // changed data in json+base64 format
						liveOptions: $ush.toString( self.liveOptions ) // live options
					}
				] );
			}

			// Switch for enable/disable save button
			$ush.debounce_fn_10ms( () => {
				$usb.panel.switchSaveButton( /* enable */self.isChanged() );
			} );
		},

		/**
		 * Save site settings changes
		 *
		 * @event handler
		 */
		_saveChanges: function() {
			const self = this;
			if (
				self.isProcessSave()
				|| ! self.isChanged()
				|| ! $usb.urlManager.hasParam( 'action', $usb.config( 'actions.site_settings' ) )
			) {
				return;
			}

			// Disable button and enable load
			$usb.panel.switchSaveButton( /* enable */true, /* isLoading */true );
			_$tmp.isProcessSave = true;

			// Send data to server
			$usb.ajax( /* request id */'_saveLiveOptions', {
				data: {
					_nonce: $usb.config( '_nonce' ),
					action: $usb.config( 'action_save_live_options' ),
					live_options: JSON.stringify( self.liveOptions )
				},
				success: ( res ) => {
					if ( ! res.success ) {
						return;
					}
					$usb.notify.add( $usb.getTextTranslation( 'site_settings_updated' ), NOTIFY_TYPE.SUCCESS );

					$usb.postMessage( 'clearCookies' );

					_$tmp.savedLiveOptions = $ush.clone( self.liveOptions );
				},
				complete: () => {
					_$tmp.isProcessSave = false;
					$usb.panel.switchSaveButton( /* enable */false );
				}
			} );
		}
	} );

	// Export API
	$usb.siteSettings = new SiteSettings( /* container */'#usb-site-settings' );

} ( jQuery );
