import PerfectScrollbar from 'perfect-scrollbar'
import { params, mapTheme } from './storelocator/params.js'
import { MarkerClusterer, GridAlgorithm } from '@googlemaps/markerclusterer'

// IMPORTANT
// - This module is a modern replacement for the old src/js/storelocator.js
// - It does NOT use gmap3 nor load-google-maps-api
// - It assumes the Google Maps JS API is enqueued separately (in PHP) with libraries=places and v=3.53.x

$ = jQuery

let _mapActive = false
let _wsModalOpened = false

// Lightweight Google Maps JS API loader
function loadGoogleMapsAPI(key, { language = 'fr', libraries = ['places'], version = '3.53.2' } = {}) {
	return new Promise((resolve, reject) => {
		if (window.google && window.google.maps && window.google.maps.places) {
			resolve(window.google.maps)
			return
		}
		const existing = document.querySelector('script[data-gmaps-loader="true"]')
		if (existing) {
			existing.addEventListener('load', () => resolve(window.google.maps))
			existing.addEventListener('error', () => reject(new Error('Google Maps failed to load')))
			return
		}
		const script = document.createElement('script')
		const libs = encodeURIComponent(libraries.join(','))
		const lang = encodeURIComponent(language)
		script.src = `https://maps.googleapis.com/maps/api/js?key=${encodeURIComponent(
			key
		)}&language=${lang}&libraries=${libs}&v=${encodeURIComponent(version)}`
		script.async = true
		script.defer = true
		script.dataset.gmapsLoader = 'true'
		script.addEventListener('load', () => resolve(window.google.maps))
		script.addEventListener('error', () => reject(new Error('Google Maps failed to load')))
		document.head.appendChild(script)
	})
}

class ModernStoreLocator {
	constructor(rootEl) {
		this.el = rootEl
		this.minZoomLevel = 5
		this._map = null
		this.markersShop = []
		this.currentShopId = null
		this.currentPlace = null
		this.lastValidCenter = null

		// UI setup that doesn't need Maps
		this._initBlocWebstores()
		window.addEventListener('resize', this.resize.bind(this))

		// Defer stores loading a bit to match legacy behavior
		setTimeout(() => {
			$.get('/wp-content/uploads/stores/stores.json', (data) => {
				window.laup_map_stores = data
				this._initMap()
			})
		}, 400)
	}

	_initMap() {
		// Autocomplete, Geocoder
		this.mainSearchInput = document.getElementById('main-search-input')
		this.mainSearchBox = new google.maps.places.Autocomplete(this.mainSearchInput)
		this.resultSearchInput = document.getElementById('result-search-input')
		this.resultSearchBox = new google.maps.places.Autocomplete(this.resultSearchInput)
		this.submitSearchButton = document.getElementById('submitSearchButton')
		this.geocoder = new google.maps.Geocoder()
		document.getElementById('submitSearchButton').addEventListener('click', (event) => {
			event.preventDefault()
			this.geocodeAddress(this.mainSearchInput.value)
		})

		// Map init (use styles from params.js, and optional mapId if provided)
		const mapOptions = {
			center: { lat: 46.523134, lng: 2.4652 }, // France
			zoom: 6,
			minZoom: this.minZoomLevel,
			mapTypeId: 'roadmap',
			mapTypeControl: false,
			disableDefaultUI: false,
			scaleControl: true,
			scrollwheel: true,
			streetViewControl: false,
			fullscreenControl: false,
			gestureHandling: 'cooperative',
			styles: mapTheme,
		}
		if (params.mapId) mapOptions.mapId = params.mapId
		this._map = new google.maps.Map(document.getElementById('map'), mapOptions)

		// Events and dependencies
		this.mainSearchBox.bindTo('bounds', this._map)
		this.mainSearchBox.addListener('place_changed', () => {
			const place = this.mainSearchBox.getPlace()
			this.resultSearchInput.value = this.mainSearchInput.value
			this._gotoPlace(place)
		})
		this.resultSearchBox.bindTo('bounds', this._map)
		this.resultSearchBox.addListener('place_changed', () => {
			const place = this.resultSearchBox.getPlace()
			this._gotoPlace(place)
		})

		this._map.addListener('drag', () => this.checkLatitude(this._map))
		this._map.addListener('dragend', () => this.checkLatitude(this._map))
		this._map.addListener('zoom_changed', () => this.checkLatitude(this._map))
		this._map.addListener('bounds_changed', () => this._onBoundsChanged())

		// Results scroller
		this.resultStore = this.el.querySelector('.result-search-bloc')
		this.resultStoreContent = this.resultStore.querySelector('.content')
		this.psContentResultStore = new PerfectScrollbar(this.resultStoreContent, {
			handlers: ['click-rail', 'drag-thumb', 'keyboard', 'wheel', 'touch'],
			suppressScrollX: true,
		})

		// Close map button
		this.el.querySelector('.close-map-button')?.addEventListener('click', (event) => {
			event.preventDefault()
			this.closeMap()
		})

		// Markers
		this._populateMarkersShop()
		this._renderMarkersShop()
	}

