/*
** Copyright (C) 2001-2026 Zabbix SIA
**
** This program is free software: you can redistribute it and/or modify it under the terms of
** the GNU Affero General Public License as published by the Free Software Foundation, version 3.
**
** This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
** without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
** See the GNU Affero General Public License for more details.
**
** You should have received a copy of the GNU Affero General Public License along with this program.
** If not, see <https://www.gnu.org/licenses/>.
**/


/**
 * JavaScript class to manage host macros.
 */
class HostMacrosManager {
	static ZBX_PROPERTY_OWN = 0x02;
	static ZBX_MACRO_TYPE_TEXT = 0;
	static ZBX_MACRO_TYPE_SECRET = 1;
	static ZBX_MACRO_TYPE_VAULT = 2;
	static ZBX_STYLE_TEXTAREA_FLEXIBLE = 'textarea-flexible';
	static DISCOVERY_STATE_AUTOMATIC = 0x1;
	static DISCOVERY_STATE_CONVERTING = 0x2;
	static DISCOVERY_STATE_MANUAL = 0x3;

	constructor({container, readonly, parent_hostid, load_callback, source}) {
		this.$container = container;
		this.readonly = readonly;
		this.parent_hostid = parent_hostid ?? null;
		this.load_callback = load_callback ?? null;
		this.source = source ?? null;
		this.xhr = null;
	}

	load(show_inherited_macros, templateids) {
		if (this.xhr) {
			this.xhr.abort();
		}

		const url = new Curl('zabbix.php');
		url.setArgument('action', 'hostmacros.list');

		const post_data = {
			macros: this.getMacros(),
			show_inherited_macros: show_inherited_macros ? 1 : 0,
			templateids: templateids,
			readonly: this.readonly ? 1 : 0,
			source: this.source
		};

		if (this.parent_hostid !== null) {
			post_data.parent_hostid = this.parent_hostid;
		}

		this.xhr = $.ajax(url.getUrl(), {
			data: post_data,
			dataType: 'json',
			method: 'POST',
			beforeSend: () => {
				this.loaderStart();
			}
		})
			.done((response) => {
				if (typeof response === 'object' && 'error' in response) {
					const message_box = makeMessageBox('bad', response.error.messages, response.error.title);

					this.$container.prepend(message_box);
				}
				else {
					this.$container.empty();

					if (typeof response.messages !== 'undefined') {
						this.$container.append(response.messages);
					}

					this.$container.append(response.body);

					// Initialize macros.
					if (this.readonly) {
						$('.' + HostMacrosManager.ZBX_STYLE_TEXTAREA_FLEXIBLE, this.getMacroTable()).textareaFlexible();
					}
					else {
						this.initMacroTable(show_inherited_macros);
					}

					if (this.load_callback !== null) {
						this.load_callback();
					}

					// Display debug after loaded content if it is enabled for user.
					if (typeof response.debug !== 'undefined') {
						this.$container.append(response.debug);

						// Override margin for inline usage.
						$('.debug-output', this.$container).css('margin', '10px 0');
					}
				}
			})
			.always(() => {
				this.loaderStop();
			});
	}

	/*
	 * Get macros from UI.
	 */
	getMacros() {
		const $macros = $('input[name^="macros"], textarea[name^="macros"]', this.$container).not(':disabled');
		const macros = {};

		// Find the correct macro inputs and prepare to submit them via AJAX.
		$macros.each(function() {
			const $this = $(this);
			const [, macro_num, field] = $this.attr('name').match(/macros\[(\d+)\]\[(\w+)\]/);

			if (!macros.hasOwnProperty(macro_num)) {
				macros[macro_num] = new Object();
			}

			macros[macro_num][field] = $this.val();
		});

		return macros;
	}

