<?php

namespace Modules\Bms\Services\Notifications;

use Illuminate\Support\Facades\Log;
use Minishlink\WebPush\Subscription;
use Minishlink\WebPush\WebPush;
use Modules\Bms\Entities\BmsPushSubscription;
use Modules\Bms\Services\Contracts\PushServiceContract;

/**
 * BMS-native web-push delivery on minishlink/web-push. Targets the relevant
 * subscriptions for a notification: client subscriptions for the addressed
 * contact (resolved from the to[] phones/emails), plus employee subscriptions
 * for staff-oriented update types. Dead subscriptions (404/410) are pruned.
 */
class PushService implements PushServiceContract
{
    /** Update types that fan out to the whole staff team. */
    protected const STAFF_TYPES = ['new_booking', 'task_assigned', 'transfer_accepted', 'maintenance_status'];

    public function enabled(): bool
    {
        return $this->publicKey() && (bool) config('bms.vapid.private_key');
    }

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

    public function sendForNotification(int $businessId, array $payload): array
    {
        if (! $this->enabled()) {
            return ['ok' => false, 'skipped' => 'vapid_unconfigured'];
        }

        $subs = $this->targets($businessId, $payload);
        if ($subs->isEmpty()) {
            return ['ok' => true, 'sent' => 0, 'pruned' => 0];
        }

        $auth = ['VAPID' => [
            'subject'    => config('bms.vapid.subject', 'mailto:admin@example.com'),
            'publicKey'  => config('bms.vapid.public_key'),
            'privateKey' => config('bms.vapid.private_key'),
        ]];
        $webPush = new WebPush($auth);

        $body = json_encode([
            'title' => $payload['subject'] ?? 'Banquet',
            'body'  => $payload['body'] ?? '',
            'data'  => $payload['data'] ?? [],
        ]);

        foreach ($subs as $s) {
            try {
                $webPush->queueNotification(
                    Subscription::create([
                        'endpoint' => $s->endpoint,
                        'keys'     => ['p256dh' => $s->p256dh, 'auth' => $s->auth],
                    ]),
                    $body
                );
            } catch (\Throwable $e) {
                Log::warning('[BMS] push queue failed: ' . $e->getMessage());
            }
        }

        $sent = 0;
        $pruned = 0;
        foreach ($webPush->flush() as $report) {
            if ($report->isSuccess()) {
                $sent++;
            } elseif (in_array($report->getResponse() ? $report->getResponse()->getStatusCode() : 0, [404, 410], true)) {
                BmsPushSubscription::where('endpoint_hash', hash('sha256', $report->getEndpoint()))->delete();
                $pruned++;
            }
        }

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

    /** Resolve the subscriptions that should receive this notification. */
    protected function targets(int $businessId, array $payload): \Illuminate\Support\Collection
    {
        $type = $payload['update_type'] ?? '';
        $query = BmsPushSubscription::where('business_id', $businessId);

        // Staff-oriented updates → all employee subscriptions.
        if (in_array($type, self::STAFF_TYPES, true)) {
            return $query->where('guard', 'employee')->get();
        }

        // Client-oriented → the addressed contact's subscriptions.
        $to = (array) ($payload['to'] ?? []);
        $contactIds = \App\Contact::where('business_id', $businessId)
            ->where(function ($q) use ($to) {
                foreach ($to as $addr) {
                    $q->orWhere('mobile', $addr)->orWhere('email', $addr);
                }
            })->pluck('id');

        if ($contactIds->isEmpty()) {
            return collect();
        }
        return $query->where('guard', 'client')->whereIn('subject_id', $contactIds)->get();
    }
}