	geocodeAddress(address) {
		if (!address) return
		this.geocoder.geocode({ address }, (results, status) => {
			if (status === 'OK' && results && results[0]) {
				this.resultSearchInput.value = this.mainSearchInput.value
				this._gotoPlace(results[0])
			} else {
				console.log('Geocode was not successful:', status)
			}
		})
	}

	_gotoPlace(place) {
		if (place && place.geometry) {
			_mapActive = true
			this.currentPlace = place
			this.el.querySelector('.container')?.classList.add('hidden')
			this.el.querySelector('.map-container')?.classList.add('show')

			let radius = 100000
			if (place.types?.includes('country')) radius = 500000
			if (place.types?.includes('locality') || place.types?.includes('postal_code')) radius = 5000
			if (
				place.types?.includes('street_address') ||
				place.types?.includes('route') ||
				place.types?.includes('neighborhood') ||
				place.types?.includes('point_of_interest')
			) {
				radius = 1000
			}

			// If viewport contains markers, fit to viewport, else use a circle bounds
			let nbShop = 0
			if (place.geometry.viewport) {
				const vp = place.geometry.viewport
				this.markersShop.forEach((m) => {
					const pos = m.getPosition()
					if (vp.contains(pos)) nbShop++
				})
			}

			if (place.geometry.viewport && nbShop > 0) {
				this._map.fitBounds(place.geometry.viewport)
			} else {
				this._map.setCenter(place.geometry.location)
				const searchCircle = new google.maps.Circle({
					strokeColor: '#CAA165',
					strokeOpacity: 0.0,
					strokeWeight: 0,
					fillOpacity: 0.0,
					map: this._map,
					center: place.geometry.location,
					radius,
				})
				this._map.fitBounds(searchCircle.getBounds())
				searchCircle.setMap(null)
			}
		}
	}

	_populateMarkersShop() {
		if (!window.laup_map_stores?.data) return
		window.laup_map_stores.data.forEach((shop) => this._processShop(shop))
	}

	_processShop(shop) {
		this._addMarkerShop(shop)
	}

