(function () {
	'use strict';

	const config = window.LocalGuardScannerAdmin;
	const startButton = document.getElementById('lgs-start-scan');
	const cancelButton = document.getElementById('lgs-cancel-scan');
	const progress = document.getElementById('lgs-progress');

	if (!config) return;

	const element = (tag, className, text) => {
		const node = document.createElement(tag);
		if (className) node.className = className;
		if (typeof text === 'string') node.textContent = text;
		return node;
	};

	document.querySelectorAll('.lgs-copy-value').forEach((button) => {
		button.hidden = false;
		button.addEventListener('click', async () => {
			const status = button.parentElement ? button.parentElement.querySelector('.lgs-copy-status') : null;
			try {
				if (!navigator.clipboard || !navigator.clipboard.writeText) throw new Error();
				await navigator.clipboard.writeText(button.dataset.copyValue || '');
				if (status) status.textContent = config.labels.copied;
			} catch (error) {
				if (status) status.textContent = config.labels.copyFailed;
			}
		});
	});

	if (!progress) return;

	const request = async (action, scanId) => {
		const body = new URLSearchParams({ action, nonce: config.nonce });
		if (scanId) body.set('scan_id', scanId);
		const controller = window.AbortController ? new AbortController() : null;
		const timeout = controller ? window.setTimeout(() => controller.abort(), 45000) : null;
		try {
			const response = await fetch(config.ajaxUrl, {
				method: 'POST',
				credentials: 'same-origin',
				headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
				body: body.toString(),
				signal: controller ? controller.signal : undefined
			});
			return await response.json();
		} finally {
			if (timeout) window.clearTimeout(timeout);
		}
	};

	const phaseLabel = (phase) => config.labels.phases && config.labels.phases[phase] ? config.labels.phases[phase] : config.labels.running;
	const activityLabel = (activity) => config.labels.activities && config.labels.activities[activity] ? config.labels.activities[activity] : config.labels.running;
	const formatNumber = (value) => new Intl.NumberFormat().format(Math.max(0, Number(value) || 0));

	const checkpointAge = (value) => {
		if (!value) return '';
		const timestamp = Date.parse(value.replace(' ', 'T') + 'Z');
		if (!Number.isFinite(timestamp)) return '';
		const seconds = Math.max(0, Math.round((Date.now() - timestamp) / 1000));
		return seconds < 10 ? config.labels.now : seconds + ' s';
	};

	const showMessage = (message, className) => {
		progress.replaceChildren(element('p', className || '', message));
	};

	const appendMetric = (container, value, label, danger) => {
		const metric = element('div', 'lgs-progress-metric');
		metric.append(element('strong', danger ? 'lgs-progress-alert' : '', formatNumber(value)), element('span', '', label));
		container.append(metric);
	};

	const appendStages = (container, phase, status) => {
		const stages = [
			['discovering', config.labels.phases.discovering],
			['scanning_files', config.labels.phases.scanning_files],
			['scanning_database', config.labels.phases.scanning_database],
			['classifying', config.labels.phases.classifying]
		];
		const current = Math.max(0, stages.findIndex((entry) => entry[0] === phase));
		const trail = element('ol', 'lgs-progress-stages');
		stages.forEach((entry, index) => {
			const itemClass = index < current || ['completed', 'partial'].includes(status) ? 'is-complete' : (index === current ? 'is-current' : '');
			trail.append(element('li', itemClass, entry[1]));
		});
		container.append(trail);
	};

	const appendCoverage = (container, coverage) => {
		if (!coverage || !coverage.structured || !coverage.scopes) return;
		const section = element('section', 'lgs-progress-coverage');
		section.append(element('h3', '', config.labels.coverageTitle), element('p', 'description', config.labels.coverageNote));
		const list = element('ul', 'lgs-coverage lgs-coverage-scopes');
		['files', 'database', 'plugins'].forEach((scope) => {
			const summary = coverage.scopes[scope];
			if (!summary) return;
			const item = element('li');
			const title = element('strong', '', (config.labels.coverageScopes[scope] || scope) + ':');
			item.append(title, document.createTextNode(' ' + (summary.verified ? config.labels.verified : config.labels.incomplete)));
			const reasons = summary.reasons || {};
			const reasonKeys = Object.keys(reasons);
			if (reasonKeys.length) {
				const reasonList = element('ul');
				reasonKeys.forEach((reason) => {
					const label = config.labels.coverageReasons[reason] || reason;
					reasonList.append(element('li', '', label + ': ' + formatNumber(reasons[reason])));
				});
				item.append(reasonList);
			}
			list.append(item);
		});
		section.append(list);
		container.append(section);
	};

	const appendFailure = (container, scan) => {
		const failure = scan.failure || {};
		const callout = element('div', 'lgs-callout lgs-callout-danger');
		const text = element('p');
		text.append(element('strong', '', config.labels.noSiteChange), document.createTextNode(' ' + (failure.message || config.labels.failed)));
		callout.append(text);
		container.append(callout);

		const details = element('dl', 'lgs-failure-details');
		[[config.labels.stage, phaseLabel(failure.stage)], [config.labels.operation, failure.operation || ''], [config.labels.code, failure.code || 'LGS-UNEXPECTED']].forEach((row) => {
			details.append(element('dt', '', row[0]), element('dd', '', row[1]));
		});
		container.append(details);

		const actions = element('p', 'lgs-actions');
		const copy = element('button', 'button', config.labels.copyDiagnostic);
		copy.type = 'button';
		const copyStatus = element('span', 'lgs-copy-status');
		copy.addEventListener('click', async () => {
			try {
				if (!navigator.clipboard || !navigator.clipboard.writeText) throw new Error();
				await navigator.clipboard.writeText(failure.diagnostic || failure.code || 'LGS-UNEXPECTED');
				copyStatus.textContent = config.labels.diagnosticCopied;
			} catch (error) {
				copyStatus.textContent = config.labels.copyFailed;
			}
		});
		actions.append(copy, copyStatus);
		container.append(actions, element('p', 'description', config.labels.diagnosticSafe));
	};

	const showScan = (scan) => {
		const data = scan.progress || {};
		const totals = data.totals || {};
		const processed = data.processed || {};
		const discovered = data.discovered || {};
		const findings = data.findings || {};
		const reconciliation = data.reconciliation || {};
		const terminal = Boolean(scan.finished);
		const wrapper = element('div', 'lgs-progress-panel' + (scan.status === 'failed' ? ' is-failed' : ''));
		const eyebrow = scan.status === 'failed' ? activityLabel('stopped') : (terminal ? activityLabel('finished') : activityLabel(scan.activity) + ' · ' + config.labels.provisional);
		wrapper.append(element('p', 'lgs-progress-eyebrow', eyebrow));

		const heading = element('div', 'lgs-progress-heading');
		let title = phaseLabel(scan.phase);
		if (scan.status === 'completed') title = config.labels.complete;
		if (scan.status === 'partial') title = config.labels.partial;
		if (scan.status === 'cancelled') title = config.labels.cancelled;
		if (scan.status === 'failed' && scan.failure) title = scan.failure.message || config.labels.failed;
		heading.append(element('h2', '', title));
		if (data.percentage !== null && typeof data.percentage !== 'undefined') heading.append(element('strong', 'lgs-progress-percent', data.percentage + ' %'));
		wrapper.append(heading);

		const meter = element('progress');
		meter.max = 100;
		if (data.percentage !== null && typeof data.percentage !== 'undefined') meter.value = data.percentage;
		wrapper.append(meter);

		const total = (Number(totals.file_objects) || 0) + (Number(totals.database_objects) || 0);
		const done = (Number(processed.file_objects) || 0) + (Number(processed.database_objects) || 0);
		const found = (Number(discovered.file_objects) || 0) + (Number(discovered.database_objects) || 0);
		const metaText = scan.phase === 'discovering' ? formatNumber(found) + ' ' + config.labels.elements : formatNumber(done) + ' / ' + formatNumber(total) + ' ' + config.labels.elements;
		const age = checkpointAge(scan.last_checkpoint_at);
		wrapper.append(element('p', 'lgs-progress-meta', metaText + (age ? ' · ' + config.labels.activity + ': ' + age : '')));

		const metrics = element('div', 'lgs-progress-metrics');
		appendMetric(metrics, processed.file_objects, config.labels.files, false);
		appendMetric(metrics, processed.database_objects, config.labels.database, false);
		appendMetric(metrics, findings.review_required, config.labels.review, true);
		appendMetric(metrics, findings.informational, config.labels.informational, false);
		wrapper.append(metrics);
		if (scan.phase === 'classifying') {
			wrapper.append(element('p', 'lgs-progress-meta', formatNumber(reconciliation.checked) + ' ' + config.labels.previousChecked + ' · ' + formatNumber(reconciliation.resolved) + ' ' + config.labels.movedToHistory));
		}
		appendStages(wrapper, scan.failure && scan.failure.stage ? scan.failure.stage : scan.phase, scan.status);
		if (terminal) appendCoverage(wrapper, scan.coverage);

		if (data.current_area) wrapper.append(element('p', 'lgs-progress-area', config.labels.area + ': ' + data.current_area + ' · ' + config.labels.batch + ' ' + formatNumber(data.batch_number)));
		if (scan.status === 'failed') appendFailure(wrapper, scan);
		progress.replaceChildren(wrapper);
	};

	const run = async (scanId) => {
		try {
			const response = await request('localguard_run_scan', scanId);
			if (!response.success) throw new Error(response.data && response.data.message ? response.data.message : config.labels.failed);
			const scan = response.data;
			showScan(scan);
			if (scan.finished) {
				if (startButton) startButton.disabled = false;
				if (cancelButton) cancelButton.hidden = true;
				return;
			}
			window.setTimeout(() => run(scanId), 600);
		} catch (error) {
			showMessage(error.name === 'AbortError' ? config.labels.requestFailed : (error.message || config.labels.failed), 'lgs-callout lgs-callout-warning');
			if (startButton) startButton.disabled = false;
		}
	};

	if (startButton) {
		startButton.addEventListener('click', async () => {
			try {
				startButton.disabled = true;
				showMessage(config.labels.running);
				const response = await request('localguard_start_scan');
				if (!response.success) throw new Error(response.data && response.data.message ? response.data.message : config.labels.failed);
				progress.dataset.scanId = response.data.scan_id;
				if (cancelButton) cancelButton.hidden = false;
				run(response.data.scan_id);
			} catch (error) {
				showMessage(error.message || config.labels.failed, 'lgs-callout lgs-callout-warning');
				startButton.disabled = false;
			}
		});
	}

	if (cancelButton) {
		cancelButton.addEventListener('click', async () => {
			try {
				await request('localguard_cancel_scan', progress.dataset.scanId);
				window.location.reload();
			} catch (error) {
				showMessage(error.message || config.labels.failed, 'lgs-callout lgs-callout-warning');
			}
		});
	}

	if (progress.dataset.scanId) run(progress.dataset.scanId);
}());
