<?php

namespace App\Services;

use App\Events\ContextualNotificationCreated;
use App\Jobs\SendWebPushJob;
use App\Models\User;
use App\Notifications\ContextualNotification;
use Illuminate\Broadcasting\BroadcastException;
use Illuminate\Notifications\DatabaseNotification;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Bus;
use Illuminate\Support\Facades\Log;

class NotificationService
{
    /**
     * @param  array<string,mixed>  $context
     */
    public function emit(User $user, string $type, array $context = []): ?DatabaseNotification
    {
        // Dotted type keys (e.g. "subscription.renewal_soon") must be read from the types array
        // directly: config("...types.$type") would treat dots as nested keys and always return null.
        /** @var array<string, array<string, mixed>> $types */
        $types = config('contextual_notifications.types', []);
        $typeConfig = $types[$type] ?? null;
        if (! is_array($typeConfig)) {
            return null;
        }

        if (! $this->isAllowedByPreferences($type, $context)) {
            return null;
        }

        $dedupeKey = (string) ($context['dedupe_key'] ?? '');
        if ($dedupeKey !== '' && $this->isDuplicate($user, $dedupeKey, (int) ($typeConfig['cooldown_hours'] ?? 24))) {
            return null;
        }

        $payload = [
            'type' => $type,
            'module' => (string) ($typeConfig['module'] ?? 'system'),
            'severity' => (string) ($context['severity'] ?? ($typeConfig['severity'] ?? 'info')),
            'priority' => (string) ($typeConfig['priority'] ?? 'normal'),
            'title' => (string) ($context['title'] ?? $type),
            'message' => (string) ($context['message'] ?? ''),
            'action_url' => (string) ($context['action_url'] ?? ($typeConfig['action_url'] ?? '/dashboard')),
            'entity_type' => $context['entity_type'] ?? null,
            'entity_id' => $context['entity_id'] ?? null,
            'dedupe_key' => $dedupeKey,
            'created_at' => now()->toIso8601String(),
        ];

        $user->notify(new ContextualNotification($payload));

        /** @var DatabaseNotification|null $notification */
        $notification = $user->notifications()->latest()->first();
        if ($notification === null) {
            return null;
        }

        $broadcastPayload = array_merge($payload, ['id' => $notification->id, 'read_at' => $notification->read_at]);
        try {
            event(new ContextualNotificationCreated($user->id, $broadcastPayload));
        } catch (BroadcastException $e) {
            Log::warning('Contextual notification broadcast failed.', [
                'user_id' => $user->id,
                'message' => $e->getMessage(),
            ]);
        }

        if (in_array('webpush', (array) ($typeConfig['channels'] ?? []), true) && $this->pushEnabled($context)) {
            $user->loadMissing('pushSubscriptions');
            foreach ($user->pushSubscriptions as $subscription) {
                $job = new SendWebPushJob($subscription->id, [
                    'title' => $payload['title'],
                    'body' => $payload['message'],
                    'url' => $payload['action_url'],
                    'notification_id' => $notification->id,
                    'type' => $type,
                    'severity' => $payload['severity'],
                ]);
                if (config('queue.default') === 'sync') {
                    Bus::dispatchSync($job);
                } else {
                    Bus::dispatch($job);
                }
            }
        }

        return $notification;
    }

    private function isAllowedByPreferences(string $type, array $context): bool
    {
        $preferences = PreferencesStore::all();
        $notifications = (array) ($preferences['notifications'] ?? []);

        if (str_starts_with($type, 'system.')) {
            return true;
        }

        if (! empty($context['is_silent'])) {
            return true;
        }

        if (str_starts_with($type, 'budget.')) {
            return ! empty($notifications['budgetAlerts']);
        }

        if (str_starts_with($type, 'transaction.')) {
            return ! empty($notifications['billReminders']);
        }

        if (str_starts_with($type, 'goal.')) {
            return ! empty($notifications['billReminders']);
        }

        return ! empty($notifications['billReminders']);
    }

    private function pushEnabled(array $context): bool
    {
        if (($context['force_push'] ?? false) === true) {
            return true;
        }

        $preferences = PreferencesStore::all();
        $notifications = (array) ($preferences['notifications'] ?? []);

        return ! empty($notifications['pushNotifications']);
    }

    private function isDuplicate(User $user, string $dedupeKey, int $cooldownHours): bool
    {
        $cutoff = Carbon::now()->subHours(max(1, $cooldownHours));

        return $user->notifications()
            ->where('data->dedupe_key', $dedupeKey)
            ->where('created_at', '>=', $cutoff)
            ->exists();
    }
}
