/* global awbPerformance, FusionPageBuilder, ajaxurl, fusionBuilderText */
window.awbWizard = {

	/**
	 * Run actions on load.
	 *
	 * @since 7.4
	 *
	 * @return {void}
	 */
	init: function() {
		this.$el        = jQuery( '.avada-dashboard' );
		this.activeStep = 1;
		this.$steps     = this.$el.find( '.awb-wizard-steps' );
		this.homeURL    = awbPerformance.homeURL;
		this.apiKey     = awbPerformance.apiKey;
		this.saveChange = awbPerformance.saveChange;
		this.accessible = this.checkSiteAccessible();
		this.assets     = new FusionPageBuilder.Assets();

		// Font variant notice.
		this.$vnote     = this.$el.find( '.variant-analysis' );
		this.$vcount    = this.$el.find( '.variant-count' );

		// Lighthouse data is turned on.
		this.lighthouse = {
			before: false,
			after: false
		};
		this.runTests   = 'none';

		// Listeners for events.
		this.addListeners();

		// Init typography fields, could be moved.
		this.fontsRendered = false;
		this.initFontOptions();
	},


	/**
	 * Init font family/variant select fields.
	 *
	 * @since 7.4
	 *
	 * @return {void}
	 */
	initFontOptions: function() {
		var self        = this,
			$fontFamily = this.$el.find( '.fusion-builder-font-family' );

		if ( _.isUndefined( self.assets ) || _.isUndefined( self.assets.webfonts ) ) {
			jQuery.when( self.assets.getWebFonts() ).done( function() {
				self.initAfterWebfontsLoaded( $fontFamily );
			} );
		} else {
			this.initAfterWebfontsLoaded( $fontFamily );
		}

		this.maybeShowPreload();
	},

	/**
	 * Handle dependency for preloading options.
	 *
	 * @since 7.4
	 *
	 * @return {void}
	 */
	maybeShowPreload: function() {
		var preloadVal = this.$el.find( 'input[name="preload_fonts"]' ).val();
		if ( 'all' === preloadVal || 'google_fonts' === preloadVal ) {
			this.$el.find( '[name="preload_fonts_variants"], [name="preload_fonts_subsets"]' ).closest( '.pyre_metabox_field' ).show();
		} else {
			this.$el.find( '[name="preload_fonts_variants"], [name="preload_fonts_subsets"]' ).closest( '.pyre_metabox_field' ).hide();
		}
	},

	/**
	 * Check if site is accessible.
	 *
	 * @since 7.4
	 *
	 * @return {void}
	 */
	checkSiteAccessible: function() {
		if ( this.homeURL.includes( 'localhost' ) ) {
			return false;
		}
		if ( ! this.homeURL.includes( '//' ) ) {
			return false;
		}
		return true;
	},

	/**
	 * Change which step is active.
	 *
	 * @since 7.4
	 *
	 * @return {void}
	 */
	changeStep: function( step ) {
		var $step = this.$el.find( '.awb-wizard-section[data-step="' + step + '"]' );

		if ( $step.length ) {

			// If this is the final step.
			if ( 'finish' === $step.attr( 'data-id' ) ) {
				this.finishSteps();
				this.maybeFinishGPS();
			}

			$step.removeClass( 'hidden' );
			this.$el.find( '.awb-wizard-section:not([data-step="' + step + '"])' ).addClass( 'hidden' );
			this.activeStep = parseInt( step );

			this.$steps.find( '.completed' ).removeClass( 'completed' );
			this.$steps.find( 'li:not([data-id="' + step + '"])' ).removeClass( 'active' );
			this.$steps.find( '[data-id="' + step + '"]' ).addClass( 'active' ).prevAll().addClass( 'completed' );

			this.scrollTo();
		}
	},

	/**
	 * Perform finishing steps, cache, homepage load and test if desired.
	 *
	 * @since 7.4
	 *
	 * @return {void}
	 */
	finishSteps: function() {
		var self          = this,
			$progress     = this.$el.find( '.finish-progress' ),
			$heading      = $progress.find( '.avada-db-card-notice-heading' ),
			$improvements = this.$el.find( '.awb-possible-improvements' );

		$progress.addClass( 'fetching' );

		// Clear cache and maybe run lighthouse.
		this.clearCache().done( function() {
			$heading.html( awbPerformance.loadingHome );

			jQuery.ajax( {
				type: 'GET',
				url: self.homeURL
			} )
			.done( function() {
				if ( ! self.maybeFinishGPS() ) {
					$progress.removeClass( 'fetching' ).addClass( 'success' );
					$heading.html( awbPerformance.wizardComplete );
				} else {
					$heading.html( awbPerformance.performLighthouse );
				}
			} )
			.fail( function() {
				$progress.removeClass( 'fetching' );
				self.addTemporaryClass( $progress, 'error' );
				$heading.html( awbPerformance.errorLoadingPage );
			} );
		} ).fail( function() {
			$progress.removeClass( 'fetching' );
			self.addTemporaryClass( $progress, 'error' );
			$heading.html( awbPerformance.errorClearingCache );
		} );

		// Check if there are any recommendations to highlight.
		if ( this.$el.find( '.pyre_metabox_field.value-bad' ).length ) {
			$improvements.html( '' ).closest( '.awb-recommendation-holder' ).addClass( 'show-recommendations' );
			this.$el.find( '.pyre_metabox_field.value-bad' ).each( function() {
				var $element = jQuery( this );
				$improvements.append( '<li><strong>' + $element.find( 'label' ).text() + '</strong> - ' + $element.next( '.wizard-recommendation' ).text() + '</li>' );
			} );
		}
	},

	/**
	 * Clear the cache.
	 *
	 * @since 7.4
	 *
	 * @return {void}
	 */
	clearCache: function() {
		return jQuery.ajax( {
			type: 'POST',
			url: ajaxurl,
			data: {
				action: 'awb_performance_cache',
				awb_performance_nonce: jQuery( '#awb-performance-nonce' ).val()
			}
		} );
	},

	/**
	 * Scroll to top when step changed.
	 *
	 * @since 7.4
	 *
	 * @return {void}
	 */
	scrollTo: function() {
		jQuery( 'html, body' ).animate( {
			scrollTop: 0
		}, 300 );
	},

	/**
	 * Check which test to run.
	 *
	 * @since 7.4
	 *
	 * @return {void}
	 */
	checkTest: function() {
		if ( ! awbPerformance.lighthouse ) {
			return 'none';
		}
		return this.$el.find( '#awb_wizard_tests' ).length ? this.$el.find( '#awb_wizard_tests' ).val() : 'none';
	},

	/**
	 * Check if we should start a before lighthouse test and run it.
	 *
	 * @since 7.4
	 *
	 * @return {void}
	 */
	maybeStartGPS: function() {
		this.runTests = this.checkTest();

		// If user wants testing and nothing retrieved already, run test in  background.
		if ( 'none' !== this.runTests && false === this.lighthouse.before ) {
			this.runPageSpeed( 'before' );
		}
	},

	/**
	 * Check if we should start an after lighthouse test and run it.
	 *
	 * @since 7.4
	 *
	 * @return {void}
	 */
	maybeFinishGPS: function() {
		// If user wants testing and nothing retrieved already, run test in  background.
		if ( 'none' !== this.runTests && false === this.lighthouse.after ) {

			// TODO: clear cache (always), then if testing score load page first to ensure CSS & JS generated.
			this.runPageSpeed( 'after' );

			this.$el.find( '.awb-wizard-score-holder' ).removeClass( 'hidden' );
			return true;
		}

		this.$el.find( '.awb-wizard-score-holder' ).addClass( 'hidden' );
		return false;
	},

	/**
	 * Add needed listeners.
	 *
	 * @since 7.4
	 *
	 * @return {void}
	 */
	addListeners: function() {
		var self = this;

		// Listen for step change click.
		this.$el.find( '.awb-wizard-link' ).on( 'click', function( event ) {
			var target      = jQuery( this ).attr( 'data-id' ),
				$activeStep = self.$el.find( '.awb-wizard-section[data-step="' + self.activeStep + '"]' );

			event.preventDefault();

			// Check if active step has unsaved changes.
			if ( '1' === $activeStep.attr( 'data-save' ) ) {
				if ( ! window.confirm( self.saveChange ) ) { // eslint-disable-line no-alert
					return;
				}
			}

			if ( target ) {
				self.changeStep( target );
			}

			// If its the starting step.
			if ( jQuery( this ).hasClass( 'awb-get-started' ) ) {
				self.maybeStartGPS();
			}
		} );

		// Listen for test radio change and set to el to control what is shown on final step.
		this.$el.find( '#awb_wizard_tests' ).on( 'change', function() {
			self.$el.attr( 'data-test', jQuery( this ).val() );
		} );

		// Listen for option change and enable save.
		this.$el.find( '.button-set-value:not( #awb_wizard_tests ), select, input[type="checkbox"], .fusion-slider-input' ).on( 'change', function() {
			var $field      = jQuery( this ).closest( '.pyre_metabox_field' ),
				value       = jQuery( this ).val(),
				recommended = $field.attr( 'data-recommendation' );

			// If its peload fonts changing, update the dependencies.
			if ( 'preload_fonts' === jQuery( this ).attr( 'name' ) ) {
				self.maybeShowPreload();
			}

			// If its an initial font option render, don't trigger save state.
			if ( 4 == parseInt( jQuery( this ).closest( '.awb-wizard-section' ).attr( 'data-step' ) ) && ! self.fontsRendered ) {
				return;
			}

			// Check if it has recommendation and compare.
			if ( recommended ) {
				if ( 'object' === typeof value && 'function' === typeof value.join ) {
					value = value.join( ',' );
				}
				if ( recommended === value ) {
					$field.removeClass( 'value-bad' ).addClass( 'value-good' );
				} else {
					$field.removeClass( 'value-good' ).addClass( 'value-bad' );
				}
			}

			// Set step change to requiring a save.
			jQuery( this ).closest( '.awb-wizard-section' ).attr( 'data-save', '1' );
		} );

		// Listen stage save click.
		this.$el.find( '.awb-wizard-save' ).on( 'click', function( event ) {
			var $button  = jQuery( this ),
				saveData = {},
				$step    = $button.closest( '.awb-wizard-section' );

			// Get the data we want to save.
			if ( 'elements' === $button.attr( 'data-id' ) ) {
				saveData = [];
				$step.find( ':checkbox:checked' ).each( function() {
					saveData.push( jQuery( this ).val() );
				} );
			} else {
				$step.find( '.pyre_field input, .pyre_field select' ).each( function() {
					var value = jQuery( this ).val(),
						name  = jQuery( this ).attr( 'name' );

					if ( 'string' !== typeof name ) {
						return;
					}

					if ( 'object' === typeof value && ! value.length ) {
						value = [ '' ];
					}

					// Empty extra is pointless, remove it.
					name = name.replace( '[]', '' );

					// If its an array subvalue.
					if ( name.includes( '[' ) ) {
						name = name.split( '[' );
						if ( 'object' !== typeof saveData[ name[ 0 ] ] ) {
							saveData[ name[ 0 ] ] = {};
						}
						saveData[ name[ 0 ] ][ name[ 1 ].replace( ']', '' ) ] = value;
						return;
					}

					// Not a sub value.
					saveData[ name ] = value;
				} );
			}

			event.preventDefault();

			if ( $button.hasClass( 'disabled' ) ) {
				return;
			}

			$button.addClass( 'disabled saving' );

			jQuery.ajax( {
				type: 'POST',
				url: ajaxurl,
				dataType: 'json',
				data: {
					action: 'awb_performance_save',
					save_data: saveData,
					step: $button.attr( 'data-id' ),
					awb_performance_nonce: jQuery( '#awb-performance-nonce' ).val()
				}
			} )
			.done( function( response ) {
				$button.trigger( 'blur' );

				if ( 'object' !== typeof response ) {
					$button.removeClass( 'disabled saving' );
					self.addTemporaryClass( $button, 'error' );
					return;
				}

				if ( ! response.success ) {
					self.displayError( response.data );
					$button.removeClass( 'disabled saving' );
					self.addTemporaryClass( $button, 'error' );
					return;
				}

				$button.removeClass( 'disabled saving' );
				self.addTemporaryClass( $button, 'success' );
				$button.closest( '.awb-wizard-section' ).attr( 'data-save', '0' );
			} )
			.fail( function() {
				$button.removeClass( 'disabled saving' );
				self.addTemporaryClass( $button, 'error' );
			} );
		} );

		// Check all elements.
		this.$el.find( '.awb-wizard-checkall' ).on( 'click', function( event ) {
			event.preventDefault();
			self.$el.find( '.fusion-builder-option-field input' ).prop( 'checked', true );
			jQuery( this ).closest( '.awb-wizard-section' ).attr( 'data-save', '1' );
		} );

		// Uncheck all elements.
		this.$el.find( '.awb-wizard-uncheckall' ).on( 'click', function( event ) {
			event.preventDefault();
			self.$el.find( '.fusion-builder-option-field input' ).prop( 'checked', false );
			jQuery( this ).closest( '.awb-wizard-section' ).attr( 'data-save', '1' );
		} );

		// Apply recommendations
		this.$el.find( '.awb-wizard-apply' ).on( 'click', function( event ) {
			event.preventDefault();
			self.applyRecommendations();
			self.addTemporaryClass( jQuery( this ), 'success' );
		} );

		// Listen for scan button clicks.
		this.$el.find( '.awb-wizard-scan-button' ).on( 'click', function( event ) {
			var $button  = jQuery( this ),
				scanType = $button.attr( 'data-id' );

			event.preventDefault();

			if ( $button.hasClass( 'disabled' ) ) {
				return;
			}

			$button.addClass( 'disabled saving' );

			if ( 'fonts' === scanType || 'optimize' === scanType ) {
				$button.removeClass( 'disabled saving' ).trigger( 'blur' );
				$button.closest( '.awb-wizard-section' ).addClass( 'show-recommendations' );
				self.updateRecommendations();
				self.addTemporaryClass( $button, 'success' );
				return;
			}

			console.log( 'Scanning for ' +  scanType );
			jQuery.ajax( {
				type: 'GET',
				url: ajaxurl,
				dataType: 'json',
				data: {
					action: 'awb_performance_scan',
					scan_type: scanType,
					awb_performance_nonce: jQuery( '#awb-performance-nonce' ).val()
				}
			} )
			.done( function( response ) {
				$button.trigger( 'blur' );
				$button.closest( '.awb-wizard-section' ).addClass( 'show-recommendations' );

				if ( 'object' !== typeof response ) {
					$button.removeClass( 'disabled saving' );
					self.addTemporaryClass( $button, 'error' );
					return;
				}

				if ( ! response.success ) {
					self.displayError( response.data );
					$button.removeClass( 'disabled saving' );
					self.addTemporaryClass( $button, 'error' );
					return;
				}

				if ( 'icons' === scanType ) {
					self.updateIcons( response.data.markup );
					self.updateRecommendations( response.data.recommendations );
				} else if ( 'features' === scanType ) {
					self.updateRecommendations( response.data );
				} else if ( 'elements' === scanType ) {
					self.updateElements( response.data );
				}
				$button.removeClass( 'disabled saving' );
				self.addTemporaryClass( $button, 'success' );
			} )
			.fail( function() {
				$button.removeClass( 'disabled saving' );
				self.addTemporaryClass( $button, 'error' );

				if ( 'icons' === scanType || 'elements' === scanType ) {
					alert( awbPerformance.scanError ); // eslint-disable-line no-alert
				}
			} );
		} );

	},

	/**
	 * Uncheck elements not being used.
	 *
	 * @since 7.4
	 *
	 * @return {void}
	 */
	updateElements: function( elements ) {
		var $checkboxes = this.$el.find( '.fusion-builder-option-field input' ),
			self        = this;

		if ( 'object' === typeof elements ) {

			// Check all first.
			$checkboxes.prop( 'checked', true );

			// Disable for each unused.
			jQuery.each( elements, function( element, disable ) { // eslint-disable-line no-unused-vars
				var $checkbox = self.$el.find( 'input[value="' + element + '"]' );
				if ( ! $checkbox.closest( 'li' ).hasClass( 'hidden' ) ) {
					$checkbox.prop( 'checked', false );
				}
			} );

			this.$el.find( '.awb-wizard-section[data-step="' + this.activeStep + '"]' ).attr( 'data-save', '1' );
		}
	},

	/**
	 * Update icon scan markup.
	 *
	 * @since 7.4
	 *
	 * @return {void}
	 */
	updateRecommendations: function( data ) {
		var self        = this,
			$activeStep = this.$el.find( '.awb-wizard-section[data-step="' + this.activeStep + '"]' );

		// Add dynamic recommendation text.
		if ( 'object' === typeof data ) {
			jQuery.each( data, function( optionID, optionData ) {
				var $element = self.$el.find( '[name="' + optionID + '"]' ).closest( '.pyre_metabox_field' ),
					dynamic  = 'undefined' === typeof optionData.dynamic ? false : optionData.dynamic;

				if ( $element.length ) {
					$element.next( '.wizard-recommendation' ).remove();

					if ( 'string' === typeof optionData.message && '' !== optionData.message ) {
						$element.after( '<p class="wizard-recommendation"><i class="fusiona-af-rating"></i> ' + optionData.message + '</p>' );
					}
					if ( dynamic && 'undefined' !== typeof optionData.value ) {
						$element.attr( 'data-recommendation', optionData.value );
					}
				}
			} );
		}

		// Loop inputs and change value/add coloring.
		$activeStep.find( '.pyre_metabox_field' ).each( function() {
			var $element    = jQuery( this ),
				recommended = $element.attr( 'data-recommendation' ),
				$input      = $element.find( 'input, select' ),
				value       = $input.val();

			if ( ! recommended ) {
				return;
			}

			if ( 'object' === typeof value && 'function' === typeof value.join ) {
				value = value.join( ',' );
			}

			if ( recommended === value ) {
				$element.addClass( 'value-good' ).removeClass( 'value-bad' );
			} else {
				$element.addClass( 'value-bad' ).removeClass( 'value-good' );
			}
		} );

		// Make apply all button visibile.
		$activeStep.addClass( 'show-apply-all' );
	},

	/**
	 * Apply recommendations on current step.
	 *
	 * @since 7.4
	 *
	 * @return {void}
	 */
	applyRecommendations: function() {
		var $activeStep = this.$el.find( '.awb-wizard-section[data-step="' + this.activeStep + '"]' );

		$activeStep.find( '.pyre_metabox_field' ).each( function() {
			var $element    = jQuery( this ),
				recommended = $element.attr( 'data-recommendation' ),
				$input      = $element.find( 'input, select' ),
				value       = $input.length ? $input.val() : null;

			// Allow empty recommended value for FA select field.
			if ( ! recommended && 'status_fontawesome' !== $input.attr( 'name' ) ) {
				return;
			}

			if ( $element.find( 'select[multiple]' ).length && 'string' === typeof recommended ) {
				recommended = recommended.split( ',' );
				$element.find( 'select[multiple]' ).val( recommended ).trigger( 'change' );
				return;
			}

			if ( null !== value && recommended === value ) {
				return;
			}

			$element.find( '.buttonset-item.ui-state-active' ).removeClass( 'ui-state-active' );
			$element.find( '.buttonset-item[data-value="' + recommended + '"]' ).addClass( 'ui-state-active' );
			$element.find( 'input, select' ).val( recommended ).trigger( 'change' );
		} );

		// Hide button, no longer needed.
		setTimeout( function() {
			$activeStep.removeClass( 'show-apply-all' );
		}, 2000 );
	},

	/**
	 * Update icon scan markup.
	 *
	 * @since 7.4
	 *
	 * @return {void}
	 */
	updateIcons: function( data ) {

		if ( '' !== data ) {
			this.$el.find( '#fusion-used-icons-table' ).removeClass( 'hidden' );
			this.$el.find( '#fusion-used-icons-table tbody' ).html( data );
		}
	},

	/**
	 * Add a class, wait and then remove.
	 *
	 * @since 7.4
	 *
	 * @return {void}
	 */
	addTemporaryClass: function( $element, classname ) {
		$element.addClass( classname );
		setTimeout( function() {
			$element.removeClass( classname );
		}, 2000 );
	},

	/**
	 * Display an error message to user.
	 *
	 * @since 7.4
	 *
	 * @return {void}
	 */
	displayError: function( message ) {
		window.alert( message ); // eslint-disable-line no-alert
	},

	/**
	 * Run GPS test.
	 *
	 * @since 7.4
	 *
	 * @return {void}
	 */
	runPageSpeed: function( status ) {
		var self       = this,
			api        = 'https://www.googleapis.com/pagespeedonline/v5/runPagespeed',
			parameters = {
				url: encodeURIComponent( this.homeURL ),
				category: 'PERFORMANCE',
				strategy: this.runTests.toUpperCase()
			},
			query     = api;

		status = 'undefined' === typeof status ? 'before' : status;

		// Site is not accessible, don't try.
		if ( ! this.accessible || 'none' === this.runTests ) {
			self.$el.find( '.awb-wizard-score-holder' ).addClass( 'hidden' );
			return;
		}

		console.log( 'Running Test ', this.runTests );

		// Which type of analysis to run.
		query += '?url=' + parameters.url;
		query += '&category=' + parameters.category;
		query += '&strategy=' + parameters.strategy;

		if ( this.apiKey ) {
			query += '&key=' + this.apiKey;
		}

		// Fetch results.
		fetch( query ).then( ( response ) => response.json() ).then( ( json ) => {
			var lighthouse, lighthouseMetrics;

			// Hide message saying it is fetching scores.
			if ( 'after' === status ) {
				self.$el.find( '.finish-progress' ).removeClass( 'fetching' ).addClass( 'success' ).find( '.avada-db-card-notice-heading' ).html( awbPerformance.wizardComplete );
			}

			// Results failed in some way, set it so no further tests run.
			if ( 'object' !== typeof json || 'undefined' === typeof json.lighthouseResult ) {
				console.log( 'Lighthouse failed' );
				self.$el.find( '.awb-wizard-score-holder' ).addClass( 'hidden' );
				self.accessible = false;
				return;
			}

			lighthouse        = json.lighthouseResult;

			console.log( 'lighthouse' );

			lighthouseMetrics = {
				score: lighthouse.categories.performance.score * 100,
				fcp: lighthouse.audits[ 'first-contentful-paint' ].score * 100,
				lcp: lighthouse.audits[ 'largest-contentful-paint' ].score * 100,
				cls: lighthouse.audits[ 'cumulative-layout-shift' ].score * 100,
				fid: lighthouse.audits[ 'max-potential-fid' ].score * 100
			};

			// Store data with status.
			self[ status ] = lighthouseMetrics;

			console.log( 'Lighthouse completed', lighthouseMetrics );

			// Update actual markup
			jQuery.each( lighthouseMetrics, function( metricId, metricValue ) {
				var $score     = self.$el.find( '.awb-score-' + status + '[data-type="' + metricId + '"]' ),
					scoreClass = 'good';

				if ( $score.length ) {
					if ( 50 > metricValue ) {
						scoreClass = 'poor';
					} else if ( 90 > metricValue ) {
						scoreClass = 'okay';
					}

					$score.removeClass( 'score-good score-okay score-poor score-fetching' ).addClass( 'score-' + scoreClass ).attr( 'data-score', metricValue );
					$score.find( '.lh-gauge__percentage' ).html( Math.round( metricValue ) );
					$score.find( '.lh-gauge-arc' ).css( 'stroke-dasharray',  ( 360 * metricValue / 100 ) + ', 360' );
				}
			} );
		} );
	},

	/**
	 * Create the data for font family and render select field.
	 *
	 * @since 2.2
	 * @param {object} $fontFamily - The option jQuery elements.
	 * @return {Void}
	 */
	initAfterWebfontsLoaded: function( $fontFamily ) {
		var self          = this,
			fonts         = this.assets.webfonts,
			standardFonts = [],
			googleFonts   = [],
			customFonts   = [],
			data          = [];

		data.push( {
			id: '',
			text: fusionBuilderText.typography_default
		} );

		// Format standard fonts as an array.
		if ( ! _.isUndefined( fonts.standard ) ) {
			_.each( fonts.standard, function( font ) {
				standardFonts.push( {
					id: font.family.replace( /&quot;/g, '&#39' ),
					text: font.label
				} );
			} );
		}

		// Format google fonts as an array.
		if ( ! _.isUndefined( fonts.google ) ) {
			_.each( fonts.google, function( font ) {
				googleFonts.push( {
					id: font.family,
					text: font.label
				} );
			} );
		}

		// Format custom fonts as an array.
		if ( ! _.isUndefined( fonts.custom ) ) {
			_.each( fonts.custom, function( font ) {
				if ( font.family && '' !== font.family ) {
					customFonts.push( {
						id: font.family.replace( /&quot;/g, '&#39' ),
						text: font.label
					} );
				}
			} );
		}

		// Combine forces and build the final data.
		if ( customFonts[ 0 ] ) {
			data.push( { text: 'Custom Fonts', children: customFonts } );
		}
		data.push( { text: 'Standard Fonts', children: standardFonts } );
		data.push( { text: 'Google Fonts',   children: googleFonts } );

		$fontFamily.each( function() {
			var $familyInput  = jQuery( this ).find( '.input-font_family' );

			$familyInput.select2( {
				data: data
			} );

			jQuery( this ).find( '.font-family' ).addClass( 'loaded' );

			$familyInput.val( $familyInput.data( 'value' ) ).trigger( 'change' );

			self.renderVariant( jQuery( this ) );

			$familyInput.on( 'change', function() {
				var $wrapper = jQuery( this ).closest( '.fusion-builder-font-family' );

				self.renderVariant( $wrapper );
				self.updateVariantCount();
			} );
		} );

		this.$el.on( 'change', '.input-variant', function() {
			self.updateVariantCount();
		} );

		this.updateVariantCount();
		this.fontsRendered = true;
	},

	/**
	 * Count unique variants.
	 *
	 * @since 7.4
	 * @return {Void}
	 */
	updateVariantCount: function() {
		var variants      = {},
			variantLength = 0;

		this.$el.find( '.fusion-builder-font-family' ).each( function() {
			var $family  = jQuery( this ).find( '.input-font_family' ),
				$variant = jQuery( this ).find( '.input-variant' ),
				family   = $family.val(),
				variant  = $variant.val(),
				search   = '';

			if ( null === family ) {
				family = $family.attr( 'data-default' );
			}

			if ( null === variant ) {
				variant = $variant.attr( 'data-default' );
			}

			search = family + variant;

			if ( 'undefined' !== typeof variants[ search ] ) {
				return;
			}

			variants[ search ] = true;
		} );

		variantLength = Object.keys( variants ).length;
		this.$vnote.attr( 'data-count', variantLength );
		this.$vcount.html( variantLength );
	},

	/**
	 * Render variant select with relevant choices and hide if should not be shwon.
	 *
	 * @since 2.2
	 * @param {object} $fontOption - The option jQuery element.
	 * @return {Void}
	 */
	renderVariant: function( $fontOption ) {
		var data          = [],
			fontFamily    = $fontOption.find( '.input-font_family' ).val(),
			variants      = this.getVariants( fontFamily ),
			$input        = $fontOption.find( '.input-variant' ),
			value         = 'undefined' !== typeof $input.attr( 'data-value' ) ? $input.attr( 'data-value' ).toString() : false,
			valueExists   = false,
			defaultVal    = $input.attr( 'data-default' ),
			defaultExists = false;

		if ( $input.hasClass( 'select2-hidden-accessible' ) ) {
			value = null !== $input.val() ? $input.val() : value;
			$input.select2( 'destroy' ).empty();
		}

		_.each( variants, function( scopedVariant ) {

			if ( scopedVariant.id && 'italic' === scopedVariant.id ) {
				scopedVariant.id = '400italic';
			}
			if ( 'function' === typeof scopedVariant.id.toString && scopedVariant.id.toString() === value ) {
				valueExists = true;
			}

			if ( 'function' === typeof scopedVariant.id.toString && 'function' === typeof defaultVal.toString && scopedVariant.id.toString() === defaultVal.toString() ) {
				defaultExists = true;
			}

			data.push( {
				id: scopedVariant.id,
				text: scopedVariant.label
			} );
		} );

		$input.select2( {
			data: data
		} );

		// if no value exists, set to default, otherwise use first.
		if ( ! valueExists && defaultExists ) {
			value = defaultVal;
		} else if ( ! valueExists && 'object' === typeof variants[ 0 ] ) {
			value = variants[ 0 ].id;
		}

		$input.val( value ).trigger( 'change' );
	},

	/**
	 * Get variants for a font-family.
	 *
	 * @since 2.2
	 * @param {string} fontFamily - The font-family name.
	 * @return {Object} - Returns the variants for the selected font-family.
	 */
	getVariants: function( fontFamily ) {
		var variants = false;

		if ( this.isCustomFont( fontFamily ) ) {
			return [
				{
					id: '400',
					label: 'Normal 400'
				}
			];
		}

		_.each( this.assets.webfonts.standard, function( font ) {
			if ( fontFamily && font.family === fontFamily ) {
				variants = font.variants;
				return font.variants;
			}
		} );

		_.each( this.assets.webfonts.google, function( font ) {
			if ( font.family === fontFamily ) {
				variants = font.variants;
				return font.variants;
			}
		} );
		return variants;
	},

	/**
	 * Check if a font-family is a custom font or not.
	 *
	 * @since 2.2
	 * @param {string} family - The font-family to check.
	 * @return {boolean} - Whether the font-family is a custom font or not.
	 */
	isCustomFont: function( family ) {
		var isCustom = false;

		// Figure out if this is a google-font.
		_.each( this.assets.webfonts.custom, function( font ) {
			if ( font.family === family ) {
				isCustom = true;
			}
		} );

		return isCustom;
	}
};

( function( jQuery ) {

	'use strict';

	jQuery( document ).ready( function() {
		window.awbWizard.init();
	} );
}( jQuery ) );
