<?php

declare(strict_types=1);

namespace App\Services;

use App\Repositories\ZonaRepository;
use GuzzleHttp\Client;

/**
 * Calcula el costo de envío en base a distancia real (Google Distance Matrix)
 * y las reglas de tarifa de la zona de cobertura correspondiente.
 */
class CotizacionService
{
    private Client $http;
    private ZonaRepository $zonas;
    private string $googleApiKey;

    public function __construct(ZonaRepository $zonas, ?Client $http = null)
    {
        $this->zonas = $zonas;
        $this->http = $http ?? new Client(['timeout' => 5.0]);
        $this->googleApiKey = $_ENV['GOOGLE_MAPS_API_KEY'] ?? '';
    }

    /**
     * @return array{
     *   distancia_km: float,
     *   duracion_min: float,
     *   zona_id: int|null,
     *   tarifa_base: float,
     *   tarifa_por_km: float,
     *   recargo: float,
     *   total: float
     * }
     */
    public function cotizar(float $origenLat, float $origenLng, float $destinoLat, float $destinoLng): array
    {
        [$distanciaKm, $duracionMin] = $this->obtenerDistanciaYDuracion($origenLat, $origenLng, $destinoLat, $destinoLng);

        $zona = $this->zonas->buscarPorCoordenada($origenLat, $origenLng);

        $tarifaBase = $zona['tarifa_base'] ?? 3000.0;
        $tarifaPorKm = $zona['tarifa_por_km'] ?? 800.0;
        $recargo = $this->calcularRecargo($zona);

        $total = round($tarifaBase + ($distanciaKm * $tarifaPorKm) + $recargo, 2);

        return [
            'distancia_km' => round($distanciaKm, 2),
            'duracion_min' => round($duracionMin, 1),
            'zona_id' => $zona['id'] ?? null,
            'tarifa_base' => (float) $tarifaBase,
            'tarifa_por_km' => (float) $tarifaPorKm,
            'recargo' => (float) $recargo,
            'total' => $total,
        ];
    }

    /**
     * @return array{0: float, 1: float} [distancia_km, duracion_min]
     */
    private function obtenerDistanciaYDuracion(float $oLat, float $oLng, float $dLat, float $dLng): array
    {
        if (empty($this->googleApiKey)) {
            return [$this->haversineKm($oLat, $oLng, $dLat, $dLng), 0.0];
        }

        try {
            $response = $this->http->get('https://maps.googleapis.com/maps/api/distancematrix/json', [
                'query' => [
                    'origins' => "{$oLat},{$oLng}",
                    'destinations' => "{$dLat},{$dLng}",
                    'key' => $this->googleApiKey,
                    'units' => 'metric',
                ],
            ]);

            $data = json_decode((string) $response->getBody(), true);
            $element = $data['rows'][0]['elements'][0] ?? null;

            if ($element && ($element['status'] ?? '') === 'OK') {
                $distanciaKm = ($element['distance']['value'] ?? 0) / 1000;
                $duracionMin = ($element['duration']['value'] ?? 0) / 60;

                return [$distanciaKm, $duracionMin];
            }
        } catch (\Throwable $e) {
            // Cae a estimación por haversine si la API falla o no responde.
        }

        return [$this->haversineKm($oLat, $oLng, $dLat, $dLng), 0.0];
    }

    private function haversineKm(float $lat1, float $lng1, float $lat2, float $lng2): float
    {
        $earthRadiusKm = 6371.0;

        $dLat = deg2rad($lat2 - $lat1);
        $dLng = deg2rad($lng2 - $lng1);

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

        return $earthRadiusKm * $c;
    }

    /**
     * @param array<string, mixed>|null $zona
     */
    private function calcularRecargo(?array $zona): float
    {
        if ($zona === null) {
            return 0.0;
        }

        $horaActual = (int) date('G');
        $esHoraPico = ($horaActual >= 12 && $horaActual <= 14) || ($horaActual >= 18 && $horaActual <= 20);

        return $esHoraPico ? (float) ($zona['recargo_hora_pico'] ?? 0.0) : 0.0;
    }
}
