<?php

namespace App\Domain\Notification\Services;

use App\Domain\Identity\Models\Guardian;
use App\Domain\Notification\Enums\NotificationChannel;
use App\Domain\Notification\Enums\NotificationStatus;
use App\Domain\Notification\Models\NotificationLog;
use App\Domain\Participant\Models\Participant;
use App\Domain\Shared\Models\DeviceToken;
use App\Domain\Shared\Models\SystemSetting;
use App\Models\User;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Kreait\Firebase\Factory;
use Kreait\Firebase\Messaging\CloudMessage;
use Kreait\Firebase\Messaging\Notification;

class PushNotificationService
{
    private ?\Kreait\Firebase\Contract\Messaging $messaging = null;
    private bool $initialized = false;

    public function sendToUser(User $user, string $title, string $body, array $data = [], ?string $eventType = null): void
    {
        $tokens = DeviceToken::withoutGlobalScope('academy')
            ->where('user_id', $user->id)
            ->where('is_active', true)
            ->pluck('device_token')
            ->toArray();

        if (empty($tokens)) {
            return;
        }

        $this->sendToTokens($tokens, $title, $body, $data, $user->academy_id, $user->id, $eventType);
    }

    public function sendToParticipantGuardians(Participant $participant, string $title, string $body, array $data = [], ?string $eventType = null): void
    {
        $userIds = $this->getGuardianUserIds($participant);

        if ($userIds->isEmpty()) {
            return;
        }

        $tokens = DeviceToken::withoutGlobalScope('academy')
            ->whereIn('user_id', $userIds)
            ->where('is_active', true)
            ->pluck('device_token')
            ->toArray();

        if (empty($tokens)) {
            return;
        }

        $this->sendToTokens($tokens, $title, $body, $data, $participant->academy_id, $userIds->first(), $eventType);
    }

    public function sendToUsers(Collection $userIds, string $title, string $body, array $data = [], ?int $academyId = null, ?string $eventType = null): void
    {
        $tokens = DeviceToken::withoutGlobalScope('academy')
            ->whereIn('user_id', $userIds)
            ->where('is_active', true)
            ->pluck('device_token')
            ->toArray();

        if (empty($tokens)) {
            return;
        }

        $this->sendToTokens($tokens, $title, $body, $data, $academyId, $userIds->first(), $eventType);
    }

    public function sendToGroupParticipants(\App\Domain\Training\Models\TrainingGroup $group, string $title, string $body, array $data = [], ?string $eventType = null): void
    {
        $participantIds = $group->enrollments()
            ->where('status', 'active')
            ->pluck('participant_id');

        $participants = Participant::whereIn('id', $participantIds)->get();

        $allUserIds = collect();
        foreach ($participants as $participant) {
            $allUserIds = $allUserIds->merge($this->getGuardianUserIds($participant));
        }

        $allUserIds = $allUserIds->unique();

        if ($allUserIds->isEmpty()) {
            return;
        }

        $tokens = DeviceToken::withoutGlobalScope('academy')
            ->whereIn('user_id', $allUserIds)
            ->where('is_active', true)
            ->pluck('device_token')
            ->toArray();

        if (empty($tokens)) {
            return;
        }

        $this->sendToTokens($tokens, $title, $body, $data, $group->academy_id, null, $eventType);
    }

    public function sendBroadcast(array $tokens, string $title, string $body, array $data, ?int $academyId, ?string $eventType): array
    {
        $messaging = $this->getMessaging();

        if (!$messaging) {
            Log::info('[Push] Firebase not configured — skipping broadcast', [
                'title' => $title,
                'tokens_count' => count($tokens),
            ]);
            return ['success' => 0, 'failed' => 0];
        }

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

        $message = CloudMessage::new()
            ->withNotification($notification)
            ->withData(array_merge($data, ['click_action' => 'FLUTTER_NOTIFICATION_CLICK']));

        try {
            $report = $messaging->sendMulticast($message, $tokens);

            $successCount = $report->successes()->count();
            $failureCount = $report->failures()->count();

            if ($failureCount > 0) {
                $invalidTokens = [];
                foreach ($report->failures()->getItems() as $failure) {
                    $error = $failure->error();
                    if ($error && in_array($error->value, ['UNREGISTERED', 'INVALID_ARGUMENT'])) {
                        $invalidTokens[] = $failure->target()->value();
                    }
                }

                if (!empty($invalidTokens)) {
                    DeviceToken::withoutGlobalScope('academy')
                        ->whereIn('device_token', $invalidTokens)
                        ->update(['is_active' => false]);
                }
            }

            return ['success' => $successCount, 'failed' => $failureCount];
        } catch (\Throwable $e) {
            Log::error('[Push] Broadcast failed', ['title' => $title, 'error' => $e->getMessage()]);
            return ['success' => 0, 'failed' => count($tokens)];
        }
    }

