<?php

namespace App\Modules\Geo\Services;

/**
 * Geo Fence Service
 * 
 * Handles geofencing logic using Haversine formula for distance calculation.
 */
class GeoFenceService
{
    /**
     * Earth's radius in meters
     */
    private const EARTH_RADIUS_METERS = 6371000;

    /**
     * Check if a point (latitude, longitude) is inside a zone
     * 
     * Uses Haversine formula to calculate distance between two points
     * on Earth's surface and compares it to zone radius.
     * 
     * @param float $lat Latitude of the point to check
     * @param float $lng Longitude of the point to check
     * @param array $zone Zone data with 'latitude', 'longitude', and 'radius_meters'
     * @return bool True if point is inside zone, false otherwise
     */
    public function isInsideZone(float $lat, float $lng, array $zone): bool
    {
        // Validate zone data
        if (!isset($zone['latitude']) || !isset($zone['longitude']) || !isset($zone['radius_meters'])) {
            return false;
        }

        $zoneLat = (float) $zone['latitude'];
        $zoneLng = (float) $zone['longitude'];
        $radiusMeters = (int) $zone['radius_meters'];

        // Calculate distance using Haversine formula
        $distance = $this->calculateHaversineDistance($lat, $lng, $zoneLat, $zoneLng);

        // Check if distance is within radius
        return $distance <= $radiusMeters;
    }

    /**
     * Calculate distance between two points using Haversine formula
     * 
     * Haversine formula calculates the great-circle distance between
     * two points on a sphere given their longitudes and latitudes.
     * 
     * @param float $lat1 Latitude of first point
     * @param float $lng1 Longitude of first point
     * @param float $lat2 Latitude of second point
     * @param float $lng2 Longitude of second point
     * @return float Distance in meters
     */
    private function calculateHaversineDistance(float $lat1, float $lng1, float $lat2, float $lng2): float
    {
        // Convert degrees to radians
        $lat1Rad = deg2rad($lat1);
        $lng1Rad = deg2rad($lng1);
        $lat2Rad = deg2rad($lat2);
        $lng2Rad = deg2rad($lng2);

        // Calculate differences
        $deltaLat = $lat2Rad - $lat1Rad;
        $deltaLng = $lng2Rad - $lng1Rad;

        // Haversine formula
        $a = sin($deltaLat / 2) * sin($deltaLat / 2) +
             cos($lat1Rad) * cos($lat2Rad) *
             sin($deltaLng / 2) * sin($deltaLng / 2);

        $c = 2 * atan2(sqrt($a), sqrt(1 - $a));

        // Distance in meters
        $distance = self::EARTH_RADIUS_METERS * $c;

        return $distance;
    }

    /**
     * Calculate distance between two points (public helper method)
     * 
     * @param float $lat1 Latitude of first point
     * @param float $lng1 Longitude of first point
     * @param float $lat2 Latitude of second point
     * @param float $lng2 Longitude of second point
     * @return float Distance in meters
     */
    public function calculateDistance(float $lat1, float $lng1, float $lat2, float $lng2): float
    {
        return $this->calculateHaversineDistance($lat1, $lng1, $lat2, $lng2);
    }
}