	initMacroTable(show_inherited_macros) {
		const $parent = this.getMacroTable();
		const dropdown_btn_classes = {
			[HostMacrosManager.ZBX_MACRO_TYPE_TEXT]: ZBX_ICON_TEXT,
			[HostMacrosManager.ZBX_MACRO_TYPE_SECRET]: ZBX_ICON_EYE_OFF,
			[HostMacrosManager.ZBX_MACRO_TYPE_VAULT]: ZBX_ICON_LOCK
		};

		$parent
			.dynamicRows({
				remove_next_sibling: show_inherited_macros,
				template: show_inherited_macros ? '#macro-row-tmpl-inherited' : '#macro-row-tmpl',
				allow_empty: true,
			})
			.on('click', 'button.element-table-add', () => {
				this.initMacroFields($parent);
			})
			.on('click', 'button.element-table-change', (e) => {
				const macro_num = e.target.id.split('_')[1];
				const inherited_type = $('#macros_' + macro_num + '_inherited_type').val();
				const macro_type = $('#macros_' + macro_num + '_inherited_macro_type').val();
				const inherited_value_field_state = (macro_type == HostMacrosManager.ZBX_MACRO_TYPE_SECRET)
					? {'disabled': true}
					: {'readonly': true};

				if (inherited_type & HostMacrosManager.ZBX_PROPERTY_OWN) {
					// Switching from ZBX_PROPERTY_BOTH to ZBX_PROPERTY_INHERITED.
					$('#macros_' + macro_num + '_inherited_type')
						.val(inherited_type & ~HostMacrosManager.ZBX_PROPERTY_OWN);
					$('#macros_' + macro_num + '_description')
						.prop('readonly', true)
						.val($('#macros_' + macro_num + '_inherited_description').val())
						.trigger('input');
					$('#macros_' + macro_num + '_type_button')
						.removeClass()
						.addClass(['btn-alt', 'btn-dropdown-toggle', dropdown_btn_classes[macro_type]].join(' '))
						.prop('disabled', true)
						.attr({'aria-haspopup': false});
					$('input[type=hidden]', $('#macros_' + macro_num + '_type_button').parent())
						.val(macro_type)
						.trigger('change');
					$('#macros_' + macro_num + '_value')
						.prop(inherited_value_field_state)
						.val($('#macros_' + macro_num + '_inherited_value').val())
						.trigger('input');
					$('#macros_' + macro_num + '_value')
						.closest('.macro-input-group')
						.find('.btn-undo')
						.remove();
					$('#macros_' + macro_num + '_value_btn').prop('disabled', true);
					$('#macros_' + macro_num + '_change_inheritance').text(t('Change'));
					$('#macros_' + macro_num + '_allow_revert').remove();
					$('#macros_' + macro_num + '_hostmacroid').remove();

					document.querySelectorAll(`[data-field-type][name^="macros[${macro_num}]"]`)
						.forEach(field => field.setAttribute('data-skip-from-submit', 1));
				}
				else {
					// Switching from ZBX_PROPERTY_INHERITED to ZBX_PROPERTY_BOTH.
					$('#macros_' + macro_num + '_inherited_type')
						.val(inherited_type | HostMacrosManager.ZBX_PROPERTY_OWN);
					$('#macros_' + macro_num + '_value')
						.prop('disabled', false)
						.prop('readonly', false)
						.attr({'placeholder': t('value')})
						.val('')
						.focus();
					$('#macros_' + macro_num + '_description').prop('readonly', false);
					$('#macros_' + macro_num + '_type_button')
						.prop('disabled', false)
						.attr({'aria-haspopup': true});
					$('#macros_' + macro_num + '_change_inheritance').text(t('Remove'));

					document.querySelectorAll(`[data-field-type][name^="macros[${macro_num}]"]`)
						.forEach(field => field.removeAttribute('data-skip-from-submit'));
				}

				$('#macros_' + macro_num + '_discovery_state').val(HostMacrosManager.DISCOVERY_STATE_MANUAL);
				$('#macros_' + macro_num + '_change_state').remove();
			})
			.on('click', 'button.element-table-set-manual', (e) => {
				const num = e.target.id.split('_')[1];
				const $row = $(e.target.closest('.form_row'));
				const $discovery = $('#macros_' + num + '_discovery_state', $row);

				if ($discovery.val() == HostMacrosManager.DISCOVERY_STATE_CONVERTING) {
					const original_type = $('#macros_' + num + '_original_macro_type').val();
					const original_value = $('#macros_' + num + '_original_value', $row).val();
					const original_descr = $('#macros_' + num + '_original_description', $row).val();
					const original_value_field_state = (original_type == HostMacrosManager.ZBX_MACRO_TYPE_SECRET)
						? {'disabled': true}
						: {'readonly': true};

					$('#macros_' + num + '_value_btn', $row).prop('disabled', true);
					$('#macros_' + num + '_type_button', $row)
						.removeClass()
						.addClass(['btn-alt', 'btn-dropdown-toggle', dropdown_btn_classes[original_type]].join(' '))
						.prop('disabled', true)
						.attr({'aria-haspopup': false});
					$('input[type=hidden]', $('#macros_' + num + '_type_button', $row).parent())
						.val(original_type)
						.trigger('change');
					$('#macros_' + num + '_value', $row)
						.val(original_value)
						.prop(original_value_field_state)
						.trigger('input');
					$('#macros_' + num + '_value', $row)
						.closest('.macro-input-group')
						.find('.btn-undo')
						.hide();
					$('#macros_' + num + '_description')
						.val(original_descr)
						.prop('readonly', true)
						.trigger('input');
					$('#macros_' + num + '_type_button', $row).trigger('change');
					$discovery.val(HostMacrosManager.DISCOVERY_STATE_AUTOMATIC);
					$('#macros_' + num + '_change_state').text(t('Change'));
				}
				else {
					$('#macros_' + num + '_value', $row)
						.prop('readonly', false)
						.focus();
					$('#macros_' + num + '_value_btn', $row).prop('disabled', false);
					$('#macros_' + num + '_type_button', $row)
						.prop('disabled', false)
						.attr({'aria-haspopup': true});
					$('#macros_' + num + '_description').prop('readonly', false);
					$('#macros_' + num + '_type_button', $row).trigger('change');
					$discovery.val(HostMacrosManager.DISCOVERY_STATE_CONVERTING);
					$('#macros_' + num + '_change_state').text(t('Revert'));
				}
			})
			.on('afteradd.dynamicRows', function() {
				$('.macro-input-group').macroValue();
			});

		this.initMacroFields($parent);
	}

