<?php

namespace App\Services;

use Illuminate\Support\Facades\Http;
use RuntimeException;

class FirebasePushService
{
    public function isConfigured(): bool
    {
        try {
            $serviceAccount = $this->serviceAccount();

            return !empty($serviceAccount['client_email'])
                && !empty($serviceAccount['private_key'])
                && !empty($this->projectId($serviceAccount));
        } catch (RuntimeException) {
            return false;
        }
    }

    public function sendToToken(string $token, string $title, string $body, array $data = [], ?string $imageUrl = null): array
    {
        $serviceAccount = $this->serviceAccount();
        $projectId = $this->projectId($serviceAccount);
        $accessToken = $this->accessToken($serviceAccount);

        $notification = ['title' => $title, 'body' => $body];
        if ($imageUrl !== null) {
            $notification['image'] = $imageUrl;
        }

        $androidNotification = ['channel_id' => 'pos_alerts'];
        if ($imageUrl !== null) {
            $androidNotification['image'] = $imageUrl;
        }

        $message = [
            'token' => $token,
            'notification' => $notification,
            'data' => collect($data)->map(fn ($value) => (string) $value)->all(),
            'android' => [
                'priority' => 'high',
                'notification' => $androidNotification,
            ],
        ];

        if ($imageUrl !== null) {
            $message['apns'] = [
                'payload' => ['aps' => ['mutable-content' => 1]],
                'fcm_options' => ['image' => $imageUrl],
            ];
        }

        $response = Http::withToken($accessToken)
            ->acceptJson()
            ->post("https://fcm.googleapis.com/v1/projects/{$projectId}/messages:send", [
                'message' => $message,
            ]);

        return [
            'ok' => $response->successful(),
            'status' => $response->status(),
            'body' => $response->json() ?: $response->body(),
        ];
    }

    public function sendFromTemplate(string $event, \App\Models\User $user, array $extraVars = []): void
    {
        $template = \App\Models\NotificationTemplate::forEvent($event);
        if (!$template) {
            return;
        }

        $devices = $user->devices()->whereNotNull('fcm_token')->get();
        if ($devices->isEmpty()) {
            return;
        }

        $sub = $user->activeSubscription();
        $planName = ucfirst($user->currentPlan());
        $daysLeft = $sub ? (int) now()->diffInDays($sub->expires_at, false) : 0;

        $vars = array_merge([
            '{name}'       => $user->name,
            '{email}'      => $user->email,
            '{plan}'       => $planName,
            '{expires_at}' => $sub ? ($sub->expires_at?->format('Y-m-d') ?? '') : '',
            '{days}'       => (string) max(0, $daysLeft),
        ], $extraVars);

        $rendered = $template->render($vars);

        foreach ($devices as $device) {
            try {
                $this->sendToToken(
                    $device->fcm_token,
                    $rendered['title'],
                    $rendered['body'],
                    ['event' => $event],
                    $template->image_url
                );
            } catch (\Throwable) {
                // best-effort: continue to next device
            }
        }
    }

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

        $signatureInput = $header . '.' . $payload;
        $privateKey = openssl_pkey_get_private($serviceAccount['private_key']);
        if (!$privateKey) {
            throw new RuntimeException('Firebase private key is invalid.');
        }

        $signature = '';
        openssl_sign($signatureInput, $signature, $privateKey, OPENSSL_ALGO_SHA256);
        $jwt = $signatureInput . '.' . $this->base64UrlEncode($signature);

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

        if ($response->failed() || !$response->json('access_token')) {
            throw new RuntimeException('Unable to obtain Firebase access token.');
        }

        return (string) $response->json('access_token');
    }

    private function serviceAccount(): array
    {
        $path = config('services.firebase.service_account_json');
        if (!$path) {
            throw new RuntimeException('Firebase service account JSON is not configured.');
        }

        $resolvedPath = str_starts_with($path, DIRECTORY_SEPARATOR) || preg_match('/^[A-Za-z]:\\\\/', $path)
            ? $path
            : base_path($path);

        if (!is_file($resolvedPath)) {
            throw new RuntimeException('Firebase service account JSON file not found.');
        }

        $decoded = json_decode((string) file_get_contents($resolvedPath), true);
        if (!is_array($decoded)) {
            throw new RuntimeException('Firebase service account JSON is invalid.');
        }

        return $decoded;
    }

    private function projectId(array $serviceAccount): string
    {
        return (string) (config('services.firebase.project_id') ?: ($serviceAccount['project_id'] ?? ''));
    }

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