/home2/mshostin/crm.ms-hostingladz.com/app/Services/FirebaseNotificationService.php
<?php

namespace App\Services;

use Kreait\Firebase\Factory;
use Kreait\Firebase\Messaging\CloudMessage;
use Kreait\Firebase\Messaging\Notification;
use Kreait\Firebase\Messaging\WebPushConfig;
use Kreait\Firebase\Messaging\AndroidConfig;
use Kreait\Firebase\Messaging\ApnsConfig;
use Kreait\Firebase\Exception\MessagingException;
use Kreait\Firebase\Exception\FirebaseException;
use App\Models\Notification as NotificationModel;
use Illuminate\Support\Facades\Log;
use Exception;

class FirebaseNotificationService
{
    protected $messaging;

    public function __construct()
    {
        try {
            $factory = new Factory();

            // Check if using environment variables or service account file
            if (config('firebase.use_env', false) && env('FIREBASE_PROJECT_ID')) {
                // Use environment variables
                $factory = $factory->withServiceAccount([
                    'type' => 'service_account',
                    'project_id' => env('FIREBASE_PROJECT_ID'),
                    'private_key_id' => env('FIREBASE_PRIVATE_KEY_ID'),
                    'private_key' => env('FIREBASE_PRIVATE_KEY'),
                    'client_email' => env('FIREBASE_CLIENT_EMAIL'),
                    'client_id' => env('FIREBASE_CLIENT_ID'),
                    'auth_uri' => env('FIREBASE_AUTH_URI', 'https://accounts.google.com/o/oauth2/auth'),
                    'token_uri' => env('FIREBASE_TOKEN_URI', 'https://oauth2.googleapis.com/token'),
                    'auth_provider_x509_cert_url' => env('FIREBASE_AUTH_PROVIDER_X509_CERT_URL', 'https://www.googleapis.com/oauth2/v1/certs'),
                    'client_x509_cert_url' => env('FIREBASE_CLIENT_X509_CERT_URL'),
                ]);
            } else {
                // Use service account file
                $credentialsPath = storage_path('app/firebase/firebase_credentials.json');

                if (!file_exists($credentialsPath)) {
                    throw new Exception('Firebase credentials file not found at: ' . $credentialsPath);
                }

                $factory = $factory->withServiceAccount($credentialsPath);
            }

            $this->messaging = $factory->createMessaging();

        } catch (Exception $e) {
            Log::error('Firebase initialization failed: ' . $e->getMessage());
            // Do not throw exception to allow app to boot without Firebase
            $this->messaging = null;
        }
    }

    /**
     * Send a push notification to one or more device tokens.
     *
     * @param array|string $deviceTokens
     * @param string $title
     * @param string $body
     * @param array $data
     * @param string $type
     * @param int|null $recipientId
     * @param string|null $recipientType
     * @param int|null $senderId
     * @return array
     */
    public function sendNotification($deviceTokens, string $title, string $body, array $data = [], string $type = 'general', $recipientId = null, $recipientType = null, $senderId = null,$platform = 'android' )
    {
        $results = [];
        $successCount = 0;
        $failureCount = 0;

        try {
            if (!$this->messaging) {
                Log::warning('Firebase service is not initialized. Notification skipped.');
                return ['success' => 0, 'failure' => 1, 'results' => ['error' => 'Firebase not initialized']];
            }

            $notification = Notification::create($title, $body);

        // Ensure all data values are strings
        $stringData = [];
        foreach ($data as $key => $value) {
            $stringData[$key] = is_scalar($value) || $value === null
                ? (string) $value
                : json_encode($value);
        }

        $message = CloudMessage::new()->withData($stringData);

        // Platform-specific configs
        if ($platform === 'android') {
            $message = $message->withAndroidConfig(AndroidConfig::fromArray([
                'priority' => 'high',
                'notification' => [
                    'sound' => 'default',
                ],
            ]));
        } elseif ($platform === 'ios') {
            $message = $message->withApnsConfig(ApnsConfig::fromArray([
                'headers' => ['apns-priority' => '10'],
                'payload' => [
                    'aps' => [
                        'sound' => 'default',
                        'alert' => ['title' => $title, 'body' => $body],
                    ],
                ],
            ]));
        } elseif ($platform === 'web') {
            $message = $message->withWebPushConfig(WebPushConfig::fromArray([
                'headers' => ['Urgency' => 'high'],
                'notification' => [
                    'title' => $title,
                    'body' => $body,
                    'icon' => '/firebase-logo.png',
                ],
            ]));
        }

        // Attach notification if not data-only
        $includeNotification = empty($stringData['_data_only'])
            || $stringData['_data_only'] === 'false'
            || $stringData['_data_only'] === '0';

        if ($includeNotification) {
            $message = $message->withNotification($notification);
        }

        // Handle single or multiple tokens
        $tokens = is_array($deviceTokens) ? $deviceTokens : [$deviceTokens];

        foreach ($tokens as $token) {
            $result = $this->sendToToken($message, $token, $title, $body, $data, $type, $recipientId, $recipientType, $senderId);
            $results[] = $result;
            $result['success'] ? $successCount++ : $failureCount++;
        }

        Log::info("Firebase notification sent", [
            'success_count' => $successCount,
            'failure_count' => $failureCount,
            'total' => count($results)
        ]);

        } catch (MessagingException $e) {
            Log::error('Firebase notification failed (MessagingException): ' . $e->getMessage(), [
                'errors' => method_exists($e, 'errors') ? $e->errors() : null,
            ]);
            throw $e;
        } catch (FirebaseException $e) {
            Log::error('Firebase notification failed (FirebaseException): ' . $e->getMessage());
            throw $e;
        } catch (Exception $e) {
            Log::error('Firebase notification failed: ' . $e->getMessage());
            throw $e;
        }

        return [
            'success_count' => $successCount,
            'failure_count' => $failureCount,
            'results' => $results
        ];
    }