	initMacroFields($parent) {
		$('.'+HostMacrosManager.ZBX_STYLE_TEXTAREA_FLEXIBLE, $parent).not('.initialized-field')
				.each((index, textarea) => {
			const $textarea = $(textarea);

			if ($textarea.hasClass('macro')) {
				$textarea.on('change keydown', (e) => {
					if (e.type === 'change' || e.which === 13) {
						this.macroToUpperCase($textarea);
						$textarea.textareaFlexible();
					}
				});
			}

			$textarea
				.addClass('initialized-field')
				.textareaFlexible();
		});
	}

	getMacroTable() {
		return $('.inherited-macros-table, .host-macros-table', this.$container).eq(0);
	}

	loaderStart() {
		this.$container.trigger('loader.start');
		this.$preloader = $('<span>', {class: 'is-loading'});

		const macros_table = this.$container.find('table');

		macros_table.hide();
		this.$container.append(this.$preloader);
	}

	loaderStop() {
		this.$preloader.remove();

		const macros_table = this.$container.find('table');

		macros_table.show();
		this.$container.trigger('loader.stop');
	}

	macroToUpperCase($element) {
		var macro = $element.val(),
			end = macro.indexOf(':');

		if (end == -1) {
			$element.val(macro.toUpperCase());
		}
		else {
			var macro_part = macro.substr(0, end),
				context_part = macro.substr(end, macro.length);

			$element.val(macro_part.toUpperCase() + context_part);
		}
	}

	getManualDiscoveryState() {
		return HostMacrosManager.DISCOVERY_STATE_MANUAL;
	}

	getDefaultMacroType() {
		return HostMacrosManager.ZBX_MACRO_TYPE_TEXT;
	}
}