	_addMarkerShop(shop) {
		if (!shop || shop.lat === '' || shop.lng === '') return

		let url = '/wp-content/themes/laurentperrier/dist/assets/img/pin.png?v2'
		if (window.devicePixelRatio > 1.5) {
			url = '/wp-content/themes/laurentperrier/dist/assets/img/pin@2x.png?v2'
		}

		const icon = {
			url,
			anchor: new google.maps.Point(16, 34),
			size: new google.maps.Size(32, 42),
			scaledSize: new google.maps.Size(32, 42),
		}

		const marker = new google.maps.Marker({
			position: new google.maps.LatLng({ lat: parseFloat(shop.lat), lng: parseFloat(shop.lng) }),
			icon,
		})
		marker.data = shop

		marker.addListener('click', (e) => {
			// console.log('marker click :: ', marker, marker.data.i)
			// Highlight corresponding list item and focus map
			this.markersShop.forEach((m) => m.setOpacity(0.6))
			marker.setOpacity(1)
			this.currentShopId = marker.data.i
			document
				.querySelectorAll('.result-search-bloc .content ul li')
				.forEach((li) => li.classList.remove('highlight'))
			document.getElementById('results-map-list')?.classList.remove('list_highlight')

			const target = document.getElementById(this.currentShopId)

			if (target) {
				target.classList.add('highlight')
				document.getElementById('results-map-list')?.classList.add('list_highlight')
				// this.resultStoreContent.scrollTop = target.offsetTop
			}
			this._map.setCenter(marker.getPosition())
			this._map.setZoom(15)
		})

		this.markersShop.push(marker)
	}

	_renderMarkersShop() {
		// Build clusters with official MarkerClusterer (npm)
		if (this.cluster) this.cluster.clearMarkers()
		const clusterOptions = {
			map: this._map,
			markers: this.markersShop,
			algorithm: new GridAlgorithm({ minimumClusterSize: 5 }),
		}
		// Only use AdvancedMarker renderer when a valid mapId is configured
		if (params.mapId) {
			clusterOptions.renderer = {
				render: ({ count, position }) => {
					const el = document.createElement('div')
					el.className = 'cluster'
					el.textContent = String(count)
					return new google.maps.marker.AdvancedMarkerElement({ position, content: el })
				},
			}
		} else {
			// No mapId: use classic Marker with SVG icon so we can style clusters
			clusterOptions.renderer = {
				render: ({ count, position }) => {
					// thresholds for sizes/colors (tweak to your brand)
					let r = 18
					let scale = 1
					if (count >= 25) {
						r = 26
						scale = 1.5
					} else if (count >= 10) {
						r = 22
						scale = 1.2
					}
					const svgMarker = {
						path: 'M14,0h0A14,14,0,0,0,0,14C0,25.2,14,36,14,36S28,25.2,28,14A14,14,0,0,0,14,0',
						fillColor: '#ae7d3c',
						fillOpacity: 0,
						strokeWeight: 0,
						rotation: 0,
						scale: scale,
						anchor: new google.maps.Point(-r, -r),
					}

					// const size = new google.maps.Size(r * 2, r * 2)
					// const anchor = new google.maps.Point(r, r)
					return new google.maps.Marker({
						position,
						icon: svgMarker,
						label: {
							text: count.toString(),
							color: '#fff',
							textAnchor: 'middle',
							className: 'cluster',
						},
						zIndex: Number.MAX_SAFE_INTEGER - r,
					})
				},
			}
		}
		this.cluster = new MarkerClusterer(clusterOptions)

		// Initial render of the side list based on current bounds
		this._updateResultsByBounds()
	}

