<?php
/**
 * OpenStreetMap + Leaflet.js map provider (Pro per ADR-11).
 *
 * Renders interactive maps without an API key. Leaflet is bundled
 * locally — no CDN dependency. Marker clustering via the
 * Leaflet.markercluster plugin (also bundled).
 *
 * @package ThemeREX\AutoDealer
 */

declare(strict_types=1);

namespace ThemeREX\AutoDealer\Integrations\Maps;

use ThemeREX\AutoDealer\Admin\AdminSettings;

final class LeafletProvider implements MapProviderInterface {

	public const HANDLE_CSS         = 'trx-ad-leaflet';
	public const HANDLE_JS          = 'trx-ad-leaflet';
	public const HANDLE_CLUSTER_CSS = 'trx-ad-leaflet-markercluster';
	public const HANDLE_CLUSTER_JS  = 'trx-ad-leaflet-markercluster';
	public const HANDLE_BOOT        = 'trx-ad-maps-frontend';

	private const LEAFLET_VERSION = '1.9.4';
	private const CLUSTER_VERSION = '1.5.3';

	public function enqueueAssets(): void {
		if ( ! defined( 'TRX_AD_URL' ) || ! defined( 'TRX_AD_VERSION' ) ) {
			return;
		}

		$base = TRX_AD_URL . 'assets/vendor/leaflet/';

		wp_register_style(
			self::HANDLE_CSS,
			$base . 'leaflet.css',
			array(),
			self::LEAFLET_VERSION
		);
		wp_register_script(
			self::HANDLE_JS,
			$base . 'leaflet.js',
			array(),
			self::LEAFLET_VERSION,
			true
		);

		wp_register_style(
			self::HANDLE_CLUSTER_CSS,
			$base . 'MarkerCluster.css',
			array( self::HANDLE_CSS ),
			self::CLUSTER_VERSION
		);
		wp_register_script(
			self::HANDLE_CLUSTER_JS,
			$base . 'leaflet.markercluster.js',
			array( self::HANDLE_JS ),
			self::CLUSTER_VERSION,
			true
		);

		wp_register_script(
			self::HANDLE_BOOT,
			TRX_AD_URL . 'assets/js/maps-frontend.js',
			array( self::HANDLE_JS ),
			(string) TRX_AD_VERSION,
			true
		);

		wp_enqueue_style( self::HANDLE_CSS );
		wp_enqueue_script( self::HANDLE_JS );

		if ( $this->clusterEnabled() ) {
			wp_enqueue_style( self::HANDLE_CLUSTER_CSS );
			wp_enqueue_script( self::HANDLE_CLUSTER_JS );
		}

		wp_enqueue_script( self::HANDLE_BOOT );
	}

	/**
	 * Render a map placeholder with embedded JSON config.
	 *
	 * Expected $args shape:
	 *   - id        (string)  DOM id, defaults to a random one.
	 *   - height    (string)  CSS height, default "400px".
	 *   - center    (?array)  ['lat','lng'], defaults to first pin or [0,0].
	 *   - zoom      (?int)    overrides setting default.
	 *   - pins      (array)   list of ['lat','lng','title','content','url'?].
	 *   - cluster   (?bool)   overrides setting default.
	 *
	 * @param array<string, mixed> $args
	 */
	public function render( array $args ): string {
		$this->enqueueAssets();

		$pins   = isset( $args['pins'] ) && is_array( $args['pins'] ) ? array_values( array_filter( $args['pins'], array( $this, 'isValidPin' ) ) ) : array();
		$id     = isset( $args['id'] ) ? sanitize_html_class( (string) $args['id'] ) : 'trx-ad-map-' . wp_generate_uuid4();
		$height = isset( $args['height'] ) ? (string) $args['height'] : '400px';
		$zoom   = isset( $args['zoom'] ) ? (int) $args['zoom'] : (int) AdminSettings::get( 'map_default_zoom', 12 );

		$cluster = array_key_exists( 'cluster', $args )
			? (bool) $args['cluster']
			: $this->clusterEnabled();

		$center = array( 0.0, 0.0 );
		if ( isset( $args['center']['lat'], $args['center']['lng'] ) ) {
			$center = array( (float) $args['center']['lat'], (float) $args['center']['lng'] );
		} elseif ( ! empty( $pins ) ) {
			$center = array( (float) $pins[0]['lat'], (float) $pins[0]['lng'] );
		}

		$config = array(
			'provider' => 'leaflet',
			'center'   => array(
				'lat' => $center[0],
				'lng' => $center[1],
			),
			'zoom'     => max( 1, min( 20, $zoom ) ),
			'cluster'  => $cluster,
			'pins'     => array_map( array( $this, 'normalizePin' ), $pins ),
		);

		return sprintf(
			'<div class="trx-ad-map" id="%1$s" data-trx-ad-map="%2$s" style="height:%3$s;width:100%%;"></div>',
			esc_attr( $id ),
			esc_attr( (string) wp_json_encode( $config ) ),
			esc_attr( $height )
		);
	}

	/**
	 * @param mixed $pin
	 */
	private function isValidPin( $pin ): bool {
		if ( ! is_array( $pin ) ) {
			return false;
		}
		if ( ! isset( $pin['lat'], $pin['lng'] ) ) {
			return false;
		}
		$lat = (float) $pin['lat'];
		$lng = (float) $pin['lng'];

		return $lat >= -90 && $lat <= 90 && $lng >= -180 && $lng <= 180 && ! ( 0.0 === $lat && 0.0 === $lng );
	}

	/**
	 * @param array<string, mixed> $pin
	 * @return array{lat:float,lng:float,title:string,content:string,url:string}
	 */
	private function normalizePin( array $pin ): array {
		return array(
			'lat'     => (float) $pin['lat'],
			'lng'     => (float) $pin['lng'],
			'title'   => isset( $pin['title'] ) ? wp_strip_all_tags( (string) $pin['title'] ) : '',
			'content' => isset( $pin['content'] ) ? wp_kses_post( (string) $pin['content'] ) : '',
			'url'     => isset( $pin['url'] ) ? esc_url_raw( (string) $pin['url'] ) : '',
		);
	}

	private function clusterEnabled(): bool {
		return (bool) AdminSettings::get( 'map_cluster', 1 );
	}
}
