(function () {
	'use strict';

	const DEFAULT_SOURCE = 'assets/data/Kecamatan.json';
	const DEFAULT_ANCHOR = {
		lat: -0.06054796552220232,
		lng: 109.34490231930592
	};
	const DEFAULT_GEO_BOUNDS = {
		south: -0.0981948,
		west: 109.2741676,
		north: 0.0381168,
		east: 109.3853475
	};
	const DEFAULT_CALIBRATION = {
		scaleX: 1,
		scaleY: 1,
		offsetLat: 0,
		offsetLng: 0
	};

	function walkCoordinates(coordinates, callback) {
		if (!Array.isArray(coordinates)) {
			return;
		}

		if (coordinates.length >= 2 && typeof coordinates[0] === 'number' && typeof coordinates[1] === 'number') {
			callback(coordinates[0], coordinates[1]);
			return;
		}

		coordinates.forEach((child) => walkCoordinates(child, callback));
	}

	function getCoordinateBounds(data) {
		const bounds = {
			minX: Infinity,
			minY: Infinity,
			maxX: -Infinity,
			maxY: -Infinity
		};

		function addGeometry(geometry) {
			if (!geometry) {
				return;
			}

			if (geometry.type === 'GeometryCollection') {
				(geometry.geometries || []).forEach(addGeometry);
				return;
			}

			walkCoordinates(geometry.coordinates, (x, y) => {
				bounds.minX = Math.min(bounds.minX, x);
				bounds.minY = Math.min(bounds.minY, y);
				bounds.maxX = Math.max(bounds.maxX, x);
				bounds.maxY = Math.max(bounds.maxY, y);
			});
		}

		if (data.type === 'FeatureCollection') {
			(data.features || []).forEach((feature) => addGeometry(feature.geometry));
		} else if (data.type === 'Feature') {
			addGeometry(data.geometry);
		} else {
			addGeometry(data);
		}

		if (!Number.isFinite(bounds.minX) || !Number.isFinite(bounds.minY)) {
			return null;
		}

		return bounds;
	}

	function createBoundingBoxTransform(sourceBounds, targetBounds, calibration) {
		const sourceWidth = sourceBounds.maxX - sourceBounds.minX;
		const sourceHeight = sourceBounds.maxY - sourceBounds.minY;
		const targetCenterLng = (targetBounds.west + targetBounds.east) / 2 + calibration.offsetLng;
		const targetCenterLat = (targetBounds.south + targetBounds.north) / 2 + calibration.offsetLat;
		const targetWidth = (targetBounds.east - targetBounds.west) * calibration.scaleX;
		const targetHeight = (targetBounds.north - targetBounds.south) * calibration.scaleY;

		return function coordsToLatLng(coordinates) {
			const xRatio = ((coordinates[0] - sourceBounds.minX) / sourceWidth) - 0.5;
			const yRatio = ((coordinates[1] - sourceBounds.minY) / sourceHeight) - 0.5;
			const lng = targetCenterLng + (xRatio * targetWidth);
			const lat = targetCenterLat + (yRatio * targetHeight);
			return L.latLng(lat, lng);
		};
	}

	window.addKecamatanLayer = async function addKecamatanLayer(map, options) {
		const settings = Object.assign({
			source: DEFAULT_SOURCE,
			anchor: DEFAULT_ANCHOR,
			targetBounds: DEFAULT_GEO_BOUNDS,
			calibration: DEFAULT_CALIBRATION,
			fitBounds: false
		}, options || {});

		if (!map || typeof L === 'undefined') {
			return null;
		}

		if (!map.getPane('kecamatanPane')) {
			map.createPane('kecamatanPane');
			map.getPane('kecamatanPane').style.zIndex = 350;
		}

		const response = await fetch(settings.source, {
			headers: { 'Accept': 'application/json' }
		});

		if (!response.ok) {
			throw new Error('Gagal memuat data batas kecamatan.');
		}

		const data = await response.json();
		const coordinateBounds = getCoordinateBounds(data);

		if (!coordinateBounds) {
			throw new Error('Data batas kecamatan tidak memiliki koordinat valid.');
		}

		const layer = L.geoJSON(data, {
			coordsToLatLng: createBoundingBoxTransform(coordinateBounds, settings.targetBounds, settings.calibration),
			pane: 'kecamatanPane',
			style: {
				color: '#0f766e',
				weight: 2,
				opacity: 0.9,
				fillColor: '#14b8a6',
				fillOpacity: 0.08,
				dashArray: '6 4'
			},
			onEachFeature: function (feature, featureLayer) {
				const name = feature && feature.properties && (
					feature.properties.nama ||
					feature.properties.NAMA ||
					feature.properties.KECAMATAN ||
					feature.properties.kecamatan
				);

				featureLayer.bindPopup(name ? `Kecamatan: ${name}` : 'Batas Kecamatan');
			}
		}).addTo(map);

		if (settings.fitBounds && layer.getBounds().isValid()) {
			map.fitBounds(layer.getBounds().pad(0.12), { maxZoom: 15 });
		}

		return layer;
	};
}());
