<?php

namespace App\Listeners;

use App\Events\PosicaoAtualizada;
use App\Events\ParagemClienteProxima;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Facades\Cache;
use App\Models\User;

class VerificaProximidade implements ShouldQueue
{
    use InteractsWithQueue;

    /**
     * Verifica proximidade entre motorista e clientes, e dispara evento se estiver próximo
     */
    public function handle(PosicaoAtualizada $event): void
    {
        $posMotorista = [$event->latitude, $event->longitude];

        // 🔍 Filtra clientes dentro de ~1km² para escuta eficiente
        $clientes = User::where('typeUser', 'cliente')
            ->whereBetween('latitude', [$event->latitude - 0.01, $event->latitude + 0.01])
            ->whereBetween('longitude', [$event->longitude - 0.01, $event->longitude + 0.01])
            ->get();

        foreach ($clientes as $cliente) {
            $posCliente = [$cliente->latitude, $cliente->longitude];

            if ($this->estaProximo($posMotorista, $posCliente)) {
                $cacheKey = "proximo_{$cliente->id}";

                // ⏱️ Evita duplicidade silenciosa via cache temporário
                if (!Cache::has($cacheKey)) {
                    broadcast(new ParagemClienteProxima(
                        clienteId: $cliente->id,
                        paragem: $this->nomeParagem($cliente)
                    ));

                    Cache::put($cacheKey, true, now()->addMinutes(2));
                }
            }
        }
    }

    /**
     * Verifica se a distância entre dois pontos é menor que 200 metros
     */
    private function estaProximo(array $a, array $b): bool
    {
        return $this->haversine($a[0], $a[1], $b[0], $b[1]) < 0.2;
    }

    /**
     * Calcula distância entre dois pontos usando fórmula de Haversine
     */
    private function haversine(float $lat1, float $lon1, float $lat2, float $lon2): float
    {
        $earthRadius = 6371;

        $dLat = deg2rad($lat2 - $lat1);
        $dLon = deg2rad($lon2 - $lon1);

        $a = sin($dLat / 2) ** 2 +
             cos(deg2rad($lat1)) * cos(deg2rad($lat2)) *
             sin($dLon / 2) ** 2;

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

        return $earthRadius * $c;
    }

    /**
     * Retorna nome da paragem associada ao cliente
     */
    private function nomeParagem(User $cliente): string
    {
        return $cliente->paragem_nome ?? 'paragem desconhecida';
    }
}