    /**
     * Send notification to a specific token
     */
    private function sendToToken($message, $token, $title, $body, $data, $type, $recipientId, $recipientType, $senderId)
    {
        try {
            $result = $this->messaging->send($message->withChangedTarget('token', $token));

            // Log successful notification
            $this->logNotification($title, $body, $data, $type, $recipientId, $recipientType, $senderId, 'delivered');

            return [
                'success' => true,
                'token' => $token,
                'message_id' => $result,
                'error' => null
            ];
        } catch (MessagingException $e) {
            Log::error("Failed to send notification to token {$token} (MessagingException): " . $e->getMessage(), [
                'errors' => method_exists($e, 'errors') ? $e->errors() : null,
            ]);

            // Log failed notification
            $this->logNotification($title, $body, $data, $type, $recipientId, $recipientType, $senderId, 'failed', $e->getMessage());

            return [
                'success' => false,
                'token' => $token,
                'message_id' => null,
                'error' => $e->getMessage()
            ];
        } catch (FirebaseException $e) {
            Log::error("Failed to send notification to token {$token} (FirebaseException): " . $e->getMessage());

            $this->logNotification($title, $body, $data, $type, $recipientId, $recipientType, $senderId, 'failed', $e->getMessage());

            return [
                'success' => false,
                'token' => $token,
                'message_id' => null,
                'error' => $e->getMessage()
            ];
        } catch (Exception $e) {
            Log::error("Failed to send notification to token {$token}: " . $e->getMessage());

            $this->logNotification($title, $body, $data, $type, $recipientId, $recipientType, $senderId, 'failed', $e->getMessage());

            return [
                'success' => false,
                'token' => $token,
                'message_id' => null,
                'error' => $e->getMessage()
            ];
        }
    }

    /**
     * Log notification to database
     */
    private function logNotification($title, $body, $data, $type, $recipientId, $recipientType, $senderId, $status, $errorMessage = null)
    {
        try {
            NotificationModel::create([
                'title' => $title,
                'body' => $body,
                'data' => $data,
                'type' => $type,
                'status' => $status,
                'recipient_id' => $recipientId,
                'recipient_type' => $recipientType,
                'sender_id' => $senderId,
                'sent_at' => now(),
                'delivered_at' => $status === 'delivered' ? now() : null,
                'error_message' => $errorMessage,
            ]);
        } catch (Exception $e) {
            Log::error('Failed to log notification: ' . $e->getMessage());
        }
    }

    /**
     * Send notification to all agents
     */
    public function sendToAllAgents($title, $body, $data = [], $senderId = null)
    {
        $agents = \App\Models\MobileAgent::with('user')
            ->whereHas('user', function($query) {
                $query->whereNotNull('device_token');
            })
            ->get();

        $tokens = $agents->pluck('user.device_token')->filter()->toArray();

        if (empty($tokens)) {
            return ['success_count' => 0, 'failure_count' => 0, 'results' => []];
        }

        return $this->sendNotification($tokens, $title, $body, $data, 'general', null, 'all', $senderId);
    }


    /**
     * Send notification to agents by type
     */
    public function sendToAgentsByType($typeId, $title, $body, $data = [], $senderId = null)
    {
        $agents = \App\Models\MobileAgent::with('user')
            ->where('type_id', $typeId)
            ->whereHas('user', function($query) {
                $query->whereNotNull('device_token');
            })
            ->get();

        $tokens = $agents->pluck('user.device_token')->filter()->toArray();

        if (empty($tokens)) {
            return ['success_count' => 0, 'failure_count' => 0, 'results' => []];
        }

        return $this->sendNotification($tokens, $title, $body, $data, 'general', $typeId, 'type', $senderId);
    }
}