/**
 * ThemeREX Auto Dealer — Archive Filters
 *
 * Vanilla JS, no jQuery. Handles three things on the inventory archive:
 *
 *   1. Live count badge: as the user changes any filter, debounced
 *      fetch /vehicles?... reads X-WP-Total and updates the "Apply"
 *      button label so the user knows how many results to expect.
 *   2. Cascading make → model: changing the make dropdown fetches
 *      /models?make={id} and rebuilds the model dropdown.
 *   3. URL state cleanup: empty fields are stripped on submit so the
 *      resulting URL is the minimal canonical query.
 *
 * Localised data is exposed via window.trxAdArchive (rest URL + nonce).
 *
 * @package ThemeREX\AutoDealer
 * @version 1.0.0
 */

(function () {
	'use strict';

	var DEBOUNCE_MS = 250;

	function getConfig() {
		return window.trxAdArchive || null;
	}

	function debounce(fn, wait) {
		var t;
		return function () {
			var ctx = this, args = arguments;
			clearTimeout(t);
			t = setTimeout(function () { fn.apply(ctx, args); }, wait);
		};
	}

	/**
	 * Join the REST base, an endpoint, and an optional query string with the
	 * correct separator. On pretty-permalink sites cfg.restUrl ends in `/v1/`
	 * so a `?` query is fine; on plain-permalink sites it is the
	 * `?rest_route=/trx-auto-dealer/v1/` form which ALREADY contains a `?`, so
	 * a query string must be appended with `&` — a second `?` would fold the
	 * params into the route name and 404. Mirrors blocks-editor.js' use of
	 * wp.url.addQueryArgs (not available on the public side without a dep).
	 *
	 * @param {object} cfg          window.trxAdArchive (has .restUrl).
	 * @param {string} endpoint     e.g. 'vehicles', 'models', 'search-options'.
	 * @param {string} [queryString] Already-encoded query without leading ?/&.
	 * @returns {string}
	 */
	function restEndpoint(cfg, endpoint, queryString) {
		var url = cfg.restUrl + endpoint;
		if (!queryString) { return url; }
		var sep = cfg.restUrl.indexOf('?') === -1 ? '?' : '&';
		return url + sep + queryString;
	}

	/**
	 * Walk the form, return { name: value } only for non-empty fields.
	 *
	 * @param {HTMLFormElement} form
	 * @returns {URLSearchParams}
	 */
	function collectParams(form) {
		var params = new URLSearchParams();
		var fd = new FormData(form);
		fd.forEach(function (value, key) {
			var v = String(value).trim();
			if (v !== '') {
				params.append(key, v);
			}
		});
		return params;
	}

	/**
	 * Strip empty inputs before native submission so URLs stay clean.
	 *
	 * @param {HTMLFormElement} form
	 */
	function bindCleanSubmit(form) {
		form.addEventListener('submit', function () {
			var inputs = form.querySelectorAll('input, select');
			inputs.forEach(function (el) {
				if (el.type === 'checkbox' && !el.checked) {
					el.disabled = true;
				} else if ((el.type === 'text' || el.type === 'search' || el.type === 'number') && el.value.trim() === '') {
					el.disabled = true;
				} else if (el.tagName === 'SELECT' && el.value === '') {
					el.disabled = true;
				}
			});
		});
	}

	/**
	 * Refresh the model <select> options when a make is chosen.
	 *
	 * @param {HTMLFormElement} form
	 */
	function bindCascadingModels(form) {
		var cfg = getConfig();
		if (!cfg) { return; }

		var makeSel = form.querySelector('[data-trx-ad-make]');
		var modelSel = form.querySelector('[data-trx-ad-model]');
		if (!makeSel || !modelSel) { return; }

		makeSel.addEventListener('change', function () {
			var selected = makeSel.options[makeSel.selectedIndex];
			var termId = selected ? selected.getAttribute('data-id') : '';

			modelSel.innerHTML = '<option value="">' + (cfg.i18n && cfg.i18n.anyModel ? cfg.i18n.anyModel : 'Any model') + '</option>';

			if (!termId) {
				modelSel.disabled = true;
				return;
			}

			modelSel.disabled = true;
			fetch(restEndpoint(cfg, 'models', 'make=' + encodeURIComponent(termId)), {
				headers: { 'X-WP-Nonce': cfg.restNonce, 'Accept': 'application/json' },
				credentials: 'same-origin'
			})
				.then(function (r) { return r.ok ? r.json() : []; })
				.then(function (rows) {
					if (!Array.isArray(rows)) { return; }
					rows.forEach(function (m) {
						var opt = document.createElement('option');
						opt.value = m.slug;
						opt.setAttribute('data-id', m.id);
						opt.textContent = m.name;
						modelSel.appendChild(opt);
					});
					modelSel.disabled = false;
				})
				.catch(function () { modelSel.disabled = false; });
		});
	}

	/**
	 * Debounced live-count: hits /vehicles with the current params and reads X-WP-Total.
	 *
	 * @param {HTMLFormElement} form
	 */
	function bindLiveCount(form) {
		var cfg = getConfig();
		if (!cfg) { return; }

		var pill = form.querySelector('[data-trx-ad-count]');
		if (!pill) { return; }

		var fetchCount = debounce(function () {
			var params = collectParams(form);
			params.set('per_page', '1');

			fetch(restEndpoint(cfg, 'vehicles', params.toString()), {
				headers: { 'X-WP-Nonce': cfg.restNonce, 'Accept': 'application/json' },
				credentials: 'same-origin'
			})
				.then(function (r) {
					var total = r.headers.get('X-WP-Total');
					return total !== null ? parseInt(total, 10) : 0;
				})
				.then(function (n) {
					if (Number.isNaN(n)) { n = 0; }
					var label = (cfg.i18n && cfg.i18n.showResults) ? cfg.i18n.showResults : 'Show %d';
					pill.textContent = label.replace('%d', String(n));
					pill.hidden = false;
				})
				.catch(function () { pill.hidden = true; });
		}, DEBOUNCE_MS);

		// Re-count on any field change.
		form.addEventListener('input', fetchCount);
		form.addEventListener('change', fetchCount);

		// Initial preview reflects the current URL state.
		fetchCount();
	}

	/**
	 * Auto-submit the form when a select/checkbox/range field changes so
	 * the listing grid refreshes without the user clicking Apply. Text
	 * inputs are excluded (keystroke-by-keystroke reload would be jarring);
	 * those still rely on Enter or the Apply button.
	 */
	function bindAutoSubmit(form) {
		var debouncedSubmit = debounce(function () {
			// Strip empty fields the same way bindCleanSubmit does so URLs
			// stay canonical.
			var inputs = form.querySelectorAll('input, select');
			for (var i = 0; i < inputs.length; i++) {
				var el = inputs[i];
				if (el.type !== 'submit' && el.value === '' && el.name) {
					el.disabled = true;
				}
			}
			// `form.submit()` skips submit-event listeners, which would defeat
			// the AJAX archive interceptor in frontend.js. `requestSubmit()`
			// dispatches the submit event — same redirect path when no AJAX
			// handler is bound, but the interceptor can preventDefault() and
			// swap the grid in place when the new archive is being rendered.
			if (typeof form.requestSubmit === 'function') {
				form.requestSubmit();
			} else {
				form.submit();
			}
		}, DEBOUNCE_MS);

		form.addEventListener('change', function (e) {
			var t = e.target;
			if (!t || !t.tagName) { return; }
			var tag = t.tagName.toLowerCase();
			var type = (t.type || '').toLowerCase();
			// Skip free-text inputs — auto-submit on every keystroke is bad UX.
			if (tag === 'input' && (type === 'text' || type === 'search' || type === 'number')) {
				return;
			}
			debouncedSubmit();
		});
	}

	/**
	 * On make change, refresh the entire filter form's facet options
	 * (body styles, fuel types, conditions, year/price/mileage ranges)
	 * by calling /search-options?make=<id>. Keeps the dropdowns and
	 * range hints in sync with the current selection so the user
	 * doesn't see facet values that yield zero results.
	 */
	function bindNarrowedFacets(form) {
		var cfg = getConfig();
		if (!cfg) { return; }
		var makeSel = form.querySelector('[data-trx-ad-make]');
		if (!makeSel) { return; }

		var refresh = function () {
			var sel = makeSel.options[makeSel.selectedIndex];
			var slug = sel ? sel.value : '';
			var query = slug ? 'make=' + encodeURIComponent(slug) : '';
			fetch(restEndpoint(cfg, 'search-options', query), {
				headers: { 'X-WP-Nonce': cfg.restNonce, 'Accept': 'application/json' },
				credentials: 'same-origin'
			})
				.then(function (r) { return r.ok ? r.json() : null; })
				.then(function (data) {
					if (!data) { return; }
					applyFacetUpdate(form, 'body_style', data.body_styles || [], 'data-trx-ad-body-style');
					applyFacetUpdate(form, 'fuel_type', data.fuel_types || [], 'data-trx-ad-fuel-type');
					applyFacetUpdate(form, 'condition', data.conditions || [], 'data-trx-ad-condition');
					applyRangeHint(form, 'year', data.year_range);
					applyRangeHint(form, 'price', data.price_range);
					applyRangeHint(form, 'mileage', data.mileage_range);
				})
				.catch(function () { /* silent — keep stale options */ });
		};
		makeSel.addEventListener('change', refresh);
	}

	function applyFacetUpdate(form, name, options, dataAttr) {
		var sel = form.querySelector('[' + dataAttr + ']') || form.querySelector('select[name="' + name + '"]');
		if (!sel) { return; }
		var current = sel.value;
		// Preserve "Any" placeholder (first <option>).
		var firstOpt = sel.querySelector('option[value=""]');
		sel.innerHTML = '';
		if (firstOpt) { sel.appendChild(firstOpt); }
		options.forEach(function (o) {
			var opt = document.createElement('option');
			opt.value = o.slug;
			opt.setAttribute('data-id', o.id);
			opt.textContent = o.name + (o.count ? ' (' + o.count + ')' : '');
			sel.appendChild(opt);
		});
		// Restore prior selection if still present.
		if (current && sel.querySelector('option[value="' + cssEscape(current) + '"]')) {
			sel.value = current;
		}
	}

	function applyRangeHint(form, prefix, range) {
		if (!range) { return; }
		var fromEl = form.querySelector('input[name="' + prefix + '_from"]');
		var toEl   = form.querySelector('input[name="' + prefix + '_to"]');
		if (range.min !== null && range.min !== undefined) {
			if (fromEl) { fromEl.setAttribute('min', String(range.min)); fromEl.setAttribute('placeholder', String(range.min)); }
			if (toEl)   { toEl.setAttribute('min', String(range.min)); }
		}
		if (range.max !== null && range.max !== undefined) {
			if (fromEl) { fromEl.setAttribute('max', String(range.max)); }
			if (toEl)   { toEl.setAttribute('max', String(range.max)); toEl.setAttribute('placeholder', String(range.max)); }
		}
	}

	function cssEscape(v) {
		// Conservative — only escape what could break the selector.
		return String(v).replace(/(["\\])/g, '\\$1');
	}

	function init() {
		var form = document.querySelector('[data-trx-ad-filters]');
		if (!form) { return; }
		bindCascadingModels(form);
		bindNarrowedFacets(form);
		bindLiveCount(form);
		bindCleanSubmit(form);
		bindAutoSubmit(form);
	}

	if (document.readyState === 'loading') {
		document.addEventListener('DOMContentLoaded', init);
	} else {
		init();
	}
})();
