<?php

namespace App\Services;

use Google\Auth\Credentials\ServiceAccountCredentials;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use RuntimeException;
use App\Models\User;
use App\Models\UserNotification;
use App\Models\UserPushToken;

class FirebaseCloudMessagingService
{
    private const MESSAGING_SCOPE = 'https://www.googleapis.com/auth/firebase.messaging';

    /**
     * Send a visible notification with a data payload to an Android or iOS device.
     */
    public function send(string $deviceToken, string $title, string $body, array $data = [], bool $store = true): bool
    {
        if ($store) $this->storeNotification($deviceToken, $title, $body, $data);
        if (! config('services.firebase.enabled', true)) {
            Log::info('FCM notification skipped because Firebase is disabled.');
            return false;
        }

        try {
            $credentials = $this->credentials();
            $projectId = config('services.firebase.project_id') ?: ($credentials['project_id'] ?? null);

            if (! $projectId) {
                throw new RuntimeException('Firebase project ID is not configured.');
            }

            $auth = new ServiceAccountCredentials(self::MESSAGING_SCOPE, $credentials);
            $accessToken = $auth->fetchAuthToken()['access_token'] ?? null;

            if (! $accessToken) {
                throw new RuntimeException('Unable to obtain a Firebase access token.');
            }

            $response = Http::withToken($accessToken)
                ->acceptJson()
                ->post("https://fcm.googleapis.com/v1/projects/{$projectId}/messages:send", [
                    'message' => [
                        'token' => $deviceToken,
                        'notification' => [
                            'title' => $title,
                            'body' => $body,
                        ],
                        'data' => collect($data)->map(fn ($value) => (string) $value)->all(),
                        'android' => [
                            'priority' => 'high',
                            'notification' => [
                                'sound' => 'default',
                                'channel_id' => config('services.firebase.android_channel_id', 'notifications'),
                            ],
                        ],
                        'apns' => [
                            'headers' => [
                                'apns-priority' => '10',
                                'apns-push-type' => 'alert',
                                'apns-topic' => config('services.firebase.ios_bundle_id'),
                            ],
                            'payload' => [
                                'aps' => [
                                    'sound' => 'default',
                                    'content-available' => 1,
                                ],
                            ],
                        ],
                    ],
                ]);

            if ($response->failed()) {
                Log::warning('FCM notification failed.', [
                    'status' => $response->status(),
                    'response' => $response->json(),
                ]);
                $errorCode = $response->json('error.details.0.errorCode') ?? $response->json('error.status');
                if (in_array($errorCode, ['UNREGISTERED', 'INVALID_ARGUMENT'], true)) {
                    UserPushToken::where('token_hash', hash('sha256', trim($deviceToken)))->delete();
                    User::where('push_token', $deviceToken)->update(['push_token' => null]);
                }
            } else {
                Log::info('FCM notification accepted.', [
                    'message_name' => $response->json('name'),
                    'project_id' => $projectId,
                    'token_suffix' => substr($deviceToken, -8),
                ]);
            }

            return $response->successful();
        } catch (\Throwable $exception) {
            Log::warning('FCM notification could not be sent.', [
                'error' => $exception->getMessage(),
            ]);

            return false;
        }
    }

    public function sendToUser(?User $user, string $title, string $body, array $data = []): bool
    {
        if (! $user) return false;
        $this->storeNotificationForUser($user->id, $title, $body, $data);
        if (! $this->notificationEnabled($user, $data)) {
            Log::info('Push notification skipped by user preference.', [
                'user_id' => $user->id,
                'notification_type' => $data['type'] ?? null,
            ]);
            return true;
        }
        $tokens = $user->pushTokens()->pluck('token');
        if (filled($user->push_token)) $tokens->push($user->push_token);
        $tokens = $tokens->map(fn ($token) => trim((string) $token))->filter()->unique()->values();
        $sent = false;
        foreach ($tokens as $token) $sent = $this->send($token, $title, $body, $data, false) || $sent;
        return $sent;
    }

    private function notificationEnabled(User $user, array $data): bool
    {
        $type = (string) ($data['type'] ?? '');
        $role = ($data['recipient_role'] ?? null) === 'professional' ? 'professional' : 'client';
        $category = match ($type) {
            'payment_confirmed', 'payment_status_changed', 'instant_professional_selected' => 'payment',
            'appointment_reminder', 'consultation_ending_soon' => 'reminder',
            'booking_created', 'booking_status_changed', 'booking_expired',
            'instant_booking_request', 'instant_offer_received' => 'booking',
            default => null,
        };
        if (! $category) return true;

        return (bool) $user->{"{$role}_{$category}_notifications"};
    }

    private function storeNotificationForUser(int $userId, string $title, string $body, array $data): void
    {
        UserNotification::create(['user_id' => $userId, 'title' => $title, 'body' => $body,
            'data' => collect($data)->map(fn ($value) => (string) $value)->all()]);
    }

    private function storeNotification(string $deviceToken, string $title, string $body, array $data): void
    {
        try {
            $userId = User::where('push_token', $deviceToken)->latest('updated_at')->value('id');
            if (! $userId) {
                Log::warning('In-app notification was not stored because no user owns the push token.', [
                    'token_suffix' => substr($deviceToken, -8),
                    'notification_type' => $data['type'] ?? null,
                ]);
                return;
            }
            $notification = UserNotification::create([
                'user_id' => $userId,
                'title' => $title,
                'body' => $body,
                'data' => collect($data)->map(fn ($value) => (string) $value)->all(),
            ]);
            Log::info('In-app notification stored.', [
                'notification_id' => $notification->id,
                'user_id' => $userId,
                'notification_type' => $data['type'] ?? null,
            ]);
        } catch (\Throwable $exception) {
            Log::warning('Unable to store in-app notification.', ['error' => $exception->getMessage()]);
        }
    }

    private function credentials(): array
    {
        $path = config('services.firebase.credentials');

        if ($path && ! str_starts_with($path, DIRECTORY_SEPARATOR)) {
            $path = base_path($path);
        }

        if (! $path || ! is_file($path) || ! is_readable($path)) {
            throw new RuntimeException('Firebase service-account credentials are not configured or readable.');
        }

        $credentials = json_decode(file_get_contents($path), true, flags: JSON_THROW_ON_ERROR);

        if (! is_array($credentials)) {
            throw new RuntimeException('Firebase service-account credentials are invalid.');
        }

        return $credentials;
    }
}
