(function(){
	if (typeof truebeUpload === 'undefined') { return; }
	var cfg = truebeUpload;
	var form = document.getElementById('truebe-upload-form');
	if (!form) { return; }
	var card = document.getElementById('truebe-card');
	var drop = document.getElementById('truebe-drop');
	var input = document.getElementById('truebe_document');
	var choose = document.getElementById('truebe-choose');
	var list = document.getElementById('truebe-file-list');
	var alertBox = document.getElementById('truebe-drop-alert');
	var submit = document.getElementById('truebe-submit');
	var action = document.getElementById('truebe-upload-action');
	var prog = document.getElementById('truebe-progress');
	var fill = document.getElementById('truebe-progress-fill');
	var plabel = document.getElementById('truebe-progress-label');
	var success = document.getElementById('truebe-success');
	var successTitle = document.getElementById('truebe-success-title');
	var successSub = document.getElementById('truebe-success-sub');
	var successCheck = success ? success.querySelector('.truebe-success-check') : null;
	var dropIcon = document.getElementById('truebe-drop-icon');
	var dropTitle = document.getElementById('truebe-drop-title');
	var files = [];
	var runToken = 0;
	var busy = false;
	var crawlTimer = null;
	var crawlPct = 0;
	var crawlLabel = '';
	var crawlCeiling = 99;

	/*
	 * Batch-wide bar. Each file owns an equal slice of 0-100; the crawl creeps
	 * inside the current file's slice and snaps to the slice boundary when that
	 * file finishes, so one climb covers the whole batch and speed scales with
	 * batch size. Holds just under 100 until the final file reports done.
	 */
	function paint(){
		prog.style.display = 'block';
		var pct = Math.min(99, Math.round(crawlPct));
		plabel.textContent = crawlLabel ? crawlLabel + ' \u00b7 ' + pct + '%' : pct + '%';
		fill.style.width = pct + '%';
	}
	function startCrawl(){
		stopCrawl();
		crawlTimer = setInterval(function(){
			if (crawlPct >= crawlCeiling){ crawlPct = crawlCeiling; paint(); return; }
			crawlPct += 1;
			paint();
		}, 900);
	}
	function stopCrawl(){ if (crawlTimer){ clearInterval(crawlTimer); crawlTimer = null; } }
	function resetCrawl(){ stopCrawl(); crawlPct = 0; crawlLabel = ''; crawlCeiling = 99; }

	/* Position the crawl inside file slice `index` of `total`. */
	function setSlice(index, total){
		var slice = 100 / total;
		var floor = slice * index;
		crawlCeiling = Math.min(99, slice * (index + 1) - 1);
		if (crawlPct < floor){ crawlPct = floor; }
		paint();
	}
	/* Snap to the end of file slice `index` of `total`. */
	function completeSlice(index, total){
		var slice = 100 / total;
		crawlPct = Math.min(99, slice * (index + 1));
		paint();
	}

	function setBusy(on){
		busy = on;
		drop.classList.toggle('is-busy', on);
		choose.disabled = on;
	}

	function showAlert(msg){
		if (!alertBox){ return; }
		alertBox.textContent = msg;
		alertBox.style.display = 'block';
	}
	/*
	 * Over-limit alert: bell + counted message (count bolded) on line one,
	 * pricing link + tail sentence on line two, built as DOM nodes.
	 */
	function showOverLimitAlert(staged, remaining){
		if (!alertBox){ return; }
		alertBox.textContent = '';
		alertBox.style.background = '#e8f1fc';
		alertBox.style.color = '#1d4ed8';

		var line1 = document.createElement('div');
		var bell = document.createElement('span');
		bell.textContent = '\u{1F514} ';
		line1.appendChild(bell);
		var parts = (cfg.overLimitAlert || '').split('%d');
		line1.appendChild(document.createTextNode(parts[0] || ''));
		var strong = document.createElement('strong');
		strong.textContent = String(remaining);
		line1.appendChild(strong);
		line1.appendChild(document.createTextNode(parts[1] || ''));
		alertBox.appendChild(line1);

		var line2 = document.createElement('div');
		var a = document.createElement('a');
		a.href = cfg.upgradeUrl;
		a.target = '_blank';
		a.rel = 'noopener';
		a.textContent = cfg.overLimitLink || '';
		a.style.color = 'inherit';
		a.style.fontWeight = '600';
		a.style.textDecoration = 'underline';
		line2.appendChild(a);
		line2.appendChild(document.createTextNode(' ' + (cfg.overLimitTail || '')));
		alertBox.appendChild(line2);

		alertBox.style.display = 'block';
	}
	function hideAlert(){
		if (!alertBox){ return; }
		alertBox.style.display = 'none';
		alertBox.style.background = '';
		alertBox.style.color = '';
	}

	function isAccepted(file){
		var name = (file.name || '').toLowerCase();
		return name.slice(-4) === '.pdf' || name.slice(-5) === '.docx';
	}

	function removeFile(f){
		if (busy){ return; }
		var idx = files.indexOf(f);
		if (idx > -1){ files.splice(idx, 1); }
		renderList();
		action.style.display = files.length ? 'flex' : 'none';
	}

	function chipStatus(f){
		var s = document.createElement('span');
		s.className = 'truebe-chip-status truebe-chip-status-' + (f.status || 'ready');
		if (f.status === 'failed' && f.code && (f.code === 'doc_limit' || f.code === 'tier_exceeded') && cfg.upgradeUrl){
			var a = document.createElement('a');
			a.href = cfg.upgradeUrl;
			a.target = '_blank';
			a.rel = 'noopener';
			a.textContent = f.message || cfg.statusFailed;
			s.appendChild(a);
		} else if (f.status === 'done'){
			s.textContent = '\u2713 ' + (f.statusText || cfg.statusDone);
		} else if (f.status === 'working'){
			var spin = document.createElement('span');
			spin.className = 'truebe-chip-spinner';
			spin.setAttribute('aria-label', f.statusText || cfg.statusWorking);
			s.appendChild(spin);
		} else {
			s.textContent = f.statusText || '';
		}
		return s;
	}

	function renderList(){
		list.innerHTML = '';
		files.forEach(function(f){
			var li = document.createElement('li');
			li.className = 'truebe-chip';
			var ic = document.createElement('span');
			ic.className = 'dashicons dashicons-media-document truebe-chip-icon';
			li.appendChild(ic);
			var n = document.createElement('span');
			n.className = 'truebe-chip-name';
			n.textContent = f.file.name;
			n.title = f.file.name;
			li.appendChild(n);
			if (f.status === 'ready' && !busy){
				var x = document.createElement('button');
				x.type = 'button';
				x.className = 'truebe-chip-remove';
				x.setAttribute('aria-label', 'Remove ' + f.file.name);
				x.textContent = '\u00d7';
				x.addEventListener('click', function(e){ e.stopPropagation(); removeFile(f); });
				li.appendChild(x);
			} else {
				li.appendChild(chipStatus(f));
			}
			list.appendChild(li);
		});
	}

	function setStatus(f, status, text, code, message){
		f.status = status;
		f.statusText = text;
		f.code = code || '';
		f.message = message || '';
		renderList();
	}

	function addFiles(fileList){
		var rejected = false;
		Array.prototype.forEach.call(fileList, function(file){
			if (!isAccepted(file)){ rejected = true; return; }
			var dupe = files.some(function(f){ return f.file.name === file.name && f.file.size === file.size; });
			if (!dupe){ files.push({ file: file, status: 'ready', statusText: '' }); }
		});
		if (rejected){ showAlert(cfg.badTypeAlert); } else { hideAlert(); }
		renderList();
		action.style.display = files.length ? 'flex' : 'none';
	}

	function resetAll(){
		runToken++;
		resetCrawl();
		hideAlert();
		files = [];
		input.value = '';
		renderList();
		prog.style.display = 'none';
		fill.style.width = '0%';
		fill.style.background = '';
		plabel.textContent = '';
		success.style.display = 'none';
		card.style.display = '';
		action.style.display = 'none';
		if (dropIcon){ dropIcon.style.display = ''; }
		if (dropTitle){ dropTitle.style.display = ''; }
		submit.textContent = cfg.labelUpload;
		submit.disabled = false;
		setBusy(false);
	}

	choose.addEventListener('click', function(){ if (busy){ return; } input.click(); });
	drop.addEventListener('click', function(e){ if (busy || e.target === choose || e.target.closest('#truebe-file-list')) return; input.click(); });
	input.addEventListener('change', function(){ if (input.files.length){ addFiles(input.files); input.value = ''; } });

	var another = document.getElementById('truebe-another');
	if (another){ another.addEventListener('click', resetAll); }

	['dragenter','dragover'].forEach(function(ev){ drop.addEventListener(ev, function(e){ e.preventDefault(); if (busy){ return; } card.classList.add('is-over'); }); });
	['dragleave','drop'].forEach(function(ev){ drop.addEventListener(ev, function(e){ e.preventDefault(); card.classList.remove('is-over'); }); });
	drop.addEventListener('drop', function(e){ if (busy){ return; } if (e.dataTransfer.files.length){ addFiles(e.dataTransfer.files); } });

	function pollJob(job, f, token){
		return new Promise(function(resolve){
			function tick(){
				if (token !== runToken){ resolve(); return; }
				fetch(cfg.ajax + '?action=truebe_job_status&job=' + encodeURIComponent(job) + '&_ajax_nonce=' + encodeURIComponent(cfg.nonce))
					.then(function(r){ return r.json(); })
					.then(function(j){
						if (token !== runToken){ resolve(); return; }
						if (!j || !j.success){ setTimeout(tick, 2000); return; }
						var s = j.data;
						if (s.state === 'done'){ setStatus(f, 'done', cfg.statusDone); resolve(); return; }
						if (s.state === 'error'){ setStatus(f, 'failed', s.label || cfg.statusFailed); resolve(); return; }
						if (s.label === cfg.labelBuildingMatch){ crawlLabel = cfg.labelBuildingFrom.replace('%s', f.file.name); }
						else if (s.label){ crawlLabel = s.label; }
						paint();
						setTimeout(tick, 1500);
					})
					.catch(function(){ setTimeout(tick, 2000); });
			}
			tick();
		});
	}

	function uploadOne(f, index, total, token){
		return new Promise(function(resolve){
			if (token !== runToken){ resolve(false); return; }
			setStatus(f, 'working', cfg.statusWorking);
			setSlice(index, total);
			crawlLabel = cfg.labelStart + ': ' + f.file.name;
			paint();
			var data = new FormData();
			data.append('action', form.querySelector('input[name="action"]').value);
			data.append('_wpnonce', cfg.nonce);
			data.append('truebe_public_attested', '1');
			data.append('truebe_document', f.file, f.file.name);
			fetch(cfg.ajax, { method:'POST', body:data, credentials:'same-origin' })
				.then(function(r){ return r.json(); })
				.then(function(j){
					if (token !== runToken){ resolve(false); return; }
					if (!j || !j.success){
						var code = (j && j.data && j.data.code) ? j.data.code : '';
						var msg  = (j && j.data && j.data.message) ? j.data.message : cfg.uploadFailed;
						setStatus(f, 'failed', msg, code, msg);
						completeSlice(index, total);
						resolve(code === 'doc_limit' || code === 'tier_exceeded' ? 'stop' : false);
						return;
					}
					var job = j.data.job_id;
					var pd = new URLSearchParams(); pd.append('action','truebe_process'); pd.append('job', job); pd.append('_ajax_nonce', cfg.nonce);
					fetch(cfg.ajax, { method:'POST', body:pd, credentials:'same-origin' });
					pollJob(job, f, token).then(function(){
						completeSlice(index, total);
						resolve(true);
					});
				})
				.catch(function(){ setStatus(f, 'failed', cfg.uploadFailed); completeSlice(index, total); resolve(false); });
		});
	}

	function finishRun(){
		resetCrawl();
		prog.style.display = 'none';
		action.style.display = 'none';
		card.style.display = 'none';
		var done = files.filter(function(f){ return f.status === 'done'; }).length;
		if (successTitle){ successTitle.textContent = done > 0 ? cfg.titleReady : cfg.titleNone; }
		if (successCheck){ successCheck.style.display = done > 0 ? '' : 'none'; }
		successSub.textContent = cfg.summaryDone.replace('%1$s', done).replace('%2$s', files.length);
		successSub.style.display = 'block';
		success.style.display = 'block';
		setBusy(false);
	}

	form.addEventListener('submit', function(e){
		e.preventDefault();
		if (busy || !files.length){ return; }
		var ready = files.filter(function(f){ return f.status === 'ready'; });
		/* Pre-flight: block the whole batch before any upload if it would blow the plan's doc limit. */
		var remaining = (typeof cfg.docsRemaining !== 'undefined') ? parseInt(cfg.docsRemaining, 10) : -1;
		if (remaining >= 0 && ready.length > remaining){
			showOverLimitAlert(ready.length, remaining);
			return;
		}
		var token = ++runToken;
		setBusy(true);
		hideAlert();
		submit.disabled = true;
		submit.textContent = cfg.labelUploading;
		if (dropIcon){ dropIcon.style.display = 'none'; }
		if (dropTitle){ dropTitle.style.display = 'none'; }
		fill.style.background = '';
		renderList();
		resetCrawl();
		startCrawl();
		var batch = ready;
		var total = batch.length;
		var i = 0;
		function next(){
			if (token !== runToken){ return; }
			if (i >= total){ finishRun(); return; }
			var f = batch[i];
			var index = i;
			i++;
			uploadOne(f, index, total, token).then(function(result){
				if (token !== runToken){ return; }
				if (result === 'stop'){
					for (var k = i; k < total; k++){
						if (batch[k].status === 'ready'){ setStatus(batch[k], 'skipped', cfg.statusSkipped); }
					}
					finishRun();
					return;
				}
				next();
			});
		}
		next();
	});
})();