<?php

namespace App\Notifications\Channels;

use App\Models\DeviceToken;
use App\Models\User;
use App\Support\FirebaseCredentials;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Log;
use Kreait\Firebase\Contract\Messaging;
use Kreait\Firebase\Exception\FirebaseException;
use Kreait\Firebase\Messaging\CloudMessage;
use Kreait\Firebase\Messaging\Notification as FcmNotification;
use Throwable;

class FcmChannel
{
    public function __construct(
        protected ?Messaging $messaging = null
    ) {}

    public function send(object $notifiable, Notification $notification): void
    {
        if (! method_exists($notification, 'toFcm')) {
            return;
        }

        /** @var array{title?: string, body?: string, data?: array<string, mixed>} $payload */
        $payload = $notification->toFcm($notifiable);
        $title = (string) ($payload['title'] ?? '');
        $body = (string) ($payload['body'] ?? '');
        if ($title === '' && $body === '') {
            return;
        }

        $tokens = $this->resolveTokens($notifiable);
        if ($tokens === []) {
            Log::info('FCM skipped: no device tokens for notifiable.', [
                'user_id' => $notifiable instanceof User ? $notifiable->id : null,
                'notification' => $notification::class,
            ]);

            return;
        }

        $messaging = $this->resolveMessaging();
        if ($messaging === null) {
            return;
        }

        $data = [];
        foreach (($payload['data'] ?? []) as $key => $value) {
            if ($value === null) {
                continue;
            }
            $data[(string) $key] = is_scalar($value) ? (string) $value : json_encode($value);
        }

        $message = CloudMessage::new()
            ->withNotification(FcmNotification::create(
                $title !== '' ? $title : 'KiyoHR',
                $body !== '' ? $body : $title
            ))
            ->withData($data)
            ->withAndroidConfig([
                'priority' => 'high',
                'notification' => [
                    'channel_id' => 'high_importance_channel',
                    'sound' => 'default',
                ],
            ])
            ->withApnsConfig([
                'payload' => [
                    'aps' => [
                        'sound' => 'default',
                        'badge' => 1,
                    ],
                ],
            ]);

        try {
            $report = $messaging->sendMulticast($message, $tokens);
            $successCount = $report->successes()->count();
            $failureCount = $report->failures()->count();
            $stale = array_values(array_unique(array_merge(
                $report->invalidTokens(),
                $report->unknownTokens()
            )));

            Log::info('FCM multicast completed', [
                'user_id' => $notifiable instanceof User ? $notifiable->id : null,
                'notification' => $notification::class,
                'tokens' => count($tokens),
                'success' => $successCount,
                'failure' => $failureCount,
                'invalid' => count($stale),
            ]);

            if ($stale !== []) {
                DeviceToken::query()->whereIn('token', $stale)->delete();
                Log::info('FCM removed stale device tokens', [
                    'user_id' => $notifiable instanceof User ? $notifiable->id : null,
                    'count' => count($stale),
                ]);
            }
        } catch (FirebaseException|Throwable $e) {
            Log::warning('FCM send failed', [
                'error' => $e->getMessage(),
                'user_id' => $notifiable instanceof User ? $notifiable->id : null,
                'notification' => $notification::class,
                'tokens' => count($tokens),
            ]);
        }
    }

    /**
     * @return list<string>
     */
    protected function resolveTokens(object $notifiable): array
    {
        if (method_exists($notifiable, 'routeNotificationForFcm')) {
            $tokens = $notifiable->routeNotificationForFcm();
            if (is_array($tokens)) {
                return array_values(array_filter(array_map('strval', $tokens)));
            }
            if (is_string($tokens) && $tokens !== '') {
                return [$tokens];
            }
        }

        if ($notifiable instanceof User) {
            return $notifiable->deviceTokens()->pluck('token')->filter()->values()->all();
        }

        return [];
    }

    protected function resolveMessaging(): ?Messaging
    {
        if ($this->messaging instanceof Messaging) {
            return $this->messaging;
        }

        $credentials = FirebaseCredentials::path();
        if ($credentials === null) {
            Log::warning('FCM skipped: FIREBASE_CREDENTIALS not configured or file missing.', [
                'configured' => config('firebase.projects.app.credentials') ?: env('FIREBASE_CREDENTIALS'),
                'expected' => storage_path(FirebaseCredentials::DEFAULT_RELATIVE),
            ]);

            return null;
        }

        // Always point the Admin SDK at a path that exists (case-resolved).
        config(['firebase.projects.app.credentials' => $credentials]);

        try {
            return app('firebase.messaging');
        } catch (Throwable $e) {
            Log::warning('FCM messaging unavailable', ['error' => $e->getMessage()]);

            return null;
        }
    }
}