	_updateResultsByBounds() {
		const map = this._map
		const bounds = map.getBounds()
		if (!bounds) return

		const results = []
		let count = 0
		this.markersShop.forEach((marker) => {
			if (bounds.contains(marker.getPosition())) {
				if (count > 100) return
				count++
				let dist = -1
				if (this.currentPlace?.geometry?.location) {
					const p = marker.getPosition()
					dist = _getDistanceFromLatLonInKm(
						p.lat(),
						p.lng(),
						this.currentPlace.geometry.location.lat(),
						this.currentPlace.geometry.location.lng()
					)
				}
				results.push({ marker, dist })
			}
		})

		results.sort((a, b) => a.dist - b.dist)
		const resultContainer = document.querySelector('.result-search-bloc .content ul.gmaplist')
		if (!resultContainer) return
		let html = ''
		results.forEach(({ marker, dist }) => {
			const d = marker.data
			if (d.f == 'fr') {
				html += `
          <li id="${d.i}" data-dist="${dist}">
            <address itemscope itemtype="http://schema.org/PostalAddress">
              ${d.n ? `<h3 itemprop="name">${d.n}</h3>` : ''}
              ${d.a ? `<div itemprop="streetAddress">${d.a}<br />${d.a2 ? d.a2 + '<br />' : ''}</div>` : ''}
              <div>
                ${d.z ? `<span itemprop="postalCode">${d.z}</span>` : ''}
                ${d.c ? `<span itemprop="addressLocality">${d.c},</span>` : ''}
                ${d.co ? `<span itemprop="addressCountry">${d.co}</span>` : ''}
              </div>
              ${d.p ? `<div itemprop="telephone" content="${d.p}">${d.p}</div>` : ''}
            </address>
            <span class="arrow_right"></span>
          </li>`
			} else {
				html += `
          <li id="${d.i}" data-dist="${dist}" class="format_us">
            <address itemscope itemtype="http://schema.org/PostalAddress">
              <h3 itemprop="name">${d.n || ''}</h3>
              <div itemprop="streetAddress">${d.a || ''}<br />${d.a2 ? d.a2 + '<br />' : ''}</div>
              <div>
                <span itemprop="addressCountry">${d.cn || ''}</span>
              </div>
              <div itemprop="telephone" content="${d.p || ''}">${d.p || ''}</div>
            </address>
            <span class="arrow_right"></span>
          </li>`
			}
		})
		resultContainer.innerHTML = html

		if (this.currentShopId && document.getElementById(this.currentShopId)) {
			document.getElementById(this.currentShopId).classList.add('highlight')
			document.getElementById('results-map-list')?.classList.add('list_highlight')
		}

		resultContainer.querySelectorAll('li').forEach((item) => {
			item.addEventListener('click', (event) => {
				event.preventDefault()
				this.currentShopId = item.id
				document
					.querySelectorAll('.result-search-bloc .content ul li')
					.forEach((li) => li.classList.remove('highlight'))
				document.getElementById('results-map-list')?.classList.remove('list_highlight')

				this.markersShop.forEach((m) => {
					if (m.data.id === item.id || m.data.i === item.id) {
						m.setOpacity(1)
						item.classList.add('highlight')
						document.getElementById('results-map-list')?.classList.add('list_highlight')
						this._map.setCenter(m.getPosition())
						this._map.setZoom(15)
					} else {
						m.setOpacity(0.6)
					}
				})
			})
		})

		if (this.psContentResultStore) {
			this.psContentResultStore.update()
			if (this.currentShopId) {
				// console.log('scrolling to :: ', this.currentShopId, document.getElementById(this.currentShopId))
				this.resultStoreContent.scrollTop = document.getElementById(this.currentShopId).offsetTop
			}
		}
	}

	_onBoundsChanged() {
		// Update US webstore visibility according to center country
		const center = this._map.getCenter()
		const latlng = new google.maps.LatLng(center.lat(), center.lng())
		this.geocoder.geocode({ location: latlng }, (results, status) => {
			if (status === 'OK' && results && results.length) {
				let countryCode = null
				for (let r = 0; r < results.length; r++) {
					const res = results[r]
					if (!countryCode && res.types && res.types[0] === 'country') {
						countryCode = res.address_components?.[0]?.short_name
						break
					}
				}
				if (countryCode !== 'US') {
					document.querySelectorAll('#results-map-list .format_us').forEach(() => {
						countryCode = 'US'
					})
				}
				if (countryCode === 'US') {
					const usList = document.getElementById('webStoreUsList')
					if (usList) usList.style.display = 'block'
				} else {
					const usList = document.getElementById('webStoreUsList')
					if (usList) usList.style.display = 'none'
				}
			}
		})

		// Refresh results list
		this._updateResultsByBounds()
	}

