<?php

namespace App\Services;

use App\Models\User;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;

class FcmService
{
    /**
     * Envía una notificación push a un usuario específico
     *
     * @param  User|int  $userOrId
     */
    public static function sendNotification($userOrId, string $title, string $body, array $data = []): bool
    {
        $user = $userOrId instanceof User ? $userOrId : User::find($userOrId);

        if (!$user) {
            Log::info("No se puede enviar push/notificación: Usuario #{$userOrId} no existe.");
            return false;
        }

        // Guardar notificación en base de datos
        try {
            \App\Models\UserNotification::create([
                'user_id' => $user->id,
                'title' => $title,
                'body' => $body,
                'is_read' => false,
            ]);
        } catch (\Exception $e) {
            Log::error("Error al guardar notificación en DB para usuario #{$user->id}: " . $e->getMessage());
        }

        if (!$user->fcm_token) {
            Log::info("No se puede enviar push: Usuario #{$user->id} no tiene fcm_token registrado. Notificación guardada en DB.");

            return true;
        }

        return self::sendPushNotification($user->fcm_token, $title, $body, $data);
    }

    /**
     * Envía la notificación push a través de la API HTTP v1 de FCM
     */
    public static function sendPushNotification(string $fcmToken, string $title, string $body, array $data = []): bool
    {
        $projectId = config('services.fcm.project_id');
        if (! $projectId) {
            Log::warning('FCM_PROJECT_ID no configurado en el backend.');

            return false;
        }

        $accessToken = self::getAccessToken();
        if (! $accessToken) {
            Log::error('No se pudo obtener el token de acceso OAuth2 para Firebase.');

            return false;
        }

        // Convertir todos los valores de data a String (requisito de FCM)
        $formattedData = [];
        foreach ($data as $key => $value) {
            $formattedData[(string) $key] = (string) $value;
        }

        $response = Http::withToken($accessToken)
            ->post("https://fcm.googleapis.com/v1/projects/{$projectId}/messages:send", [
                'message' => [
                    'token' => $fcmToken,
                    'notification' => [
                        'title' => $title,
                        'body' => $body,
                    ],
                    // FCM exige un objeto JSON para "data"; un array PHP vacío se
                    // serializa como "[]" (lista) en vez de "{}" y la API lo rechaza.
                    'data' => (object) $formattedData,
                ],
            ]);

        if ($response->successful()) {
            Log::info('Notificación push enviada con éxito al token: '.substr($fcmToken, 0, 15).'...');

            return true;
        }

        Log::error('Fallo al enviar notificación push FCM v1 API.', [
            'status' => $response->status(),
            'body' => $response->body(),
        ]);

        return false;
    }

    /**
     * Obtiene el token de acceso OAuth2 de Google para FCM v1
     */
    private static function getAccessToken(): ?string
    {
        return Cache::remember('fcm_oauth_token', 3000, function () {
            $filePath = config('services.fcm.service_account_path');
            if (! file_exists($filePath)) {
                Log::warning("Archivo de credenciales Service Account de Firebase no encontrado en: {$filePath}. Asegúrate de crear el archivo.");

                return null;
            }

            try {
                $serviceAccount = json_decode(file_get_contents($filePath), true);
                if (! $serviceAccount || ! isset($serviceAccount['private_key']) || ! isset($serviceAccount['client_email'])) {
                    Log::error('Formato inválido del archivo JSON de Service Account.');

                    return null;
                }

                $privateKey = $serviceAccount['private_key'];
                $clientEmail = $serviceAccount['client_email'];

                // Construir JWT Claims
                $now = time();
                $header = ['alg' => 'RS256', 'typ' => 'JWT'];
                $payload = [
                    'iss' => $clientEmail,
                    'scope' => 'https://www.googleapis.com/auth/firebase.messaging',
                    'aud' => 'https://oauth2.googleapis.com/token',
                    'iat' => $now,
                    'exp' => $now + 3600,
                ];

                $encodedHeader = self::base64UrlEncode(json_encode($header));
                $encodedPayload = self::base64UrlEncode(json_encode($payload));

                $signatureInput = $encodedHeader.'.'.$encodedPayload;
                $signature = '';

                if (! openssl_sign($signatureInput, $signature, $privateKey, OPENSSL_ALGO_SHA256)) {
                    Log::error('Fallo al firmar el JWT de aserción de Google con OpenSSL.');

                    return null;
                }

                $encodedSignature = self::base64UrlEncode($signature);
                $assertion = $signatureInput.'.'.$encodedSignature;

                // Intercambiar JWT Assertion por token de acceso OAuth2
                $response = Http::asForm()->post('https://oauth2.googleapis.com/token', [
                    'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
                    'assertion' => $assertion,
                ]);

                if ($response->successful()) {
                    return $response->json('access_token');
                }

                Log::error('Intercambio de Token de Firebase fallido en Google OAuth2.', [
                    'status' => $response->status(),
                    'body' => $response->body(),
                ]);
            } catch (\Exception $e) {
                Log::error('Excepción ocurrida al generar Token OAuth2 de Firebase: '.$e->getMessage());
            }

            return null;
        });
    }

    /**
     * Codifica en base64 Url-Safe
     */
    private static function base64UrlEncode(string $data): string
    {
        return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($data));
    }
}
