/**
 * Signocore Multilanguage - String Translation
 *
 * AJAX-driven string translation interface with persistent filters,
 * row-level save states, debounced auto-save, and bulk save support.
 *
 * @package Signocore\Multilanguage
 */

(function ($) {
	'use strict';

	if (typeof smlAdmin === 'undefined' || typeof smlStringTranslation === 'undefined') {
		return;
	}

	var ST = {
		currentLang: '',
		currentPage: 1,
		perPage: 30,
		context: '',
		status: 'all',
		search: '',
		debounceTimer: null,
		saveTimers: {},
		pendingChanges: {},
		statusCounts: {
			all: 0,
			translated: 0,
			needs_update: 0,
			needs_translation: 0,
		},
		isBulkSaving: false,
		i18n: smlStringTranslation.i18n || {},

		init: function () {
			var $activePill = $('.sml-string-lang-pill.active');
			var initialLang = $activePill.data('lang') || '';
			this.currentLang = initialLang;

			if (!this.currentLang) {
				return;
			}

			this.readStatusCountsFromDom();
			this.restoreStateFromUrl();
			this.syncControls();
			this.bindEvents();
			this.updateUnsavedUi();
			this.updateUrlState();

			if (this.shouldReloadInitialList(initialLang)) {
				this.loadStrings();
			}
		},

		bindEvents: function () {
			var self = this;

			$(document).on('click', '.sml-string-lang-pill', function (e) {
				e.preventDefault();
				self.onLangSwitch($(this));
			});

			$(document).on('change', '#sml-context-filter', function () {
				self.context = $(this).val();
				self.currentPage = 1;
				self.updateClearFiltersVisibility();
				self.loadStrings();
			});

			$(document).on('click', '.sml-status-chip', function (e) {
				e.preventDefault();
				self.status = $(this).data('status') || 'all';
				self.currentPage = 1;
				self.setActiveStatusChip(self.status);
				self.updateClearFiltersVisibility();
				self.loadStrings();
			});

			$(document).on('input', '#sml-search-filter', function () {
				var val = $(this).val();
				clearTimeout(self.debounceTimer);
				self.debounceTimer = setTimeout(function () {
					self.search = val;
					self.currentPage = 1;
					self.updateClearFiltersVisibility();
					self.loadStrings();
				}, 400);
			});

			$(document).on('click', '#sml-clear-filters', function (e) {
				e.preventDefault();
				self.clearFilters();
			});

			$(document).on('click', '.sml-pagination a[data-page]', function (e) {
				e.preventDefault();
				var page = parseInt($(this).data('page'), 10);
				if (page && page !== self.currentPage) {
					self.currentPage = page;
					self.loadStrings();
				}
			});

			$(document).on('input', '.sml-translation-input', function () {
				self.onInputChange($(this));
			});

			$(document).on('blur', '.sml-translation-input', function () {
				self.onInputBlur($(this));
			});

			$(document).on('click', '#sml-save-all', function (e) {
				e.preventDefault();
				self.saveAllChanges();
			});

			$(window).on('beforeunload', function () {
				if (Object.keys(self.pendingChanges).length < 1) {
					return;
				}

				return '';
			});
		},

		onLangSwitch: function ($pill) {
			this.currentLang = $pill.data('lang');
			this.currentPage = 1;

			this.syncControls();
			this.loadStrings();
		},

		loadStrings: function () {
			var self = this;
			var $list = $('#sml-string-list');
			this.renderLoadingState();
			this.setFeedback(this.i18n.loading || 'Loading strings...', 'is-info');
			this.updateUrlState();

			$.post(smlAdmin.ajaxUrl, {
				action: 'sml_get_strings',
				_ajax_nonce: smlAdmin.nonce,
				lang: this.currentLang,
				context: this.context,
				status: this.status,
				search: this.search,
				page: this.currentPage,
				per_page: this.perPage,
			}, function (response) {
				if (!response.success) {
					self.renderLoadError();
					return;
				}

				$list.removeClass('is-loading');
				self.renderStrings(response.data.strings || []);
				self.renderPagination(response.data.total || 0, response.data.page || 1, response.data.per_page || self.perPage);
				self.setStatusCounts(response.data.status_counts || null);
				self.setFeedback('');
				self.currentPage = response.data.page || 1;
				self.perPage = response.data.per_page || self.perPage;
				self.updateUrlState();
			}).fail(function () {
				self.renderLoadError();
			});
		},

		renderStrings: function (strings) {
			var $list = $('#sml-string-list');
			$list.empty();
			$list.removeClass('is-loading');

			if (!strings || !strings.length) {
				$list.html(
					'<div class="sml-string-empty">' +
					'<span class="dashicons dashicons-translation"></span>' +
					this.escHtml(this.i18n.noResults || 'No strings found matching your criteria.') +
					'</div>'
				);
				return;
			}

			var lang = this.currentLang;
			var self = this;

			$.each(strings, function (i, str) {
				var statusClass = (str.translation_value && str.translation_value.trim() !== '')
					? self.normalizeStatus(str.translation_status)
					: 'needs_translation';
				var isTextarea = str.type !== 'line';
				var key = self.getChangeKey(str.id, lang);
				var pendingChange = self.pendingChanges[key] || null;
				var originalValue = str.translation_value || '';
				var inputValue = pendingChange ? pendingChange.value : originalValue;
				var inputId = 'sml-string-translation-' + str.id;
				var saveStateClass = pendingChange ? ' is-unsaved' : '';
				var saveStateText = pendingChange ? self.getUnsavedTextShort() : '';

				var inputHtml;
				if (isTextarea) {
					inputHtml = '<textarea id="' + self.escAttr(inputId) + '"' +
						' class="sml-translation-input sml-string-translation"' +
						' data-string-id="' + str.id + '"' +
						' data-lang-code="' + self.escAttr(lang) + '"' +
						' data-original-value="' + self.escAttr(originalValue) + '"' +
						' aria-label="' + self.escAttr((self.i18n.translationField || 'Translation field') + ': ' + (str.name || '')) + '"' +
						' rows="3">' + self.escHtml(inputValue) + '</textarea>';
				} else {
					inputHtml = '<input type="text"' +
						' id="' + self.escAttr(inputId) + '"' +
						' class="sml-translation-input sml-string-translation"' +
						' data-string-id="' + str.id + '"' +
						' data-lang-code="' + self.escAttr(lang) + '"' +
						' data-original-value="' + self.escAttr(originalValue) + '"' +
						' aria-label="' + self.escAttr((self.i18n.translationField || 'Translation field') + ': ' + (str.name || '')) + '"' +
						' value="' + self.escAttr(inputValue) + '">';
				}

				var card =
					'<div class="sml-string-card' + (pendingChange ? ' is-dirty' : '') + '"' +
					' data-string-id="' + str.id + '"' +
					' data-status="' + self.escAttr(statusClass) + '">' +
					'<div class="sml-string-card-header">' +
					'<span class="sml-string-original">' + self.escHtml(str.value) + '</span>' +
					'<span class="sml-string-context">' + self.escHtml(str.context) + '</span>' +
					'</div>' +
					inputHtml +
					'<span class="sml-string-save-state' + saveStateClass + '" aria-live="polite">' + self.escHtml(saveStateText) + '</span>' +
					'</div>';

				$list.append(card);
			});
		},

		renderPagination: function (total, page, perPage) {
			var $pag = $('#sml-pagination');
			$pag.empty();

			var totalPages = Math.ceil(total / perPage);
			var html = '';

			if (totalPages > 1 && page > 1) {
				html += '<a href="#" class="sml-page-number" data-page="' + (page - 1) + '">&laquo;</a>';
			}

			if (totalPages > 1) {
				var pages = [];
				var i;
				var lastPage = 0;

				for (i = 1; i <= totalPages; i++) {
					if (i === 1 || i === totalPages || (i >= page - 1 && i <= page + 1)) {
						pages.push(i);
					}
				}

				for (i = 0; i < pages.length; i++) {
					if (pages[i] > lastPage + 1) {
						html += '<span class="sml-page-dots">&hellip;</span>';
					}

					if (pages[i] === page) {
						html += '<span class="sml-page-number current" data-page="' + pages[i] + '">' + pages[i] + '</span>';
					} else {
						html += '<a href="#" class="sml-page-number" data-page="' + pages[i] + '">' + pages[i] + '</a>';
					}

					lastPage = pages[i];
				}
			}

			if (totalPages > 1 && page < totalPages) {
				html += '<a href="#" class="sml-page-number" data-page="' + (page + 1) + '">&raquo;</a>';
			}

			html += '<span class="sml-pagination-info">' + total + ' ' + (total === 1 ? (this.i18n.item || 'item') : (this.i18n.items || 'items')) + '</span>';

			$pag.html(html);
		},

		clearFilters: function () {
			this.context = '';
			this.status = 'all';
			this.search = '';
			this.currentPage = 1;
			this.syncControls();
			this.loadStrings();
		},

		onInputChange: function ($input) {
			var stringId = parseInt($input.data('string-id'), 10);
			var langCode = String($input.data('lang-code') || '');
			var originalValue = String($input.attr('data-original-value') || '');
			var value = String($input.val() || '');
			var key = this.getChangeKey(stringId, langCode);

			if (value === originalValue) {
				delete this.pendingChanges[key];
				this.clearSaveTimer(key);
				this.setCardDirty($input, false);
				this.setRowState($input, '', '');
				this.updateUnsavedUi();
				return;
			}

			this.pendingChanges[key] = {
				string_id: stringId,
				lang_code: langCode,
				value: value,
			};

			this.setCardDirty($input, true);
			this.setRowState($input, 'unsaved', this.getUnsavedTextShort());
			this.scheduleAutoSave($input, key);
			this.updateUnsavedUi();
		},

		onInputBlur: function ($input) {
			var key = this.getInputKey($input);
			if (!key || !this.pendingChanges[key]) {
				return;
			}

			this.clearSaveTimer(key);
			this.saveInput($input, true);
		},


		scheduleAutoSave: function ($input, key) {
			var self = this;
			this.clearSaveTimer(key);
			this.saveTimers[key] = setTimeout(function () {
				self.saveInput($input, true);
			}, 900);
		},

		saveInput: function ($input, isAutoSave) {
			if (!$input || !$input.length) {
				return;
			}

			var stringId = parseInt($input.data('string-id'), 10);
			var langCode = String($input.data('lang-code') || '');
			var value = String($input.val() || '');
			var key = this.getChangeKey(stringId, langCode);
			var originalValue = String($input.attr('data-original-value') || '');
			var self = this;

			if (value === originalValue) {
				delete this.pendingChanges[key];
				this.setCardDirty($input, false);
				this.setRowState($input, '', '');
				this.updateUnsavedUi();
				return;
			}

			this.setRowState($input, 'saving', this.i18n.saving || 'Saving...');

			$.post(smlAdmin.ajaxUrl, {
				action: 'sml_save_string_translation',
				_ajax_nonce: smlAdmin.nonce,
				string_id: stringId,
				lang_code: langCode,
				value: value,
			}, function (response) {
				if (!response.success || !response.data) {
					self.handleSaveError($input, key);
					return;
				}

				var $card = $input.closest('.sml-string-card');
					var oldStatus = self.normalizeStatus($card.attr('data-status'));
					var newStatus = self.normalizeStatus(response.data.status);

					$input.attr('data-original-value', value);
					delete self.pendingChanges[key];
					self.setCardDirty($input, false);
					self.updateCardStatus($card, newStatus);
					self.adjustStatusCounts(oldStatus, newStatus);
					self.renderStatusCounts();

					if (self.status !== 'all' && newStatus !== self.status) {
						self.updateUnsavedUi();
						self.loadStrings();
						return;
					}

					self.setRowState($input, 'saved', self.i18n.saved || 'Saved');
					self.scheduleClearSavedState($input);
					self.updateLastSaved(response.data.saved_at || '');
					self.updateUnsavedUi();

				}).fail(function () {
				self.handleSaveError($input, key);
			});
		},

		saveAllChanges: function () {
			if (this.isBulkSaving) {
				return;
			}

			var self = this;
			var keys = Object.keys(this.pendingChanges);

			if (!keys.length) {
				return;
			}

			var payload = [];
			$.each(keys, function (index, key) {
				payload.push(self.pendingChanges[key]);
			});

			this.isBulkSaving = true;
			this.updateUnsavedUi();
			this.setFeedback(this.i18n.saving || 'Saving...', 'is-info');

			$.post(smlAdmin.ajaxUrl, {
				action: 'sml_save_string_translations_bulk',
				_ajax_nonce: smlAdmin.nonce,
				items_json: JSON.stringify(payload),
			}, function (response) {
				if (!response.success || !response.data || !response.data.items) {
					self.setFeedback(self.i18n.saveAllFailed || 'Bulk save failed.', 'is-error');
					return;
				}

				$.each(response.data.items, function (index, item) {
					self.applyBulkSaveResult(item);
				});

					self.updateUnsavedUi();
					self.updateLastSaved(response.data.saved_at || '');

					if (response.data.failed_count && response.data.failed_count > 0) {
						self.setFeedback(self.i18n.saveAllPartial || 'Some changes could not be saved.', 'is-warning');
					} else {
						self.setFeedback('');
					}

					if (self.status !== 'all') {
						self.loadStrings();
					}
				}).fail(function () {
					self.setFeedback(self.i18n.saveAllFailed || 'Bulk save failed.', 'is-error');
				}).always(function () {
				self.isBulkSaving = false;
				self.updateUnsavedUi();
			});
		},

		applyBulkSaveResult: function (item) {
			var stringId = parseInt(item.string_id, 10);
			var langCode = String(item.lang_code || '');
			var key = this.getChangeKey(stringId, langCode);
			var pending = this.pendingChanges[key];

			if (!pending) {
				return;
			}

			delete this.pendingChanges[key];
			this.clearSaveTimer(key);

			var $input = $('.sml-translation-input[data-string-id="' + stringId + '"][data-lang-code="' + this.escAttr(langCode) + '"]');
			if (!$input.length) {
				return;
			}

			var $card = $input.closest('.sml-string-card');
			var oldStatus = this.normalizeStatus($card.attr('data-status'));
			var newStatus = this.normalizeStatus(item.status);

			$input.val(pending.value);
			$input.attr('data-original-value', pending.value);
			this.setCardDirty($input, false);
			this.updateCardStatus($card, newStatus);
			this.adjustStatusCounts(oldStatus, newStatus);
			this.renderStatusCounts();
			this.setRowState($input, 'saved', this.i18n.saved || 'Saved');
			this.scheduleClearSavedState($input);
		},

		handleSaveError: function ($input, key) {
			this.setRowState($input, 'error', this.i18n.saveFailed || 'Save failed');
			this.setCardDirty($input, true);
			this.updateUnsavedUi();
			this.clearSaveTimer(key);
		},


		setCardDirty: function ($input, isDirty) {
			var $card = $input.closest('.sml-string-card');
			$card.toggleClass('is-dirty', isDirty);
		},

		setRowState: function ($input, state, text) {
			var $state = $input.closest('.sml-string-card').find('.sml-string-save-state');
			$state
				.removeClass('is-unsaved is-saving is-saved is-error')
				.text(text || '');

			if (state) {
				$state.addClass('is-' + state);
			}
		},

		scheduleClearSavedState: function ($input) {
			setTimeout(function () {
				var $state = $input.closest('.sml-string-card').find('.sml-string-save-state');
				if ($state.hasClass('is-saved')) {
					$state.removeClass('is-saved').text('');
				}
			}, 2000);
		},

		updateCardStatus: function ($card, status) {
			$card.attr('data-status', this.normalizeStatus(status));
		},

		updateUnsavedUi: function () {
			var count = Object.keys(this.pendingChanges).length;
			var $badge = $('#sml-unsaved-count');
			var $saveAll = $('#sml-save-all');

			if (count < 1) {
				$badge.removeClass('has-unsaved').hide();
				$saveAll.hide();
				return;
			}

			var label = count === 1
				? (this.i18n.unsavedChange || '1 unsaved change')
				: this.formatCount(this.i18n.unsavedChanges || '%d unsaved changes', count);

			$badge
				.addClass('has-unsaved')
				.text(label)
				.show();

			$saveAll
				.prop('disabled', this.isBulkSaving)
				.text(this.isBulkSaving ? (this.i18n.saving || 'Saving...') : (this.i18n.saveAll || 'Save all changes'))
				.show();
		},

		updateLastSaved: function (savedAt) {
			if (!savedAt) {
				return;
			}

			var template = this.i18n.lastSaved || 'Last saved at %s';
			$('#sml-last-saved').text(template.replace('%s', savedAt));
		},

		setFeedback: function (message, typeClass) {
			var $feedback = $('#sml-string-feedback');

			if (!message) {
				$feedback.removeClass('is-info is-warning is-error').text('');
				return;
			}

			$feedback
				.removeClass('is-info is-warning is-error')
				.addClass(typeClass || 'is-info')
				.text(message);
		},

		renderLoadError: function () {
			$('#sml-string-list').removeClass('is-loading');
			this.setFeedback(this.i18n.loadError || 'Could not load strings. Please try again.', 'is-error');
		},

		renderLoadingState: function () {
			var html = '';
			var i;
			var $list = $('#sml-string-list');

			for (i = 0; i < 3; i++) {
				html += '<div class="sml-string-card is-skeleton" aria-hidden="true">' +
					'<div class="sml-skeleton-line sml-skeleton-line--small"></div>' +
					'<div class="sml-skeleton-line"></div>' +
					'<div class="sml-skeleton-line sml-skeleton-line--large"></div>' +
					'</div>';
			}

			$list.addClass('is-loading').html(html);
		},


		setActiveStatusChip: function (status) {
			$('.sml-status-chip')
				.removeClass('active')
				.attr('aria-pressed', 'false');

			$('.sml-status-chip[data-status="' + this.escAttr(status) + '"]')
				.addClass('active')
				.attr('aria-pressed', 'true');
		},

		readStatusCountsFromDom: function () {
			this.statusCounts = {
				all: this.toInt($('#sml-status-count-all').text()),
				translated: this.toInt($('#sml-status-count-translated').text()),
				needs_update: this.toInt($('#sml-status-count-needs_update').text()),
				needs_translation: this.toInt($('#sml-status-count-needs_translation').text()),
			};
		},

		setStatusCounts: function (counts) {
			if (!counts) {
				return;
			}

			this.statusCounts = {
				all: this.toInt(counts.all),
				translated: this.toInt(counts.translated),
				needs_update: this.toInt(counts.needs_update),
				needs_translation: this.toInt(counts.needs_translation),
			};

			this.renderStatusCounts();
		},

		renderStatusCounts: function () {
			$('#sml-status-count-all').text(this.statusCounts.all);
			$('#sml-status-count-translated').text(this.statusCounts.translated);
			$('#sml-status-count-needs_update').text(this.statusCounts.needs_update);
			$('#sml-status-count-needs_translation').text(this.statusCounts.needs_translation);
		},

		adjustStatusCounts: function (oldStatus, newStatus) {
			if (oldStatus === newStatus) {
				return;
			}

			if (oldStatus && this.statusCounts[oldStatus] > 0) {
				this.statusCounts[oldStatus] -= 1;
			}

			if (newStatus && typeof this.statusCounts[newStatus] !== 'undefined') {
				this.statusCounts[newStatus] += 1;
			}
		},

		restoreStateFromUrl: function () {
			var params = new URLSearchParams(window.location.search);
			var lang = params.get('sml_lang');
			var context = params.get('sml_context');
			var status = params.get('sml_status');
			var search = params.get('sml_search');
			var page = parseInt(params.get('sml_page') || '1', 10);
			var hasLangPill = $('.sml-string-lang-pill[data-lang="' + this.escAttr(lang || '') + '"]').length > 0;

			if (lang && hasLangPill) {
				this.currentLang = lang;
			}

			if (context) {
				this.context = context;
			}

			if (status && ['all', 'translated', 'needs_update', 'needs_translation'].indexOf(status) !== -1) {
				this.status = status;
			}

			if (search) {
				this.search = search;
			}

			if (page > 0) {
				this.currentPage = page;
			}
		},

		shouldReloadInitialList: function (initialLang) {
			return this.currentLang !== initialLang ||
				this.context !== '' ||
				this.status !== 'all' ||
				this.search !== '' ||
				this.currentPage > 1;
		},

		syncControls: function () {
			$('.sml-string-lang-pill').removeClass('active');
			$('.sml-string-lang-pill[data-lang="' + this.escAttr(this.currentLang) + '"]').addClass('active');
			$('#sml-context-filter').val(this.context);
			$('#sml-search-filter').val(this.search);
			this.setActiveStatusChip(this.status);
			this.updateClearFiltersVisibility();
		},

		updateClearFiltersVisibility: function () {
			var hasFilters = this.context !== '' || this.status !== 'all' || this.search !== '';
			$('#sml-clear-filters').toggle(hasFilters);
		},

		updateUrlState: function () {
			var url = new URL(window.location.href);
			var params = url.searchParams;

			params.set('sml_lang', this.currentLang);

			if (this.context) {
				params.set('sml_context', this.context);
			} else {
				params.delete('sml_context');
			}

			if (this.status && this.status !== 'all') {
				params.set('sml_status', this.status);
			} else {
				params.delete('sml_status');
			}

			if (this.search) {
				params.set('sml_search', this.search);
			} else {
				params.delete('sml_search');
			}

			params.set('sml_page', String(this.currentPage));
			window.history.replaceState({}, '', url.toString());
		},

		getInputKey: function ($input) {
			if (!$input || !$input.length) {
				return '';
			}

			return this.getChangeKey($input.data('string-id'), $input.data('lang-code'));
		},

		getChangeKey: function (stringId, langCode) {
			return String(stringId) + '_' + String(langCode);
		},

		clearSaveTimer: function (key) {
			if (!key || !this.saveTimers[key]) {
				return;
			}

			clearTimeout(this.saveTimers[key]);
			delete this.saveTimers[key];
		},

		getUnsavedTextShort: function () {
			return this.i18n.unsaved || 'Unsaved';
		},

		normalizeStatus: function (status) {
			if (status === 'translated' || status === 'needs_update' || status === 'needs_translation') {
				return status;
			}
			return 'needs_translation';
		},

		formatCount: function (template, count) {
			return String(template).replace('%d', String(count));
		},

		toInt: function (value) {
			var parsed = parseInt(value, 10);
			return Number.isNaN(parsed) ? 0 : parsed;
		},

		getStatusLabel: function (status) {
			var labels = {
				translated: this.i18n.translated || 'Translated',
				needs_update: this.i18n.needsUpdate || 'Needs Update',
				needs_translation: this.i18n.needsTranslation || 'Needs Translation',
			};
			return labels[status] || status;
		},

		escHtml: function (str) {
			if (!str) return '';
			var div = document.createElement('div');
			div.appendChild(document.createTextNode(str));
			return div.innerHTML;
		},

		escAttr: function (str) {
			if (!str) return '';
			return String(str)
				.replace(/&/g, '&amp;')
				.replace(/"/g, '&quot;')
				.replace(/'/g, '&#39;')
				.replace(/</g, '&lt;')
				.replace(/>/g, '&gt;');
		},
	};

	$(document).ready(function () {
		ST.init();
	});
})(jQuery);
