( function ( $ ) {
	'use strict';

	window.vc.AiFormView = Backbone.View.extend({
		events: {
			'click .vc_ai-generate-button': 'generateContent',
			'change [name="contentType"]': 'changeContentType',
			'input [name="prompt"]': 'changePrompt',
			'click [data-vc-ui-element="button-save"]': 'insertContent',
			'click .wpb-copy-output': 'copyContent'
		},
		seconds: 0,
		minutes: 0,
		timerInterval: null,
		isGenerating: false,
		maxWaitingCacheInterval: 900000,
		maxPromptLength: 2000,
		initialize: function ( options ) {
			this.toggleModalPromoClass( options.data.type );
			this.$el.find( '.vc_ui-helper-modal-ai-preloader' ).after( options.data.content );
			this.setFormElements();
			$( '.edit-form-info' ).initializeTooltips();

		},
		render: function ( options ) {
			if ( this.timerInterval ) {
				this.clearTimer();
			}
			this.toggleModalPromoClass( options.type );
			this.$form.after( options.content );
			this.$form.remove();
			this.setFormElements();
			$( '.edit-form-info' ).initializeTooltips();
			return this;
		},
		setFormElements: function () {
			this.$form = this.$el.find( '.vc_ui-panel-content-container' );
			this.$generate_button = this.$el.find( '.vc_ai-generate-button' );
			this.$close_button = this.$el.find( '[data-vc-ui-element="button-close"]' );
			this.$insert_button = this.$el.find( '[data-vc-ui-element="button-save"]' );
			this.$generated_content = this.$el.find( '.wpb_ai-generated-content' );
			this.$prompt_field = this.$el.find( '[name="prompt"]' );
			this.$generate_placeholder = this.$el.find( '.vc_ui-helper-modal-ai-placeholder' );
			this.$generate_placeholder_timer = this.$generate_placeholder.find( '.vc_ai-timer' );
			this.initialButtonText = this.$generate_button.text().trim();
			this.contentType = this.$el.find( '[name="contentType"]' ).val();

			if ( 'new_content' === this.contentType && !this.$prompt_field.val().trim() ) {
				this.disableButton();
			}
		},
		generateContent: function ( e ) {
			e.preventDefault();
			var _this = this;
			// trim the prompt field value if user has set it programmatically
			var promptWords = this.$prompt_field.val().split( ' ' );
			if ( this.maxPromptLength < promptWords.length ) {
				this.$prompt_field.val( promptWords.slice( 0, this.maxPromptLength ).join( ' ' ) );
			}

			var init_data = this.$form.find( ':visible:not([style*="display: none"]), [name="prompt"], input[type="hidden"]' ).serializeArray();
			var cache_id = this.getUniqueCacheId();
			init_data.push({ name: 'cacheId', value: cache_id });
			this.$generated_content.val( '' );

			var data = {
				action: 'wpb_ai_api_get_response',
				data: init_data,
				_vcnonce: window.vcAdminNonce
			};

			this.isGenerating = true;
			this.$generate_placeholder.removeClass( 'vc_ui-hidden' );
			this.$generate_placeholder.addClass( 'wpb-generating-content' );
			this.timerInterval = setInterval( this.updateTimer.bind( this ), 1000 );

			// we break first request if you do not get response in 20 seconds.
			// if we do not get response in 20 seconds then we save response data in cache on the remote server
			// and then process another bunch of ajax requests to check if cache is ready
			$.ajax({
				type: 'POST',
				url: window.ajaxurl,
				timeout: 20000,
				data: data
			}).done( function ( response ) {
				if ( !_this.isGenerating ) {
					return false;
				}
				if ( true === response.success ) {
					_this.$generated_content.val( response.data );
					_this.resetButton( true );
					_this.$insert_button.show();
					_this.tokenUsageUpdate();
					_this.toggleCopyButton();
				} else {
					// error returned by wpbakery server api
					if ( response && response.data && response.data[0] && response.data[0].code && response.data[0].message ) {
						console.error( response.data[0].code, response.data[0].message );
						_this.resetButton( false );
						var message = response.data[0].message.replace( /\\/g, '' );
						_this.showErrorMessage( message );
					} else {
						console.error( _this.getLocale().ai_response_error );
						_this.resetButton( false );
						_this.showErrorMessage( _this.getLocale().ai_response_error );
					}
				}
			}).fail( function ( response ) {
				if ( !_this.isGenerating ) {
					return false;
				}
				if ( response && !response.statusText ) {
					console.error( _this.getLocale().ai_response_error );
					_this.resetButton( false );
					_this.showErrorMessage( _this.getLocale().ai_response_error );
					return;
				}

				if ( 'timeout' !== response.statusText ) {
					console.error( _this.getLocale().ai_response_error );
					_this.resetButton( false );
					_this.showErrorMessage( _this.getLocale().ai_response_error );
					return;
				}

				var data = {
					action: 'wpb_ai_generate_content_check_cache',
					data: {
						type: 'generate-text',
						messaged_data: true,
						cacheId: cache_id
					},
					_vcnonce: window.vcAdminNonce
				};

				// we create a timer to check if cache is ready every 10 seconds for 5 minutes
				var timeouts = [];
				for ( var time_interval = 10000; time_interval <= _this.maxWaitingCacheInterval; time_interval += 10000 ) {
					createTimeout( time_interval );
				}

				function createTimeout ( interval ) {
					timeouts.push( setTimeout( function () {
						var output_value = _this.$generated_content.val();

						if ( output_value ) {
							// stop all other timeouts related to cache checking
							for ( var i = 0; i < timeouts.length; i++ ) {
								if ( 'stop_cache_timeouts' === output_value ) {
									_this.$generated_content.val( '' );
								}
								clearTimeout( timeouts[i]);
							}
						} else {
							_this.processCachedRequest( _this, data, interval );
						}
					}, interval ) );
				}

			});
		},

		processCachedRequest: function ( _this, data, time_interval ) {
			// last timer is pass then it mean that we still do not have a content
			if ( this.maxWaitingCacheInterval === time_interval ) {
				console.error( _this.getLocale().ai_response_error );
				_this.resetButton( false );
				_this.showErrorMessage( _this.getLocale().ai_response_error );
			} else {
				// any other timer then last we process request to check cache
				$.ajax({
					type: 'POST',
					url: window.ajaxurl,
					timeout: 10000,
					data: data
				}).done( function ( response ) {
					if ( !_this.isGenerating ) {
						return false;
					}
					if ( true === response.success && response.data && 'cache_in_process' !== response.data ) {
						_this.$generated_content.val( response.data );
						_this.resetButton( true );
						_this.$insert_button.show();
					}
					// we have a cache response but first request that created cache was failed
					if ( false === response.success && response && response.data && response.data[0] && response.data[0].code && response.data[0].message ) {
						_this.$generated_content.val( 'stop_cache_timeouts' );
						_this.resetButton( false );
						var message = response.data[0].message.replace( /\\/g, '' );
						_this.showErrorMessage( message );
					}
				});
			}
		},

		tokenUsageUpdate: function () {
			var data = {
				action: 'wpb_ai_get_token_usage',
				data: {},
				_vcnonce: window.vcAdminNonce
			};
			var _this = this;

			$.ajax({
				type: 'POST',
				url: window.ajaxurl,
				data: data
			}).done( function ( response ) {
				var is_token_text = undefined !== response.data.tokens_left && undefined !== response.data.tokens_total;
				if ( true === response.success && is_token_text ) {
					var token_usage_text =
						_this.getLocale().ai_credit_usage + response.data.tokens_left + ' / ' + response.data.tokens_total;
					$( '.vc-ai-tokens-usage' ).text( token_usage_text );
				} else {
					// error returned by wpbakery server api
					var is_error_message =
						response &&
						response.data &&
						response.data[0] &&
						response.data[0].code &&
						response.data[0].message;

					if ( is_error_message ) {
						console.error( response.data[0].message );
						_this.showErrorMessage( response.data[0].message );
					} else {
						console.error( _this.getLocale().ai_response_error );
						_this.showErrorMessage( _this.getLocale().ai_response_error );
					}
				}
			}).fail( function () {
				console.error( _this.getLocale().ai_response_error );
				_this.resetButton();
				_this.showErrorMessage( _this.getLocale().ai_response_error );
			});
		},

		getUniqueCacheId: function () {
			return Date.now().toString( 36 ) + Math.random().toString( 36 ).slice( 2 );
		},

		disableButton: function () {
			this.$generate_button.prop( 'disabled', function ( _, val ) {
				return !val;
			});
			this.isGenerateDisabled = true;
		},

		resetButton: function ( isGenerated ) {
			var buttonText = isGenerated ? 'Regenerate' : this.initialButtonText;
			this.$generate_button.removeAttr( 'disabled style' );
			this.$generate_button.text( buttonText );
			this.$generate_button.blur();
			this.clearTimer();
		},

		clearTimer: function () {
			this.$generate_placeholder.addClass( 'vc_ui-hidden' );
			this.$generate_placeholder.removeClass( 'wpb-generating-content' );
			this.$generate_placeholder_timer.text( '00:00' );
			clearInterval( this.timerInterval );
			this.seconds = 0;
			this.minutes = 0;
			this.isGenerating = false;
		},

		updateTimer: function () {
			this.seconds++;

			if ( 60 === this.seconds ) {
				this.seconds = 0;
				this.minutes++;
			}

			var formattedMinutes = String( this.minutes ).padStart( 2, '0' );
			var formattedSeconds = String( this.seconds ).padStart( 2, '0' );

			this.$generate_placeholder_timer.text( formattedMinutes + ':' + formattedSeconds );
		},

		changeContentType: function ( e ) {
			this.contentType = e.target.value;
			var elementData = this.$el.data();

			// hide form fields that do not match selected content type
			var formFieldOptionalityList = $( e.target ).find( 'option:selected' ).attr( 'data-form-fields-optionality' );
			formFieldOptionalityList = formFieldOptionalityList ? formFieldOptionalityList.split( '|' ) : [];
			this.hideFormFields( formFieldOptionalityList );

			// Set all form fields to default values except content type
			this.$form.trigger( 'reset' );
			this.$form.find( '[name="contentType"]' ).val( this.contentType );

			// set the value of the "prompt" textarea with existing field value
			if ( 'improve_existing' === e.target.value || 'translate' === e.target.value ) {
				this.$generate_button.text( this.getLocale().regenerate );
				var existingContent = elementData.element.val();
				if ( 'textarea_raw_html' === elementData.param_type || 'textarea_ace' === elementData.param_type ) {
					existingContent = rawurldecode( base64_decode( existingContent.trim() ) );
				} else if ( 'textarea_html' === elementData.param_type ) {
					existingContent = window.tinymce.get( elementData.element.attr( 'id' ) ).getContent();
				}
				this.$form.find( '[name="prompt"]' ).val( existingContent );
				this.resetButton( true );
			} else {
				this.$generate_button.text( this.getLocale().generate );
				this.$form.find( '[name="prompt"]' ).val( '' );
				this.disableButton();
			}
		},

		changePrompt: function ( e ) {
			if ( this.isGenerateDisabled && e.target.value ) {
				this.resetButton( false );
				this.isGenerateDisabled = false;
			} else if ( !e.target.value && !this.isGenerateDisabled ) {
				this.disableButton();
			}
			// trim prompt value if it exceeds maxPromptLength
			var promptWords = e.target.value.split( ' ' );
			if ( promptWords.length > this.maxPromptLength ) {
				e.target.value = promptWords.slice( 0, this.maxPromptLength ).join( ' ' );
			}
		},

		showErrorMessage: function ( message ) {
			window.vc.showMessage( message, 'error', 10000, '#vc_ui-helper-modal-ai .vc_ui-panel-window-inner' );
		},

		insertContent: function () {
			var generatedContent = this.$generated_content.val();
			if ( !generatedContent ) {
				return false;
			}
			var currentParamData = this.$el.data();
			var aiFields = [ 'textarea', 'textfield', 'textarea_raw_html', 'textarea_ace' ];
			var aceEditorFields = [ 'wpb_css_editor', 'wpb_js_header_editor', 'wpb_js_footer_editor' ];

			if ( 'textarea_html' === currentParamData.param_type || 'content' === currentParamData.fieldId ) {
				var $textareaElement = currentParamData.element;
				var textareaId = $textareaElement.attr( 'id' );
				if ( 'new_content' === this.contentType ) {
					var currentTextareaValue = $textareaElement.val();
					var textAreaSpace = currentTextareaValue ? ' ' : '';
					generatedContent = currentTextareaValue + textAreaSpace + generatedContent;
				}

				var tinyMCE = window.tinymce.get( textareaId );
				if ( tinyMCE ) {
					tinyMCE.setContent( generatedContent );
				}
				$textareaElement.val( generatedContent ).trigger( 'input' ).trigger( 'change' ).trigger( 'blur' );
			} else if ( aiFields.includes( currentParamData.param_type ) ) {
				var $inputElement = currentParamData.element;
				if ( 'new_content' === this.contentType ) {
					var currentInputValue = $inputElement.val();
					if ( currentParamData.param_type !== 'textarea_ace' ) {
						var inputSpace = currentInputValue ? ' ' : '';
						generatedContent = currentInputValue + inputSpace + generatedContent;
					}
				}
				if ( currentParamData.param_type === 'textarea_ace' ) {
					var aceId = currentParamData.element.closest( '.edit_form_line' ).find( '.textarea_ace_container' ).attr( 'id' );
					var aceEditor = window.ace.edit( aceId );
					this.updateAceEditor( aceEditor, generatedContent );
				} else {
					$inputElement.val( generatedContent ).trigger( 'input' ).trigger( 'change' ).trigger( 'blur' );
				}
			} else if ( currentParamData.fieldId ) {
				if ( aceEditorFields.includes( currentParamData.fieldId ) ) {
					var aceEditor = window.ace.edit( currentParamData.fieldId );
					this.updateAceEditor( aceEditor, generatedContent );
				} else if ( currentParamData.element && currentParamData.element.length ) {
					if ( 'new_content' === this.contentType ) {
						var currentElementValue = currentParamData.element.val();
						var elementSpace = currentElementValue ? ' ' : '';
						generatedContent = currentElementValue + elementSpace + generatedContent;
					}
					currentParamData.element.val( generatedContent ).trigger( 'input' ).trigger( 'change' ).trigger( 'blur' );
				}
			}
			this.$close_button.click();
		},

		updateAceEditor: function ( aceEditor, generatedContent ) {
			var currentValue = aceEditor.getValue();
			var $codeTextarea = $( aceEditor.container ).find( 'textarea' );
			var emptySpace = '';
			if( currentValue !== '' ) {
				emptySpace = '\n\n';
			}
			generatedContent = currentValue + emptySpace + generatedContent;
			aceEditor.setValue( generatedContent );
			$codeTextarea.trigger( 'input' ).trigger( 'change' ).trigger( 'blur' );
		},

		toggleModalPromoClass: function ( type ) {
			if ( 'promo' === type ) {
				this.$el.addClass( 'vc_modal-ai-container--promo' );
			} else {
				this.$el.removeClass( 'vc_modal-ai-container--promo' );
			}
		},

		hideFormFields: function ( optionalityList ) {
			// hide form fields that do not match selected content type
			this.$form.find( 'div[data-optional-form-field]' ).each( function () {
				var $formField = $( this );
				var formFieldSlug = $formField.attr( 'data-optional-form-field' );
				if ( optionalityList.includes( formFieldSlug ) ) {
					$formField.show();
				} else {
					$formField.hide();
				}
			});
		},
		getLocale: function () {
			if ( window.i18nLocale ) {
				return window.i18nLocale;
			} else {
				return window.i18nLocaleSettings;
			}
		},
		toggleCopyButton: function () {
			var $copyButton = this.$el.find( '.wpb-copy-output' );
			if ( this.$generated_content.val() ) {
				$copyButton.removeClass( 'disabled' );
			} else {
				$copyButton.addClass( 'disabled' );
			}
		},
		copyContent: function ( e ) {
			e.preventDefault();
			var content = this.$generated_content.val();
			if ( !content ) {
				return false;
			}
			try {
				window.vc.utils.copyTextToClipboard( content );
				vc.showMessage( this.getLocale().copied, 'success', 2000, '#vc_ui-helper-modal-ai .vc_ui-panel-window-inner' );
			} catch ( error ) {
				console.error( 'Unable to copy content:', error );
			}
		}
	});
})( window.jQuery );
