<?php

namespace App\Jobs;

use App\Models\Notification;
use App\Models\PushSubscription;
use App\Models\SystemConfiguration;
use App\Models\User;
use App\Support\Media\MediaPresenter;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Foundation\Queue\Queueable;
use Minishlink\WebPush\Subscription;
use Minishlink\WebPush\WebPush;

class SendPushNotificationJob implements ShouldQueue
{
    use Queueable;

    public function __construct(
        private readonly string $title,
        private readonly string $body,
        private readonly string $url = '/admin/dashboard',
        private readonly array $roles = [],
        private readonly ?int $userId = null,
    ) {}

    public function handle(): void
    {
        $this->persistNotifications();
        $this->sendWebPush();
    }

    private function persistNotifications(): void
    {
        $userIds = $this->resolveUserIds();

        foreach ($userIds as $uid) {
            Notification::create([
                'user_id' => $uid,
                'title' => $this->title,
                'body' => $this->body,
                'url' => $this->url,
            ]);
        }
    }

    private function sendWebPush(): void
    {
        $subscriptions = $this->resolveSubscriptions();

        if ($subscriptions->isEmpty()) {
            return;
        }

        $icon = $this->resolveIcon();

        $webPush = new WebPush([
            'VAPID' => [
                'subject' => config('app.url'),
                'publicKey' => config('webpush.vapid.public_key'),
                'privateKey' => config('webpush.vapid.private_key'),
            ],
        ]);

        $payload = json_encode([
            'title' => $this->title,
            'body' => $this->body,
            'icon' => $icon,
            'url' => $this->url,
        ]);

        foreach ($subscriptions as $subscription) {
            $sub = Subscription::create([
                'endpoint' => $subscription->endpoint,
                'publicKey' => $subscription->public_key,
                'authToken' => $subscription->auth_token,
                'contentEncoding' => 'aes128gcm',
            ]);

            $webPush->queueNotification($sub, $payload);
        }

        $expiredEndpoints = [];

        foreach ($webPush->flush() as $report) {
            if (! $report->isSuccess()) {
                $expiredEndpoints[] = (string) $report->getRequest()->getUri();
            }
        }

        if (! empty($expiredEndpoints)) {
            PushSubscription::whereIn('endpoint', $expiredEndpoints)->delete();
        }
    }

    private function resolveIcon(): string
    {
        try {
            $configuration = SystemConfiguration::instance();
            $configuration->load('media');
            $logo = MediaPresenter::first($configuration, 'logo');

            return $logo['url'] ?? config('app.url').'/assets/logo.png';
        } catch (\Throwable) {
            return config('app.url').'/assets/logo.png';
        }
    }

    private function resolveSubscriptions()
    {
        $query = PushSubscription::query();

        if ($this->userId !== null) {
            $query->where('user_id', $this->userId);
        } elseif (! empty($this->roles)) {
            $query->whereHas('user', function (Builder $userQuery): void {
                $userQuery->whereHas('roles', fn (Builder $roleQuery) => $roleQuery->whereIn('name', $this->roles));
            });
        }

        return $query->get();
    }

    /**
     * @return list<int>
     */
    private function resolveUserIds(): array
    {
        if ($this->userId !== null) {
            return [$this->userId];
        }

        if (! empty($this->roles)) {
            return User::query()
                ->whereHas('roles', fn (Builder $q) => $q->whereIn('name', $this->roles))
                ->pluck('id')
                ->all();
        }

        return User::query()->pluck('id')->all();
    }
}