    public function sendSilentData(array $tokens, array $data): void
    {
        $messaging = $this->getMessaging();

        if (!$messaging || empty($tokens)) {
            return;
        }

        $message = CloudMessage::new()
            ->withData(array_merge($data, ['content_available' => '1']));

        try {
            $messaging->sendMulticast($message, $tokens);
        } catch (\Throwable $e) {
            Log::warning('[Push] Silent data push failed', ['error' => $e->getMessage()]);
        }
    }

    private function sendToTokens(array $tokens, string $title, string $body, array $data, ?int $academyId, ?int $recipientUserId, ?string $eventType): void
    {
        $messaging = $this->getMessaging();

        if (!$messaging) {
            Log::info('[Push] Firebase not configured — skipping push notification', [
                'title' => $title,
                'tokens_count' => count($tokens),
            ]);
            return;
        }

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

        $message = CloudMessage::new()
            ->withNotification($notification)
            ->withData(array_merge($data, ['click_action' => 'FLUTTER_NOTIFICATION_CLICK']));

        try {
            $report = $messaging->sendMulticast($message, $tokens);

            $successCount = $report->successes()->count();
            $failureCount = $report->failures()->count();

            // Deactivate invalid tokens
            if ($failureCount > 0) {
                $invalidTokens = [];
                foreach ($report->failures()->getItems() as $failure) {
                    $error = $failure->error();
                    if ($error && in_array($error->value, ['UNREGISTERED', 'INVALID_ARGUMENT'])) {
                        $invalidTokens[] = $failure->target()->value();
                    }
                }

                if (!empty($invalidTokens)) {
                    DeviceToken::withoutGlobalScope('academy')
                        ->whereIn('device_token', $invalidTokens)
                        ->update(['is_active' => false]);
                }
            }

            // Log the notification
            if ($academyId && $recipientUserId) {
                NotificationLog::create([
                    'academy_id' => $academyId,
                    'recipient_type' => 'user',
                    'recipient_id' => $recipientUserId,
                    'event_type' => $eventType ?? 'push_notification',
                    'channel' => NotificationChannel::Push->value,
                    'subject' => $title,
                    'body' => $body,
                    'status' => $successCount > 0 ? NotificationStatus::Sent->value : NotificationStatus::Failed->value,
                    'error_message' => $failureCount > 0 ? "{$failureCount} failed out of " . count($tokens) : null,
                    'sent_at' => $successCount > 0 ? now() : null,
                    'metadata' => [
                        'tokens_sent' => count($tokens),
                        'success_count' => $successCount,
                        'failure_count' => $failureCount,
                        'data' => $data,
                    ],
                ]);
            }

            Log::info('[Push] Sent', [
                'title' => $title,
                'success' => $successCount,
                'failed' => $failureCount,
            ]);
        } catch (\Throwable $e) {
            Log::error('[Push] Failed to send', [
                'title' => $title,
                'error' => $e->getMessage(),
            ]);

            if ($academyId && $recipientUserId) {
                NotificationLog::create([
                    'academy_id' => $academyId,
                    'recipient_type' => 'user',
                    'recipient_id' => $recipientUserId,
                    'event_type' => $eventType ?? 'push_notification',
                    'channel' => NotificationChannel::Push->value,
                    'subject' => $title,
                    'body' => $body,
                    'status' => NotificationStatus::Failed->value,
                    'error_message' => $e->getMessage(),
                    'metadata' => ['data' => $data],
                ]);
            }
        }
    }

    private function getGuardianUserIds(Participant $participant): Collection
    {
        $guardians = $participant->guardians()->with('user')->get();

        return $guardians
            ->map(fn ($g) => $g->user?->id)
            ->filter()
            ->values();
    }

    private function getMessaging(): ?\Kreait\Firebase\Contract\Messaging
    {
        if ($this->initialized) {
            return $this->messaging;
        }

        $this->initialized = true;

        try {
            $credentials = $this->resolveCredentials();

            if (!$credentials) {
                return null;
            }

            $factory = (new Factory)->withServiceAccount($credentials);
            $this->messaging = $factory->createMessaging();

            return $this->messaging;
        } catch (\Throwable $e) {
            Log::warning('[Push] Failed to initialize Firebase', ['error' => $e->getMessage()]);
            return null;
        }
    }

    private function resolveCredentials(): ?array
    {
        // 1. System settings (per-academy JSON)
        $json = SystemSetting::get('firebase_service_account_json');
        if ($json) {
            $decoded = is_string($json) ? json_decode($json, true) : $json;
            if (is_array($decoded) && isset($decoded['project_id'])) {
                return $decoded;
            }
        }

        // 2. File in storage
        $filePath = storage_path('app/firebase/service-account.json');
        if (file_exists($filePath)) {
            $decoded = json_decode(file_get_contents($filePath), true);
            if (is_array($decoded) && isset($decoded['project_id'])) {
                return $decoded;
            }
        }

        // 3. Environment variable (path to file)
        $envPath = env('FIREBASE_CREDENTIALS');
        if ($envPath && file_exists($envPath)) {
            $decoded = json_decode(file_get_contents($envPath), true);
            if (is_array($decoded) && isset($decoded['project_id'])) {
                return $decoded;
            }
        }

        return null;
    }
}
