<?php
/**
 * Free-tier geolocation helper.
 *
 * Provides:
 *   - Haversine distance between two lat/lng pairs (PHP, server-side).
 *   - A browser geolocation request snippet (templates/widgets opt in
 *     by emitting a button with `data-trx-ad-geo-request`).
 *
 * Maps rendering, radius search, and clustering are Pro per ADR-11.
 * The Pro plugin filters `trx_ad_geo_distance_provider` to swap in a
 * more accurate distance model (driving distance via Maps API).
 *
 * @package ThemeREX\AutoDealer
 * @version 1.0.0
 */

declare(strict_types=1);

namespace ThemeREX\AutoDealer\Frontend;

/**
 * Stateless distance + browser-geolocation helpers.
 */
final class GeoLocation {

	/** Mean radius of the Earth in kilometres. */
	private const EARTH_RADIUS_KM = 6371.0088;

	/**
	 * Great-circle distance between two coordinates (Haversine).
	 *
	 * @param float  $lat1 Latitude of point A in decimal degrees.
	 * @param float  $lng1 Longitude of point A.
	 * @param float  $lat2 Latitude of point B.
	 * @param float  $lng2 Longitude of point B.
	 * @param string $unit "km" (default) or "mi".
	 * @return float Distance in the requested unit.
	 */
	public static function distance( float $lat1, float $lng1, float $lat2, float $lng2, string $unit = 'km' ): float {
		$lat1Rad = deg2rad( $lat1 );
		$lat2Rad = deg2rad( $lat2 );
		$dLat    = deg2rad( $lat2 - $lat1 );
		$dLng    = deg2rad( $lng2 - $lng1 );

		$a = sin( $dLat / 2 ) ** 2
			+ cos( $lat1Rad ) * cos( $lat2Rad ) * sin( $dLng / 2 ) ** 2;
		$c = 2 * atan2( sqrt( $a ), sqrt( 1 - $a ) );

		$km = self::EARTH_RADIUS_KM * $c;

		return 'mi' === $unit ? $km * 0.621371 : $km;
	}

	/**
	 * Distance from the visitor (sessionStorage) to a listing's dealer, if known.
	 *
	 * Server-side companion to `requestUserLocation()`: the user's coords
	 * arrive as cookies (set by the JS helper) and we compute distance to
	 * the dealer pinned on the listing. Returns null when either side
	 * lacks coordinates.
	 *
	 * @param int    $post_id Listing ID.
	 * @param string $unit    "km" or "mi".
	 * @return float|null
	 */
	public static function distanceToVisitor( int $post_id, string $unit = 'km' ): ?float {
		$coords = self::getVisitorCoords();
		if ( null === $coords ) {
			return null;
		}

		// Prefer listing-level lat/lng; fall back to dealer's.
		$lat = (float) get_post_meta( $post_id, '_trx_ad_lat', true );
		$lng = (float) get_post_meta( $post_id, '_trx_ad_lng', true );

		if ( 0.0 === $lat && 0.0 === $lng ) {
			$dealer_id = (int) get_post_meta( $post_id, '_trx_ad_dealer_id', true );
			if ( $dealer_id <= 0 ) {
				return null;
			}
			$lat = (float) get_post_meta( $dealer_id, '_trx_ad_lat', true );
			$lng = (float) get_post_meta( $dealer_id, '_trx_ad_lng', true );
		}

		if ( 0.0 === $lat && 0.0 === $lng ) {
			return null;
		}

		$distance = self::distance( $coords['lat'], $coords['lng'], $lat, $lng, $unit );

		/**
		 * Filter the resolved distance. Pro replaces with a driving-distance API.
		 *
		 * @param float  $distance Distance in $unit.
		 * @param int    $post_id  Listing ID.
		 * @param string $unit     "km" or "mi".
		 * @param array  $coords   Visitor coords ['lat','lng'].
		 */
		return (float) apply_filters( 'trx_ad_geo_distance_provider', $distance, $post_id, $unit, $coords );
	}

	/**
	 * Pull the visitor's coordinates from the cookie set by the JS helper.
	 *
	 * @return array{lat:float,lng:float}|null
	 */
	public static function getVisitorCoords(): ?array {
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$raw = isset( $_COOKIE['trx_ad_geo'] ) ? sanitize_text_field( wp_unslash( (string) $_COOKIE['trx_ad_geo'] ) ) : '';
		if ( '' === $raw ) {
			return null;
		}
		$parts = array_pad( explode( ',', $raw, 2 ), 2, '' );
		// Reject non-numeric input — `(float) "not-a-number"` is 0.0, which
		// falsely passes the range check below as Null Island.
		if ( ! is_numeric( $parts[0] ) || ! is_numeric( $parts[1] ) ) {
			return null;
		}
		$lat = (float) $parts[0];
		$lng = (float) $parts[1];
		if ( $lat < -90 || $lat > 90 || $lng < -180 || $lng > 180 ) {
			return null;
		}
		return array(
			'lat' => $lat,
			'lng' => $lng,
		);
	}

	/**
	 * Render an opt-in button that asks the browser for the visitor's location.
	 *
	 * Emits a button with `data-trx-ad-geo-request`; frontend.js listens for
	 * it, calls `navigator.geolocation.getCurrentPosition`, and stores the
	 * result in a `trx_ad_geo` cookie (15-minute lifetime, lax samesite).
	 *
	 * @param string $label Button label.
	 * @return string
	 */
	public static function renderRequestButton( string $label = '' ): string {
		$label = '' !== $label ? $label : __( 'Use my location', 'trx-auto-dealer' );
		return sprintf(
			'<button type="button" class="trx-ad-btn trx-ad-btn--ghost" data-trx-ad-geo-request>%s</button>',
			esc_html( $label )
		);
	}
}
