<?php

declare(strict_types=1);

namespace App\Services;

use GuzzleHttp\Client;

/**
 * Envía notificaciones push vía FCM HTTP v1 API usando un Service Account
 * de Firebase. GCM (predecesor de FCM) fue descontinuado en 2019.
 */
class PushService
{
    private Client $http;
    private string $projectId;
    private ?array $serviceAccount;

    public function __construct(?Client $http = null)
    {
        $this->http = $http ?? new Client(['timeout' => 5.0]);
        $this->projectId = $_ENV['FCM_PROJECT_ID'] ?? '';

        $path = $_ENV['FCM_SERVICE_ACCOUNT_JSON'] ?? '';
        $this->serviceAccount = ($path && is_file($path))
            ? json_decode((string) file_get_contents($path), true)
            : null;
    }

    /**
     * @param string[] $tokens Device tokens FCM del/de los destinatarios.
     * @param array<string, string> $data Payload adicional (ej. pedido_id, estado).
     */
    public function enviar(array $tokens, string $titulo, string $cuerpo, array $data = []): void
    {
        if ($this->serviceAccount === null || empty($tokens)) {
            return;
        }

        $accessToken = $this->obtenerAccessToken();

        foreach ($tokens as $token) {
            $this->enviarAUnToken($accessToken, $token, $titulo, $cuerpo, $data);
        }
    }

    private function enviarAUnToken(string $accessToken, string $token, string $titulo, string $cuerpo, array $data): void
    {
        try {
            $this->http->post("https://fcm.googleapis.com/v1/projects/{$this->projectId}/messages:send", [
                'headers' => [
                    'Authorization' => "Bearer {$accessToken}",
                    'Content-Type' => 'application/json',
                ],
                'json' => [
                    'message' => [
                        'token' => $token,
                        'notification' => [
                            'title' => $titulo,
                            'body' => $cuerpo,
                        ],
                        'data' => $data,
                    ],
                ],
            ]);
        } catch (\Throwable $e) {
            // Token inválido/expirado u otro fallo puntual: no debe interrumpir el flujo del pedido.
            error_log('PushService: fallo al enviar a token ' . substr($token, 0, 12) . '... ' . $e->getMessage());
        }
    }

    private function obtenerAccessToken(): string
    {
        $now = time();
        $header = $this->base64UrlEncode(json_encode(['alg' => 'RS256', 'typ' => 'JWT']));
        $claims = $this->base64UrlEncode(json_encode([
            'iss' => $this->serviceAccount['client_email'],
            'scope' => 'https://www.googleapis.com/auth/firebase.messaging',
            'aud' => 'https://oauth2.googleapis.com/token',
            'iat' => $now,
            'exp' => $now + 3600,
        ]));

        $signatureInput = "{$header}.{$claims}";
        openssl_sign($signatureInput, $signature, $this->serviceAccount['private_key'], 'sha256WithRSAEncryption');
        $jwt = $signatureInput . '.' . $this->base64UrlEncode($signature);

        $response = $this->http->post('https://oauth2.googleapis.com/token', [
            'form_params' => [
                'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
                'assertion' => $jwt,
            ],
        ]);

        $body = json_decode((string) $response->getBody(), true);

        return $body['access_token'];
    }

    private function base64UrlEncode(string $data): string
    {
        return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
    }
}