	closeMap() {
		this.el.querySelector('.container')?.classList.remove('hidden')
		this.el.querySelector('.map-container')?.classList.remove('show')
		_mapActive = false
		this.currentShopId = null
		document
			.querySelectorAll('.result-search-bloc .content ul li')
			.forEach((item) => item.classList.remove('highlight'))
		document.getElementById('results-map-list')?.classList.remove('list_highlight')
		this.markersShop.forEach((m) => m.setOpacity(1))
	}

	_initBlocWebstores() {
		const blocWS = this.el.querySelector('.webstore-bloc')
		if (!blocWS) return

		const content = blocWS.querySelector('.content')
		this.psBlocWS = new PerfectScrollbar(content, {
			handlers: ['click-rail', 'drag-thumb', 'keyboard', 'wheel', 'touch'],
			suppressScrollX: true,
		})
		this.WSmodal = this.el.querySelector('.webstores-modal')

		$('#country-select-ws').selectric({
			maxHeight: 200,
			onInit: () => {
				const modalContent = this.WSmodal.querySelector('.content')
				this.psModalWS = new PerfectScrollbar(modalContent, {
					handlers: ['click-rail', 'drag-thumb', 'keyboard', 'wheel', 'touch'],
					suppressScrollX: true,
				})
			},
		})
		$('#country-select-ws').on('change', () => {
			this.selectWSCountry($('#country-select-ws option:selected').val())
		})
		document.getElementById('see-all-webstore-button')?.addEventListener('click', this.openWSModal.bind(this))
		this.WSmodal?.querySelector('.close-modal-button')?.addEventListener('click', this.closeWSModal.bind(this))
	}

	openWSModal(event) {
		if (event) event.preventDefault()
		_wsModalOpened = true
		this.WSmodal?.classList.add('js-modal-open')
		document.body.classList.add('no-scroll')

		if (window.lang) {
			$('#country-select-ws').prop('selectedIndex', 0).selectric('refresh')
			this.selectWSCountry($('#country-select-ws option:selected').val())
		}
	}

	closeWSModal(event) {
		if (event) event.preventDefault()
		document.body.classList.remove('no-scroll')
		_wsModalOpened = false
		this.WSmodal?.classList.remove('js-modal-open')
	}

	selectWSCountry(country) {
		$('.stores-list').hide()
		$(`.stores-list-${country}`).show()
		if (this.psModalWS) this.psModalWS.update()
		$('#country-select-ws').selectric('refresh')
	}

	checkLatitude(map) {
		if (this.minZoomLevel && map.getZoom() < this.minZoomLevel) {
			map.setZoom(parseInt(this.minZoomLevel))
		}
		const sLat = map.getBounds()?.getSouthWest()?.lat()
		const nLat = map.getBounds()?.getNorthEast()?.lat()
		if (sLat != null && nLat != null && (sLat < -85 || nLat > 85)) {
			if (this.lastValidCenter) map.setCenter(this.lastValidCenter)
		} else {
			this.lastValidCenter = map.getCenter()
		}
	}

	resize() {
		if (this.psModalWS) this.psModalWS.update()
	}
}

function _getDistanceFromLatLonInKm(lat1, lon1, lat2, lon2) {
	function deg2rad(deg) {
		return deg * (Math.PI / 180)
	}
	const R = 6371
	const dLat = deg2rad(lat2 - lat1)
	const dLon = deg2rad(lon2 - lon1)
	const a =
		Math.sin(dLat / 2) * Math.sin(dLat / 2) +
		Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * Math.sin(dLon / 2) * Math.sin(dLon / 2)
	const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
	return R * c
}

window.addEventListener('DOMContentLoaded', () => {
	const section = document.getElementById('section_gmap-store-locator')
	if (!section) return
	const language = window.lang || 'fr'
	loadGoogleMapsAPI(params.key, { language, libraries: ['places', 'marker'], version: '3.53.13' })
		.then(() => {
			// eslint-disable-next-line no-new
			new ModernStoreLocator(section)
		})
		.catch((err) => {
			console.error('Failed to load Google Maps API:', err)
		})
})
