<?php
namespace App\Services;

use RuntimeException;

final class GoogleRoutesService
{
    public function computeWalkingRoute(array $points): array
    {
        $key = $GLOBALS['config']['google']['routes_server_key'] ?? '';
        if (!$key) {
            return $this->fallbackRoute($points, 'Google Routes server key is missing. Saved direct-line route only.');
        }
        $valid = array_values(array_filter($points, fn($p) => isset($p['latitude'], $p['longitude']) && is_numeric($p['latitude']) && is_numeric($p['longitude'])));
        if (count($valid) < 2) {
            throw new RuntimeException('At least start and end coordinates are required.');
        }
        $origin = $this->waypoint($valid[0]);
        $destination = $this->waypoint($valid[count($valid)-1]);
        $intermediates = [];
        foreach (array_slice($valid, 1, -1) as $point) {
            $intermediates[] = $this->waypoint($point);
        }
        $payload = [
            'origin' => $origin,
            'destination' => $destination,
            'intermediates' => $intermediates,
            'travelMode' => 'WALK',
            'polylineQuality' => 'HIGH_QUALITY',
            'polylineEncoding' => 'ENCODED_POLYLINE',
            'computeAlternativeRoutes' => false,
        ];
        $ch = curl_init('https://routes.googleapis.com/directions/v2:computeRoutes');
        curl_setopt_array($ch, [
            CURLOPT_POST => true,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 25,
            CURLOPT_HTTPHEADER => [
                'Content-Type: application/json',
                'X-Goog-Api-Key: ' . $key,
                'X-Goog-FieldMask: routes.distanceMeters,routes.duration,routes.polyline.encodedPolyline,routes.viewport',
            ],
            CURLOPT_POSTFIELDS => json_encode($payload),
        ]);
        $body = curl_exec($ch);
        $code = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $err = curl_error($ch);
        curl_close($ch);
        if ($body === false || $code < 200 || $code >= 300) {
            return $this->fallbackRoute($points, 'Google Routes API failed: HTTP ' . $code . ' ' . ($body ?: $err));
        }
        $json = json_decode($body, true);
        $route = $json['routes'][0] ?? null;
        if (!$route) {
            return $this->fallbackRoute($points, 'Google returned no route. Saved direct-line route only.');
        }
        return [
            'ok' => true,
            'source' => 'google_routes_api',
            'distance_km' => round(((float)($route['distanceMeters'] ?? 0)) / 1000, 2),
            'duration_minutes' => $this->durationToMinutes($route['duration'] ?? '0s'),
            'encoded_polyline' => $route['polyline']['encodedPolyline'] ?? '',
            'bounds' => $route['viewport'] ?? null,
            'message' => 'Route calculated by Google Routes API.',
        ];
    }

    private function waypoint(array $point): array
    {
        return ['location' => ['latLng' => ['latitude' => (float)$point['latitude'], 'longitude' => (float)$point['longitude']]]];
    }

    private function durationToMinutes(string $duration): int
    {
        if (preg_match('/^(\d+)s$/', $duration, $m)) return max(1, (int)ceil(((int)$m[1]) / 60));
        return 60;
    }

    private function fallbackRoute(array $points, string $message): array
    {
        $valid = array_values(array_filter($points, fn($p) => isset($p['latitude'], $p['longitude']) && is_numeric($p['latitude']) && is_numeric($p['longitude'])));
        $km = 0.0;
        for ($i=1; $i<count($valid); $i++) {
            $km += $this->haversine((float)$valid[$i-1]['latitude'], (float)$valid[$i-1]['longitude'], (float)$valid[$i]['latitude'], (float)$valid[$i]['longitude']);
        }
        return [
            'ok' => true,
            'source' => 'fallback_haversine',
            'distance_km' => round($km, 2),
            'duration_minutes' => max(15, (int)ceil(($km / 4.5) * 60)),
            'encoded_polyline' => '',
            'bounds' => null,
            'message' => $message,
        ];
    }

    private function haversine(float $lat1, float $lon1, float $lat2, float $lon2): float
    {
        $earth = 6371;
        $dLat = deg2rad($lat2 - $lat1);
        $dLon = deg2rad($lon2 - $lon1);
        $a = sin($dLat/2) ** 2 + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * sin($dLon/2) ** 2;
        return $earth * 2 * atan2(sqrt($a), sqrt(1-$a));
    }
}
