<?php

namespace App\Services\Notifications;

use App\Models\Tenant\PushSubscription;
use Illuminate\Support\Facades\Log;
use Minishlink\WebPush\Subscription as PushSub;
use Minishlink\WebPush\WebPush;

/**
 * Sends Web Push notifications via VAPID. Subscriptions that the push service
 * reports as gone (404/410) are pruned automatically.
 */
class WebPushService
{
    public function configured(): bool
    {
        return ! empty(config('webpush.vapid.public_key'))
            && ! empty(config('webpush.vapid.private_key'));
    }

    public function publicKey(): ?string
    {
        return config('webpush.vapid.public_key');
    }

    /**
     * Send to a single stored subscription. Returns true on accepted delivery.
     */
    public function send(PushSubscription $sub, string $title, string $body, ?string $url = null, ?string $tag = null): bool
    {
        if (! $this->configured()) {
            Log::debug('webpush: VAPID not configured, skip');
            return false;
        }

        $results = $this->dispatch([$sub], $title, $body, $url, $tag);
        return $results['sent'] > 0;
    }

    /**
     * Send to every subscription of a user (a person may have several devices).
     *
     * @return array{sent:int, pruned:int}
     */
    public function sendToUser(int $userId, string $title, string $body, ?string $url = null, ?string $tag = null): array
    {
        $subs = PushSubscription::where('user_id', $userId)->get();
        return $this->dispatch($subs, $title, $body, $url, $tag);
    }

    /**
     * @param  iterable<PushSubscription>  $subs
     * @return array{sent:int, pruned:int}
     */
    public function dispatch(iterable $subs, string $title, string $body, ?string $url = null, ?string $tag = null): array
    {
        if (! $this->configured()) return ['sent' => 0, 'pruned' => 0];

        $webPush = new WebPush([
            'VAPID' => [
                'subject' => config('webpush.vapid.subject'),
                'publicKey' => config('webpush.vapid.public_key'),
                'privateKey' => config('webpush.vapid.private_key'),
            ],
        ]);
        $webPush->setDefaultOptions(['TTL' => (int) config('webpush.ttl', 2419200)]);

        $payload = json_encode([
            'title' => $title,
            'body' => $body,
            'url' => $url ?? '/app',
            'tag' => $tag ?? 'autocrm',
        ]);

        // Index by endpoint so we can prune by the report's endpoint.
        $byEndpoint = [];
        foreach ($subs as $s) {
            $byEndpoint[$s->endpoint] = $s;
            $webPush->queueNotification(
                PushSub::create([
                    'endpoint' => $s->endpoint,
                    'publicKey' => $s->public_key,
                    'authToken' => $s->auth_token,
                    'contentEncoding' => $s->content_encoding ?: 'aesgcm',
                ]),
                $payload
            );
        }

        $sent = 0;
        $pruned = 0;
        foreach ($webPush->flush() as $report) {
            $endpoint = $report->getRequest()->getUri()->__toString();
            if ($report->isSuccess()) {
                $sent++;
            } elseif ($report->isSubscriptionExpired()) {
                // 404/410 — device unsubscribed; remove stored row.
                if (isset($byEndpoint[$endpoint])) {
                    $byEndpoint[$endpoint]->delete();
                    $pruned++;
                } else {
                    PushSubscription::where('endpoint', $endpoint)->delete();
                    $pruned++;
                }
            } else {
                Log::warning('webpush: delivery failed', [
                    'endpoint' => $endpoint,
                    'reason' => $report->getReason(),
                ]);
            }
        }

        return ['sent' => $sent, 'pruned' => $pruned];
    }
}
