function escapeHtml(value) {
	return String(value)
		.replace(/&/g, '&amp;')
		.replace(/</g, '&lt;')
		.replace(/>/g, '&gt;')
		.replace(/"/g, '&quot;')
		.replace(/'/g, '&#039;');
}

function setStatus(statusLabel, text, type) {
	statusLabel.textContent = text;
	statusLabel.classList.remove('error', 'success');
	if (type) {
		statusLabel.classList.add(type);
	}
}

function buildPointForm(lat, lng) {
	return `
		<div class="form-popup">
			<h3>Tambah Data SPBU</h3>
			<div class="field">
				<label>Nama SPBU</label>
				<input type="text" data-field="nama" placeholder="Contoh: SPBU Pertamina 34.XXX" required>
			</div>
			<div class="field">
				<label>No</label>
				<input type="text" data-field="no" placeholder="Contoh: 001" required>
			</div>
			<div class="field">
				<label>Status 24 Jam</label>
				<select data-field="status_24jam">
					<option value="24jam">24 Jam</option>
					<option value="tidak">Tidak</option>
				</select>
			</div>
			<div class="field">
				<label>Latitude</label>
				<input type="text" data-field="latitude" value="${lat.toFixed(6)}" readonly>
			</div>
			<div class="field">
				<label>Longitude</label>
				<input type="text" data-field="longitude" value="${lng.toFixed(6)}" readonly>
			</div>
			<div class="actions">
				<button class="btn-save" type="button" data-action="save">Save</button>
				<button class="btn-cancel" type="button" data-action="cancel">Cancel</button>
			</div>
			<div class="status" data-role="status">Klik Save untuk menyimpan data ke database.</div>
		</div>
	`;
}

function roadColorByStatus(status) {
	if (status === 'nasional') {
		return '#dc2626';
	}
	if (status === 'provinsi') {
		return '#2563eb';
	}
	return '#16a34a';
}

function parcelColorByStatus(status) {
	if (status === 'SHM') {
		return '#ea580c';
	}
	if (status === 'HGB') {
		return '#0ea5e9';
	}
	if (status === 'HGU') {
		return '#22c55e';
	}
	return '#a855f7';
}

function calculatePolylineLengthMeters(latlngs) {
	let total = 0;
	for (let index = 1; index < latlngs.length; index += 1) {
		total += latlngs[index - 1].distanceTo(latlngs[index]);
	}
	return total;
}

function calculatePolygonAreaMeters(latlngs) {
	if (window.L && L.GeometryUtil && typeof L.GeometryUtil.geodesicArea === 'function') {
		return L.GeometryUtil.geodesicArea(latlngs);
	}

	const earthRadius = 6378137;
	const toRad = (degree) => degree * Math.PI / 180;

	let area = 0;
	for (let index = 0; index < latlngs.length; index += 1) {
		const pointA = latlngs[index];
		const pointB = latlngs[(index + 1) % latlngs.length];
		area += toRad(pointB.lng - pointA.lng) * (2 + Math.sin(toRad(pointA.lat)) + Math.sin(toRad(pointB.lat)));
	}

	return Math.abs(area * earthRadius * earthRadius / 2);
}

function polylineToWkt(latlngs) {
	const coordinates = latlngs.map((point) => `${point.lng} ${point.lat}`).join(', ');
	return `LINESTRING(${coordinates})`;
}

function polygonToWkt(latlngs) {
	const ring = latlngs.map((point) => `${point.lng} ${point.lat}`);
	if (ring[0] !== ring[ring.length - 1]) {
		ring.push(ring[0]);
	}
	return `POLYGON((${ring.join(', ')}))`;
}

async function parseJsonResponse(response) {
	const rawText = await response.text();

	try {
		return JSON.parse(rawText);
	} catch (error) {
		const preview = String(rawText || '').slice(0, 180).trim();
		if (preview) {
			throw new Error(`Respons server bukan JSON yang valid: ${preview}`);
		}
		throw new Error('Respons server bukan JSON yang valid.');
	}
}

async function initializeMap() {
	if (typeof L === 'undefined') {
		alert('Leaflet gagal dimuat. Periksa koneksi internet.');
		return;
	}

	const mapContainer = document.getElementById('map');
	if (!mapContainer) {
		return;
	}

	const map = L.map('map', {
		zoomControl: true
	}).setView([-0.06054796552220232, 109.34490231930592], 13);

	L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
		maxZoom: 19,
		attribution: '&copy; OpenStreetMap contributors'
	}).addTo(map);

	if (typeof window.addKecamatanLayer === 'function') {
		window.addKecamatanLayer(map, { fitBounds: true }).catch((error) => {
			console.warn('Data kecamatan belum termuat:', error);
		});
	}

	const markersLayer = L.featureGroup().addTo(map);
	const spatialLayer = L.featureGroup().addTo(map);

	let activePopupMarker = null;
	let isDrawing = false;
	let activeRefineState = null;

	const blueIcon = L.icon({
		iconUrl: 'assets/vendor/leaflet/images/marker-icon.png',
		iconRetinaUrl: 'assets/vendor/leaflet/images/marker-icon-2x.png',
		shadowUrl: 'assets/vendor/leaflet/images/marker-shadow.png',
		iconSize: [25, 41],
		iconAnchor: [12, 41],
		popupAnchor: [1, -34],
		shadowSize: [41, 41]
	});

	const yellowIcon = L.icon({
		iconUrl: 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-yellow.png',
		iconRetinaUrl: 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-yellow.png',
		shadowUrl: 'assets/vendor/leaflet/images/marker-shadow.png',
		iconSize: [25, 41],
		iconAnchor: [12, 41],
		popupAnchor: [1, -34],
		shadowSize: [41, 41]
	});

	const modalBackdrop = document.getElementById('modal-backdrop');
	const modalTitle = document.getElementById('modal-title');
	const featureForm = document.getElementById('feature-form');
	const roadFields = document.getElementById('road-fields');
	const parcelFields = document.getElementById('parcel-fields');
	const formStatus = document.getElementById('form-status');
	const cancelButton = document.getElementById('btn-cancel');

	let pendingLayer = null;
	let pendingType = null;
	let pendingMode = 'create';

	function setSectionEnabled(sectionElement, enabled) {
		if (!sectionElement) {
			return;
		}

		const fields = sectionElement.querySelectorAll('input, select, textarea, button');
		fields.forEach((field) => {
			field.disabled = !enabled;
		});
	}

	function getPointIcon(status24Jam) {
		const normalizedStatus = String(status24Jam || '').toLowerCase();
		if (normalizedStatus === '24jam') {
			return blueIcon;
		}
		return yellowIcon;
	}

	function fitAllData() {
		const combined = L.featureGroup();
		markersLayer.eachLayer((layer) => combined.addLayer(layer));
		spatialLayer.eachLayer((layer) => combined.addLayer(layer));

		if (combined.getLayers().length < 1) {
			return;
		}

		const bounds = combined.getBounds();
		if (!bounds.isValid()) {
			return;
		}

		map.fitBounds(bounds.pad(0.2), { maxZoom: 16 });
	}

	function cloneLatLngs(latlngs) {
		return JSON.parse(JSON.stringify(latlngs));
	}

	function setRefineButtonsState(refineButton, saveRefineButton, cancelRefineButton, isActive) {
		if (refineButton) {
			refineButton.disabled = isActive;
		}
		if (saveRefineButton) {
			saveRefineButton.disabled = !isActive;
		}
		if (cancelRefineButton) {
			cancelRefineButton.disabled = !isActive;
		}
	}

	async function persistSpatialGeometry(layer) {
		if (!layer || !layer.featureType || !layer.featureProps) {
			return;
		}

		if (layer.featureType === 'road') {
			const lengthMeters = calculatePolylineLengthMeters(layer.getLatLngs());
			const payload = {
				id: layer.featureId,
				nama_jalan: layer.featureProps.nama_jalan,
				status: layer.featureProps.status,
				panjang_meter: Number(lengthMeters.toFixed(2)),
				wkt: polylineToWkt(layer.getLatLngs())
			};

			const response = await fetch('api/road_update.php', {
				method: 'POST',
				headers: {
					'Content-Type': 'application/json',
					'Accept': 'application/json'
				},
				body: JSON.stringify(payload)
			});

			const result = await parseJsonResponse(response);
			if (!response.ok || !result.success) {
				throw new Error(result.message || 'Gagal update geometri jalan.');
			}

			layer.featureProps.panjang_meter = payload.panjang_meter;
			attachRoadPopup(layer);
			return;
		}

		if (layer.featureType === 'parcel') {
			const areaM2 = calculatePolygonAreaMeters(layer.getLatLngs()[0]);
			const payload = {
				id: layer.featureId,
				nama_pemilik: layer.featureProps.nama_pemilik,
				status_kepemilikan: layer.featureProps.status_kepemilikan,
				luas_m2: Number(areaM2.toFixed(2)),
				wkt: polygonToWkt(layer.getLatLngs()[0])
			};

			const response = await fetch('api/parcel_update.php', {
				method: 'POST',
				headers: {
					'Content-Type': 'application/json',
					'Accept': 'application/json'
				},
				body: JSON.stringify(payload)
			});

			const result = await parseJsonResponse(response);
			if (!response.ok || !result.success) {
				throw new Error(result.message || 'Gagal update geometri parsil.');
			}

			layer.featureProps.luas_m2 = payload.luas_m2;
			attachParcelPopup(layer);
		}
	}

	function closeModal(removePendingLayer) {
		if (!modalBackdrop || !featureForm || !formStatus) {
			return;
		}

		modalBackdrop.classList.remove('open');
		modalBackdrop.setAttribute('aria-hidden', 'true');
		setStatus(formStatus, '', '');

		if (removePendingLayer && pendingMode === 'create' && pendingLayer) {
			spatialLayer.removeLayer(pendingLayer);
		}

		pendingLayer = null;
		pendingType = null;
		pendingMode = 'create';
		featureForm.reset();
		setSectionEnabled(roadFields, false);
		setSectionEnabled(parcelFields, false);
	}

	function openRoadModal(layer, mode, initialData) {
		if (!modalBackdrop || !modalTitle || !roadFields || !parcelFields) {
			return;
		}

		pendingLayer = layer;
		pendingType = 'road';
		pendingMode = mode;

		modalTitle.textContent = mode === 'create' ? 'Tambah Data Jalan' : 'Update Data Jalan';
		roadFields.hidden = false;
		parcelFields.hidden = true;
		setSectionEnabled(roadFields, true);
		setSectionEnabled(parcelFields, false);

		const latlngs = layer.getLatLngs();
		const lengthMeters = calculatePolylineLengthMeters(latlngs);

		document.getElementById('nama_jalan').value = initialData && initialData.nama_jalan ? initialData.nama_jalan : '';
		document.getElementById('status_jalan').value = initialData && initialData.status ? initialData.status : 'nasional';
		document.getElementById('panjang_meter').value = lengthMeters.toFixed(2);

		modalBackdrop.classList.add('open');
		modalBackdrop.setAttribute('aria-hidden', 'false');
	}

	function openParcelModal(layer, mode, initialData) {
		if (!modalBackdrop || !modalTitle || !roadFields || !parcelFields) {
			return;
		}

		pendingLayer = layer;
		pendingType = 'parcel';
		pendingMode = mode;

		modalTitle.textContent = mode === 'create' ? 'Tambah Data Parsil Tanah' : 'Update Data Parsil Tanah';
		roadFields.hidden = true;
		parcelFields.hidden = false;
		setSectionEnabled(roadFields, false);
		setSectionEnabled(parcelFields, true);

		const latlngs = layer.getLatLngs()[0];
		const areaM2 = calculatePolygonAreaMeters(latlngs);

		document.getElementById('nama_pemilik').value = initialData && initialData.nama_pemilik ? initialData.nama_pemilik : '';
		document.getElementById('status_kepemilikan').value = initialData && initialData.status_kepemilikan ? initialData.status_kepemilikan : 'SHM';
		document.getElementById('luas_m2').value = areaM2.toFixed(2);

		modalBackdrop.classList.add('open');
		modalBackdrop.setAttribute('aria-hidden', 'false');
	}

	function renderPoint(point) {
		const pointId = Number(point.id);
		const latitude = Number(point.latitude);
		const longitude = Number(point.longitude);

		if (!Number.isFinite(pointId) || !Number.isFinite(latitude) || !Number.isFinite(longitude)) {
			return;
		}

		const marker = L.marker([latitude, longitude], {
			icon: getPointIcon(point.status_24jam),
			draggable: true
		});

		marker.addTo(markersLayer);
		marker.bindPopup(`
			<div class="point-popup">
				<b>${escapeHtml(point.nama || '')}</b><br>
				No: ${escapeHtml(point.no || '')}<br>
				Status: ${escapeHtml(point.status_24jam || '')}<br>
				Lat: ${latitude.toFixed(6)}<br>
				Lng: ${longitude.toFixed(6)}
				<div class="meta">Geser marker untuk memperbarui posisi titik.</div>
				<div class="meta">ID: ${pointId}</div>
				<button class="btn-delete" type="button" data-action="delete" data-point-id="${pointId}">Hapus Titik</button>
				<div class="status" data-role="delete-status"></div>
			</div>
		`);

		let previousLatLng = null;

		marker.on('dragstart', () => {
			previousLatLng = marker.getLatLng();
		});

		marker.on('dragend', async () => {
			const latestLatLng = marker.getLatLng();
			try {
				await updatePointPosition(pointId, latestLatLng.lat, latestLatLng.lng);
				await loadPoints();
			} catch (error) {
				if (previousLatLng) {
					marker.setLatLng(previousLatLng);
				}
				alert(error.message || 'Gagal memperbarui posisi titik.');
			}
		});

		marker.on('popupopen', (popupEvent) => {
			const popupElement = popupEvent.popup.getElement();
			if (!popupElement) {
				return;
			}

			const deleteButton = popupElement.querySelector('[data-action="delete"]');
			const deleteStatus = popupElement.querySelector('[data-role="delete-status"]');

			if (!deleteButton || !deleteStatus) {
				return;
			}

			deleteButton.addEventListener('click', async () => {
				const requestedId = Number(deleteButton.getAttribute('data-point-id'));
				if (!Number.isFinite(requestedId) || requestedId <= 0) {
					setStatus(deleteStatus, 'ID titik tidak valid.', 'error');
					return;
				}

				const isConfirmed = window.confirm('Hapus titik ini dari peta dan database?');
				if (!isConfirmed) {
					return;
				}

				deleteButton.disabled = true;
				setStatus(deleteStatus, 'Menghapus data...', '');

				try {
					await deletePoint(requestedId);
					setStatus(deleteStatus, 'Data berhasil dihapus.', 'success');
					map.closePopup();
					await loadPoints();
				} catch (error) {
					setStatus(deleteStatus, error.message || 'Gagal menghapus data.', 'error');
					deleteButton.disabled = false;
				}
			});
		});
	}

	function attachRoadPopup(layer) {
		const props = layer.featureProps;
		layer.setStyle({ color: roadColorByStatus(props.status), weight: 5 });

		layer.bindPopup(`
			<b>Jalan:</b> ${escapeHtml(props.nama_jalan)}<br>
			<b>Status:</b> ${escapeHtml(props.status)}<br>
			<b>Panjang:</b> ${Number(props.panjang_meter).toFixed(2)} m
			<div class="popup-actions">
				<button type="button" class="btn-popup-edit" data-action="edit">Edit</button>
				<button type="button" class="btn-popup-refine" data-action="refine">Rapikan</button>
			</div>
			<div class="popup-actions">
				<button type="button" class="btn-popup-save" data-action="save-refine" disabled>Simpan Rapikan</button>
				<button type="button" class="btn-popup-cancel" data-action="cancel-refine" disabled>Batal Rapikan</button>
				<button type="button" class="btn-popup-delete" data-action="delete">Hapus</button>
			</div>
			<div class="status" data-role="popup-status"></div>
		`);

		layer.off('popupopen');
		layer.on('popupopen', (event) => {
			const popupElement = event.popup.getElement();
			if (!popupElement) {
				return;
			}

			const editButton = popupElement.querySelector('[data-action="edit"]');
			const refineButton = popupElement.querySelector('[data-action="refine"]');
			const saveRefineButton = popupElement.querySelector('[data-action="save-refine"]');
			const cancelRefineButton = popupElement.querySelector('[data-action="cancel-refine"]');
			const deleteButton = popupElement.querySelector('[data-action="delete"]');
			const popupStatus = popupElement.querySelector('[data-role="popup-status"]');

			if (!editButton || !refineButton || !saveRefineButton || !cancelRefineButton || !deleteButton || !popupStatus) {
				return;
			}

			const canRefine = !!(layer.editing && typeof layer.editing.enable === 'function');
			refineButton.disabled = !canRefine;
			setRefineButtonsState(refineButton, saveRefineButton, cancelRefineButton, activeRefineState && activeRefineState.layer === layer);

			refineButton.addEventListener('click', () => {
				if (!canRefine) {
					setStatus(popupStatus, 'Mode rapikan tidak didukung pada layer ini.', 'error');
					return;
				}

				if (activeRefineState && activeRefineState.layer && activeRefineState.layer !== layer && activeRefineState.layer.editing) {
					activeRefineState.layer.editing.disable();
				}

				activeRefineState = {
					layer: layer,
					snapshot: cloneLatLngs(layer.getLatLngs())
				};

				layer.editing.enable();
				setRefineButtonsState(refineButton, saveRefineButton, cancelRefineButton, true);
				setStatus(popupStatus, 'Mode rapikan aktif. Geser titik vertex lalu klik Simpan Rapikan.', '');
			});

			saveRefineButton.addEventListener('click', async () => {
				if (!activeRefineState || activeRefineState.layer !== layer) {
					return;
				}

				saveRefineButton.disabled = true;
				setStatus(popupStatus, 'Menyimpan geometri...', '');

				try {
					await persistSpatialGeometry(layer);
					if (layer.editing) {
						layer.editing.disable();
					}
					activeRefineState = null;
					setRefineButtonsState(refineButton, saveRefineButton, cancelRefineButton, false);
					setStatus(popupStatus, 'Geometri jalan berhasil dirapikan.', 'success');
				} catch (error) {
					setStatus(popupStatus, error.message || 'Gagal menyimpan geometri.', 'error');
					saveRefineButton.disabled = false;
				}
			});

			cancelRefineButton.addEventListener('click', () => {
				if (!activeRefineState || activeRefineState.layer !== layer) {
					return;
				}

				layer.setLatLngs(activeRefineState.snapshot);
				if (typeof layer.redraw === 'function') {
					layer.redraw();
				}

				if (layer.editing) {
					layer.editing.disable();
				}

				activeRefineState = null;
				setRefineButtonsState(refineButton, saveRefineButton, cancelRefineButton, false);
				setStatus(popupStatus, 'Perubahan geometri dibatalkan.', '');
			});

			editButton.addEventListener('click', () => {
				map.closePopup();
				openRoadModal(layer, 'update', layer.featureProps);
			});

			deleteButton.addEventListener('click', async () => {
				if (!window.confirm('Hapus data jalan ini?')) {
					return;
				}

				setStatus(popupStatus, 'Menghapus data...', '');
				deleteButton.disabled = true;

				try {
					const response = await fetch('api/road_delete.php', {
						method: 'POST',
						headers: {
							'Content-Type': 'application/json',
							'Accept': 'application/json'
						},
						body: JSON.stringify({ id: layer.featureId })
					});

					const result = await parseJsonResponse(response);
					if (!response.ok || !result.success) {
						throw new Error(result.message || 'Gagal menghapus data jalan.');
					}

					spatialLayer.removeLayer(layer);
					map.closePopup();
				} catch (error) {
					setStatus(popupStatus, error.message || 'Gagal menghapus data.', 'error');
					deleteButton.disabled = false;
				}
			});
		});
	}

	function attachParcelPopup(layer) {
		const props = layer.featureProps;
		const color = parcelColorByStatus(props.status_kepemilikan);

		layer.setStyle({
			color: color,
			fillColor: color,
			fillOpacity: 0.35,
			weight: 3
		});

		layer.bindPopup(`
			<b>Pemilik:</b> ${escapeHtml(props.nama_pemilik)}<br>
			<b>Status:</b> ${escapeHtml(props.status_kepemilikan)}<br>
			<b>Luas:</b> ${Number(props.luas_m2).toFixed(2)} m2
			<div class="popup-actions">
				<button type="button" class="btn-popup-edit" data-action="edit">Edit</button>
				<button type="button" class="btn-popup-refine" data-action="refine">Rapikan</button>
			</div>
			<div class="popup-actions">
				<button type="button" class="btn-popup-save" data-action="save-refine" disabled>Simpan Rapikan</button>
				<button type="button" class="btn-popup-cancel" data-action="cancel-refine" disabled>Batal Rapikan</button>
				<button type="button" class="btn-popup-delete" data-action="delete">Hapus</button>
			</div>
			<div class="status" data-role="popup-status"></div>
		`);

		layer.off('popupopen');
		layer.on('popupopen', (event) => {
			const popupElement = event.popup.getElement();
			if (!popupElement) {
				return;
			}

			const editButton = popupElement.querySelector('[data-action="edit"]');
			const refineButton = popupElement.querySelector('[data-action="refine"]');
			const saveRefineButton = popupElement.querySelector('[data-action="save-refine"]');
			const cancelRefineButton = popupElement.querySelector('[data-action="cancel-refine"]');
			const deleteButton = popupElement.querySelector('[data-action="delete"]');
			const popupStatus = popupElement.querySelector('[data-role="popup-status"]');

			if (!editButton || !refineButton || !saveRefineButton || !cancelRefineButton || !deleteButton || !popupStatus) {
				return;
			}

			const canRefine = !!(layer.editing && typeof layer.editing.enable === 'function');
			refineButton.disabled = !canRefine;
			setRefineButtonsState(refineButton, saveRefineButton, cancelRefineButton, activeRefineState && activeRefineState.layer === layer);

			refineButton.addEventListener('click', () => {
				if (!canRefine) {
					setStatus(popupStatus, 'Mode rapikan tidak didukung pada layer ini.', 'error');
					return;
				}

				if (activeRefineState && activeRefineState.layer && activeRefineState.layer !== layer && activeRefineState.layer.editing) {
					activeRefineState.layer.editing.disable();
				}

				activeRefineState = {
					layer: layer,
					snapshot: cloneLatLngs(layer.getLatLngs())
				};

				layer.editing.enable();
				setRefineButtonsState(refineButton, saveRefineButton, cancelRefineButton, true);
				setStatus(popupStatus, 'Mode rapikan aktif. Geser titik vertex lalu klik Simpan Rapikan.', '');
			});

			saveRefineButton.addEventListener('click', async () => {
				if (!activeRefineState || activeRefineState.layer !== layer) {
					return;
				}

				saveRefineButton.disabled = true;
				setStatus(popupStatus, 'Menyimpan geometri...', '');

				try {
					await persistSpatialGeometry(layer);
					if (layer.editing) {
						layer.editing.disable();
					}
					activeRefineState = null;
					setRefineButtonsState(refineButton, saveRefineButton, cancelRefineButton, false);
					setStatus(popupStatus, 'Geometri parsil berhasil dirapikan.', 'success');
				} catch (error) {
					setStatus(popupStatus, error.message || 'Gagal menyimpan geometri.', 'error');
					saveRefineButton.disabled = false;
				}
			});

			cancelRefineButton.addEventListener('click', () => {
				if (!activeRefineState || activeRefineState.layer !== layer) {
					return;
				}

				layer.setLatLngs(activeRefineState.snapshot);
				if (typeof layer.redraw === 'function') {
					layer.redraw();
				}

				if (layer.editing) {
					layer.editing.disable();
				}

				activeRefineState = null;
				setRefineButtonsState(refineButton, saveRefineButton, cancelRefineButton, false);
				setStatus(popupStatus, 'Perubahan geometri dibatalkan.', '');
			});

			editButton.addEventListener('click', () => {
				map.closePopup();
				openParcelModal(layer, 'update', layer.featureProps);
			});

			deleteButton.addEventListener('click', async () => {
				if (!window.confirm('Hapus data parsil ini?')) {
					return;
				}

				setStatus(popupStatus, 'Menghapus data...', '');
				deleteButton.disabled = true;

				try {
					const response = await fetch('api/parcel_delete.php', {
						method: 'POST',
						headers: {
							'Content-Type': 'application/json',
							'Accept': 'application/json'
						},
						body: JSON.stringify({ id: layer.featureId })
					});

					const result = await parseJsonResponse(response);
					if (!response.ok || !result.success) {
						throw new Error(result.message || 'Gagal menghapus data parsil.');
					}

					spatialLayer.removeLayer(layer);
					map.closePopup();
				} catch (error) {
					setStatus(popupStatus, error.message || 'Gagal menghapus data.', 'error');
					deleteButton.disabled = false;
				}
			});
		});
	}

	async function loadPoints() {
		const response = await fetch('api/read_points.php', {
			headers: { 'Accept': 'application/json' }
		});

		const result = await parseJsonResponse(response);
		if (!response.ok || !result.success) {
			throw new Error(result && result.message ? result.message : 'Gagal memuat data titik.');
		}

		markersLayer.clearLayers();
		(result.data || []).forEach(renderPoint);
	}

	async function loadRoads() {
		const response = await fetch('api/road_read.php', {
			headers: { 'Accept': 'application/json' }
		});

		const result = await parseJsonResponse(response);
		if (!response.ok || !result.success) {
			throw new Error(result && result.message ? result.message : 'Gagal memuat data jalan.');
		}

		(result.data || []).forEach((item) => {
			if (!item.geometry_geojson) {
				return;
			}

			const parsed = L.geoJSON(item.geometry_geojson);
			const layer = parsed.getLayers()[0];
			if (!layer) {
				return;
			}

			layer.featureType = 'road';
			layer.featureId = item.id;
			layer.featureProps = {
				nama_jalan: item.nama_jalan,
				status: item.status,
				panjang_meter: item.panjang_meter
			};

			attachRoadPopup(layer);
			spatialLayer.addLayer(layer);
		});
	}

	async function loadParcels() {
		const response = await fetch('api/parcel_read.php', {
			headers: { 'Accept': 'application/json' }
		});

		const result = await parseJsonResponse(response);
		if (!response.ok || !result.success) {
			throw new Error(result && result.message ? result.message : 'Gagal memuat data parsil.');
		}

		(result.data || []).forEach((item) => {
			if (!item.geometry_geojson) {
				return;
			}

			const parsed = L.geoJSON(item.geometry_geojson);
			const layer = parsed.getLayers()[0];
			if (!layer) {
				return;
			}

			layer.featureType = 'parcel';
			layer.featureId = item.id;
			layer.featureProps = {
				nama_pemilik: item.nama_pemilik,
				status_kepemilikan: item.status_kepemilikan,
				luas_m2: item.luas_m2
			};

			attachParcelPopup(layer);
			spatialLayer.addLayer(layer);
		});
	}

	async function savePoint(payload) {
		const response = await fetch('api/create_point.php', {
			method: 'POST',
			headers: {
				'Content-Type': 'application/json',
				'Accept': 'application/json'
			},
			body: JSON.stringify(payload)
		});

		const result = await parseJsonResponse(response);
		if (!response.ok || !result.success) {
			throw new Error(result && result.message ? result.message : 'Gagal menyimpan data titik.');
		}
		return result;
	}

	async function deletePoint(id) {
		const response = await fetch('api/delete_point.php', {
			method: 'POST',
			headers: {
				'Content-Type': 'application/json',
				'Accept': 'application/json'
			},
			body: JSON.stringify({ id: id })
		});

		const result = await parseJsonResponse(response);
		if (!response.ok || !result.success) {
			throw new Error(result && result.message ? result.message : 'Gagal menghapus data titik.');
		}
		return result;
	}

	async function updatePointPosition(id, latitude, longitude) {
		const response = await fetch('api/update_point_position.php', {
			method: 'POST',
			headers: {
				'Content-Type': 'application/json',
				'Accept': 'application/json'
			},
			body: JSON.stringify({
				id: id,
				latitude: latitude,
				longitude: longitude
			})
		});

		const result = await parseJsonResponse(response);
		if (!response.ok || !result.success) {
			throw new Error(result && result.message ? result.message : 'Gagal memperbarui posisi titik.');
		}
		return result;
	}

	if (L.Control && L.Control.Draw) {
		const drawControl = new L.Control.Draw({
			position: 'topleft',
			draw: {
				polyline: {
					shapeOptions: { color: '#dc2626', weight: 5 }
				},
				polygon: {
					allowIntersection: false,
					showArea: true,
					shapeOptions: { color: '#ea580c', fillOpacity: 0.35, weight: 3 }
				},
				rectangle: false,
				circle: false,
				marker: false,
				circlemarker: false
			},
			edit: {
				featureGroup: spatialLayer,
				remove: true
			}
		});

		map.addControl(drawControl);

		map.on(L.Draw.Event.DRAWSTART, () => {
			isDrawing = true;
		});

		map.on(L.Draw.Event.DRAWSTOP, () => {
			isDrawing = false;
		});

		map.on(L.Draw.Event.CREATED, (event) => {
			const layer = event.layer;
			spatialLayer.addLayer(layer);

			if (event.layerType === 'polyline') {
				openRoadModal(layer, 'create', null);
				return;
			}

			if (event.layerType === 'polygon') {
				openParcelModal(layer, 'create', null);
				return;
			}

			spatialLayer.removeLayer(layer);
		});

		map.on(L.Draw.Event.EDITED, async (event) => {
			const layers = event.layers.getLayers();

			for (const layer of layers) {
				try {
					await persistSpatialGeometry(layer);
				} catch (error) {
					alert(error.message || 'Gagal menyimpan update geometri.');
				}
			}
		});

		map.on(L.Draw.Event.DELETED, async (event) => {
			const layers = event.layers.getLayers();

			for (const layer of layers) {
				if (!layer.featureType || !layer.featureId) {
					continue;
				}

				try {
					const endpoint = layer.featureType === 'road' ? 'api/road_delete.php' : 'api/parcel_delete.php';
					const response = await fetch(endpoint, {
						method: 'POST',
						headers: {
							'Content-Type': 'application/json',
							'Accept': 'application/json'
						},
						body: JSON.stringify({ id: layer.featureId })
					});

					const result = await parseJsonResponse(response);
					if (!response.ok || !result.success) {
						throw new Error(result.message || 'Gagal menghapus data spasial.');
					}
				} catch (error) {
					alert(error.message || 'Gagal sinkron delete ke database.');
				}
			}
		});
	}

	if (featureForm) {
		setSectionEnabled(roadFields, false);
		setSectionEnabled(parcelFields, false);

		featureForm.addEventListener('submit', async (event) => {
			event.preventDefault();
			if (!pendingLayer || !pendingType || !formStatus) {
				return;
			}

			setStatus(formStatus, 'Menyimpan data...', '');

			try {
				if (pendingType === 'road') {
					const namaJalan = document.getElementById('nama_jalan').value.trim();
					const statusJalan = document.getElementById('status_jalan').value;
					const panjangMeter = Number(document.getElementById('panjang_meter').value);
					const wkt = polylineToWkt(pendingLayer.getLatLngs());

					const endpoint = pendingMode === 'create' ? 'api/road_create.php' : 'api/road_update.php';
					const payload = {
						nama_jalan: namaJalan,
						status: statusJalan,
						panjang_meter: panjangMeter,
						wkt: wkt
					};

					if (pendingMode === 'update') {
						payload.id = pendingLayer.featureId;
					}

					const response = await fetch(endpoint, {
						method: 'POST',
						headers: {
							'Content-Type': 'application/json',
							'Accept': 'application/json'
						},
						body: JSON.stringify(payload)
					});

					const result = await parseJsonResponse(response);
					if (!response.ok || !result.success) {
						throw new Error(result.message || 'Gagal menyimpan data jalan.');
					}

					pendingLayer.featureType = 'road';
					pendingLayer.featureId = pendingMode === 'create' ? result.id : pendingLayer.featureId;
					pendingLayer.featureProps = {
						nama_jalan: namaJalan,
						status: statusJalan,
						panjang_meter: panjangMeter
					};

					attachRoadPopup(pendingLayer);
				}

				if (pendingType === 'parcel') {
					const namaPemilik = document.getElementById('nama_pemilik').value.trim();
					const statusKepemilikan = document.getElementById('status_kepemilikan').value;
					const luasM2 = Number(document.getElementById('luas_m2').value);
					const wkt = polygonToWkt(pendingLayer.getLatLngs()[0]);

					const endpoint = pendingMode === 'create' ? 'api/parcel_create.php' : 'api/parcel_update.php';
					const payload = {
						nama_pemilik: namaPemilik,
						status_kepemilikan: statusKepemilikan,
						luas_m2: luasM2,
						wkt: wkt
					};

					if (pendingMode === 'update') {
						payload.id = pendingLayer.featureId;
					}

					const response = await fetch(endpoint, {
						method: 'POST',
						headers: {
							'Content-Type': 'application/json',
							'Accept': 'application/json'
						},
						body: JSON.stringify(payload)
					});

					const result = await parseJsonResponse(response);
					if (!response.ok || !result.success) {
						throw new Error(result.message || 'Gagal menyimpan data parsil.');
					}

					pendingLayer.featureType = 'parcel';
					pendingLayer.featureId = pendingMode === 'create' ? result.id : pendingLayer.featureId;
					pendingLayer.featureProps = {
						nama_pemilik: namaPemilik,
						status_kepemilikan: statusKepemilikan,
						luas_m2: luasM2
					};

					attachParcelPopup(pendingLayer);
				}

				setStatus(formStatus, 'Data berhasil disimpan.', 'success');
				setTimeout(() => closeModal(false), 300);
			} catch (error) {
				setStatus(formStatus, error.message || 'Gagal menyimpan data.', 'error');
			}
		});
	}

	if (cancelButton) {
		cancelButton.addEventListener('click', () => {
			closeModal(true);
		});
	}

	if (modalBackdrop) {
		modalBackdrop.addEventListener('click', (event) => {
			if (event.target === modalBackdrop) {
				closeModal(true);
			}
		});
	}

	map.on('click', (event) => {
		if (activeRefineState && activeRefineState.layer && activeRefineState.layer.editing && activeRefineState.layer.editing.enabled()) {
			return;
		}

		if (isDrawing || (modalBackdrop && modalBackdrop.classList.contains('open'))) {
			return;
		}

		if (event.originalEvent && event.originalEvent.target && event.originalEvent.target.closest('.leaflet-interactive')) {
			return;
		}

		if (activePopupMarker) {
			map.removeLayer(activePopupMarker);
		}

		const lat = event.latlng.lat;
		const lng = event.latlng.lng;
		activePopupMarker = L.marker([lat, lng]).addTo(map);
		activePopupMarker.bindPopup(buildPointForm(lat, lng));

		activePopupMarker.once('popupopen', (popupEvent) => {
			const popupElement = popupEvent.popup.getElement();
			if (!popupElement) {
				return;
			}

			const saveButton = popupElement.querySelector('[data-action="save"]');
			const cancelPopupButton = popupElement.querySelector('[data-action="cancel"]');
			const statusLabel = popupElement.querySelector('[data-role="status"]');
			const namaInput = popupElement.querySelector('[data-field="nama"]');
			const noInput = popupElement.querySelector('[data-field="no"]');
			const statusInput = popupElement.querySelector('[data-field="status_24jam"]');
			const latitudeInput = popupElement.querySelector('[data-field="latitude"]');
			const longitudeInput = popupElement.querySelector('[data-field="longitude"]');

			if (!saveButton || !cancelPopupButton || !statusLabel || !namaInput || !noInput || !statusInput || !latitudeInput || !longitudeInput) {
				return;
			}

			cancelPopupButton.addEventListener('click', () => {
				map.closePopup();
				if (activePopupMarker) {
					map.removeLayer(activePopupMarker);
					activePopupMarker = null;
				}
			});

			saveButton.addEventListener('click', async () => {
				const nama = namaInput.value.trim();
				const no = noInput.value.trim();
				const status24jam = statusInput.value;
				const latitude = latitudeInput.value;
				const longitude = longitudeInput.value;

				if (!nama || !no) {
					setStatus(statusLabel, 'Nama dan No wajib diisi.', 'error');
					return;
				}

				saveButton.disabled = true;
				setStatus(statusLabel, 'Menyimpan data...', '');

				try {
					await savePoint({
						nama: nama,
						no: no,
						status_24jam: status24jam,
						latitude: latitude,
						longitude: longitude
					});

					setStatus(statusLabel, 'Data berhasil disimpan.', 'success');

					if (activePopupMarker) {
						map.removeLayer(activePopupMarker);
						activePopupMarker = null;
					}

					await loadPoints();
					map.closePopup();
				} catch (error) {
					setStatus(statusLabel, error.message || 'Terjadi kesalahan saat menyimpan data.', 'error');
					saveButton.disabled = false;
				}
			});
		});

		activePopupMarker.openPopup();
	});

	spatialLayer.clearLayers();

	try {
		await loadPoints();
	} catch (error) {
		console.error(error);
		alert(error.message || 'Gagal memuat data titik.');
	}

	try {
		await loadRoads();
	} catch (error) {
		console.warn('Data jalan belum termuat:', error);
	}

	try {
		await loadParcels();
	} catch (error) {
		console.warn('Data parsil belum termuat:', error);
	}

	fitAllData();

	const syncMapSize = () => {
		map.invalidateSize();
	};

	window.addEventListener('resize', syncMapSize);
	window.addEventListener('load', () => {
		setTimeout(syncMapSize, 0);
	});
}

document.addEventListener('DOMContentLoaded', () => {
	initializeMap().catch((error) => {
		console.error(error);
		alert('Terjadi kesalahan saat inisialisasi peta.');
	});
});
