<?php

namespace App\Services;

use App\Models\Notificacion;
use App\Models\PushSubscription;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Minishlink\WebPush\WebPush;

use Minishlink\WebPush\Subscription;

class NotificacionService
{
    /**
     * Enviar una notificación in-app a un usuario (jugador o aficionado).
     */
    public function enviar(
        string $tipo_dest,
        string|int $id_dest,
        string $tipo,
        string $titulo,
        string $mensaje,
        ?string $url = null
    ): Notificacion {
        $notif = Notificacion::create([
            'tipo_destinatario' => $tipo_dest,
            'id_destinatario'   => (string) $id_dest,
            'tipo'              => $tipo,
            'titulo'            => $titulo,
            'mensaje'           => $mensaje,
            'url_accion'        => $url,
            'leida'             => false,
            'enviada_email'     => false,
            'creada_en'         => now(),
        ]);

        // Intentar envío Push
        $this->enviarPush([['tipo' => $tipo_dest, 'id' => $id_dest]], $titulo, $mensaje, $url);

        return $notif;
    }

    /**
     * Enviar la misma notificación a múltiples destinatarios.
     * $destinatarios = [['tipo' => 'jugador', 'id' => 'XXX'], ...]
     */
    public function enviarMasivo(array $destinatarios, string $tipo, string $titulo, string $mensaje, ?string $url = null): int
    {
        $rows = [];
        $ahora = now()->toDateTimeString();

        foreach ($destinatarios as $dest) {
            $rows[] = [
                'tipo_destinatario' => $dest['tipo'],
                'id_destinatario'   => (string) $dest['id'],
                'tipo'              => $tipo,
                'titulo'            => $titulo,
                'mensaje'           => $mensaje,
                'url_accion'        => $url,
                'leida'             => false,
                'enviada_email'     => false,
                'creada_en'         => $ahora,
            ];
        }

        if (empty($rows)) return 0;

        // Insert en chunks para no sobrecargar
        foreach (array_chunk($rows, 500) as $chunk) {
            Notificacion::insert($chunk);
        }

        // Intentar envío Push masivo
        $this->enviarPush($destinatarios, $titulo, $mensaje, $url);

        return count($rows);
    }

    /**
     * Lógica centralizada para enviar Web Push Notifications
     */
    private function enviarPush(array $destinatarios, string $titulo, string $mensaje, ?string $url = null): void
    {
        $vPublic  = env('VAPID_PUBLIC_KEY');
        $vPrivate = env('VAPID_PRIVATE_KEY');

        if (!$vPublic || !$vPrivate) return;

        // Buscar todas las suscripciones de estos usuarios
        $subsFound = PushSubscription::where(function($query) use ($destinatarios) {
            foreach ($destinatarios as $d) {
                $query->orWhere(function($q) use ($d) {
                    $q->where('tipo_usuario', $d['tipo'])
                      ->where('id_usuario', (string)$d['id']);
                });
            }
        })->get();

        if ($subsFound->isEmpty()) return;

        try {
            $auth = [
                'VAPID' => [
                    'subject'    => env('APP_URL', 'http://localhost'),
                    'publicKey'  => $vPublic,
                    'privateKey' => $vPrivate,
                ],
            ];

            $webPush = new WebPush($auth);
            $payload = json_encode([
                'title'   => $titulo,
                'body'    => $mensaje,
                'url'     => $url ?: '/',
            ]);

            foreach ($subsFound as $sub) {
                $webPush->queueNotification(
                    Subscription::create([
                        'endpoint' => $sub->endpoint,
                        'publicKey' => $sub->p256dh,
                        'authToken' => $sub->auth,
                    ]),
                    $payload
                );
            }

            foreach ($webPush->flush() as $report) {
                if (!$report->isSuccess()) {
                    // Si falló (ej: suscripción expirada), la borramos para no reintentar
                    if ($report->isSubscriptionExpired()) {
                        $endpoint = $report->getEndpoint();
                        PushSubscription::where('endpoint', $endpoint)->delete();
                    }
                }
            }
        } catch (\Exception $e) {
            Log::error("Error enviando Web Push: " . $e->getMessage());
        }
    }

    /**
     * Obtener notificaciones no leídas de un usuario.
     */
    public function obtenerNoLeidas(string $tipo, string|int $id): Collection
    {
        return Notificacion::paraUsuario($tipo, $id)
            ->noLeidas()
            ->orderByDesc('creada_en')
            ->limit(50)
            ->get();
    }

    /**
     * Obtener todas las notificaciones de un usuario (paginadas).
     */
    public function obtenerTodas(string $tipo, string|int $id, int $perPage = 20)
    {
        return Notificacion::paraUsuario($tipo, $id)
            ->orderByDesc('creada_en')
            ->paginate($perPage);
    }

    /**
     * Contar notificaciones no leídas.
     */
    public function contarNoLeidas(string $tipo, string|int $id): int
    {
        return Notificacion::paraUsuario($tipo, $id)->noLeidas()->count();
    }

    /**
     * Marcar una notificación como leída (verificando pertenencia).
     */
    public function marcarLeida(int $id_notif, string $tipo, string|int $id_dest): bool
    {
        return (bool) Notificacion::paraUsuario($tipo, $id_dest)
            ->where('id', $id_notif)
            ->update(['leida' => true]);
    }

    /**
     * Marcar todas como leídas para un usuario.
     */
    public function marcarTodasLeidas(string $tipo, string|int $id_dest): int
    {
        return Notificacion::paraUsuario($tipo, $id_dest)
            ->noLeidas()
            ->update(['leida' => true]);
    }

    /**
     * Eliminar una notificación (verificando pertenencia).
     */
    public function eliminar(int $id_notif, string $tipo, string|int $id_dest): bool
    {
        return (bool) Notificacion::paraUsuario($tipo, $id_dest)
            ->where('id', $id_notif)
            ->delete();
    }

    /**
     * Eliminar todas las notificaciones de un usuario.
     */
    public function eliminarTodas(string $tipo, string|int $id_dest): int
    {
        return Notificacion::paraUsuario($tipo, $id_dest)->delete();
    }
}

