<?php
/**
 * Firebase Notification Service (FCM HTTP v1)
 */

declare(strict_types=1);

namespace App\Services;

use App\Core\Database;
use App\Core\Security;

class NotificationService
{
    private string $projectId;
    private string $serviceAccountPath;

    public function __construct()
    {
        $this->serviceAccountPath = env('FIREBASE_SERVICE_ACCOUNT_PATH', APP_PATH . '/config/firebase-service-account.json');
        
        // Auto-detect Project ID from Service Account JSON to prevent RESOURCE_PROJECT_INVALID
        if (file_exists($this->serviceAccountPath)) {
            $sa = json_decode(file_get_contents($this->serviceAccountPath), true);
            $this->projectId = $sa['project_id'] ?? env('FIREBASE_PROJECT_ID', '');
        } else {
            $this->projectId = env('FIREBASE_PROJECT_ID', '');
        }
    }

    /**
     * Send a push notification to a specific user or device
     */
    public function sendNotification(string $userId, string $title, string $body, array $data = [], ?string $deviceId = null): bool
    {
        $db = Database::getInstance();
        
        // 1. Get push tokens for the user
        if ($deviceId) {
            $stmt = $db->prepare("SELECT push_token FROM user_devices WHERE user_id = ? AND device_fingerprint = ? AND push_token IS NOT NULL");
            $stmt->execute([$userId, $deviceId]);
        } else {
            $stmt = $db->prepare("SELECT push_token FROM user_devices WHERE user_id = ? AND push_token IS NOT NULL");
            $stmt->execute([$userId]);
        }

        $tokens = $stmt->fetchAll(\PDO::FETCH_COLUMN);

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

        // 2. Save notification to database (Single direct insert)
        $stmt = $db->prepare("SELECT tenant_id FROM users WHERE id = ? LIMIT 1");
        $stmt->execute([$userId]);
        $tenantId = $stmt->fetchColumn();

        if ($tenantId) {
            $stmt = $db->prepare("INSERT INTO notifications (id, tenant_id, user_id, type, title, body, data, created_at) VALUES (UUID(), ?, ?, 'system', ?, ?, ?, NOW())");
            $stmt->execute([$tenantId, $userId, $title, $body, json_encode($data)]);
        }

        // 3. Send to each token
        $successCount = 0;
        foreach ($tokens as $token) {
            if ($this->dispatchToFcm($token, $title, $body, $data)) {
                $successCount++;
            }
        }

        return $successCount > 0;
    }

    /**
     * Send a data-only (silent) notification to update background state (e.g., progress)
     */
    public function sendDataNotification(string $userId, array $data, ?string $deviceId = null): bool
    {
        $db = Database::getInstance();
        if ($deviceId) {
            $stmt = $db->prepare("SELECT push_token FROM user_devices WHERE user_id = ? AND device_fingerprint = ? AND push_token IS NOT NULL");
            $stmt->execute([$userId, $deviceId]);
        } else {
            $stmt = $db->prepare("SELECT push_token FROM user_devices WHERE user_id = ? AND push_token IS NOT NULL");
            $stmt->execute([$userId]);
        }

        $tokens = $stmt->fetchAll(\PDO::FETCH_COLUMN);
        if (empty($tokens)) return false;

        $successCount = 0;
        foreach ($tokens as $token) {
            if ($this->dispatchToFcm($token, null, null, $data)) {
                $successCount++;
            }
        }

        return $successCount > 0;
    }

    /**
     * Dispatch notification to Firebase via HTTP v1 API
     */
    private function dispatchToFcm(string $token, ?string $title, ?string $body, array $data): bool
    {
        if (!file_exists($this->serviceAccountPath)) {
            error_log("[NotificationService] Firebase service account file missing: {$this->serviceAccountPath}");
            return false;
        }

        $accessToken = $this->getAccessToken();
        if (!$accessToken) return false;

        $url = "https://fcm.googleapis.com/v1/projects/{$this->projectId}/messages:send";

        $message = [
            'token' => $token,
            'data' => array_map('strval', $data),
        ];

        if ($title || $body) {
            $message['notification'] = [
                'title' => $title,
                'body' => $body,
            ];
            $message['android'] = [
                'priority' => 'high',
                'notification' => [
                    'sound' => 'default',
                    'channel_id' => 'high_importance_channel'
                ]
            ];
            $message['apns'] = [
                'payload' => [
                    'aps' => [
                        'sound' => 'default',
                    ],
                ],
            ];
        } else {
            // Silent push / Live Activity Update
            $message['android'] = [
                'priority' => 'high'
            ];
            $message['apns'] = [
                'headers' => [
                    'apns-priority' => '5',
                    'apns-push-type' => 'background'
                ],
                'payload' => [
                    'aps' => [
                        'content-available' => 1
                    ]
                ]
            ];
            
            // If the data contains live activity update markers, adjust headers for iOS ActivityKit
            if (isset($data['type']) && $data['type'] === 'batch_progress') {
                $message['apns']['headers']['apns-push-type'] = 'liveactivity';
                $message['apns']['headers']['apns-priority'] = '10';
                $message['apns']['payload']['aps']['content-state'] = $data;
                $message['apns']['payload']['aps']['timestamp'] = time();
                $message['apns']['payload']['aps']['event'] = 'update';
            }
        }

        $payload = ['message' => $message];


        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Authorization: Bearer ' . $accessToken,
            'Content-Type: application/json',
        ]);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode !== 200) {
            error_log("[NotificationService] FCM Send Error ($httpCode): " . $response);
            return false;
        }

        return true;
    }

    /**
     * Get OAuth2 Access Token for Firebase using Service Account JWT
     * Self-contained: no external libraries needed.
     */
    private function getAccessToken(): ?string
    {
        // Check cache first (token is valid for 1 hour, we cache for 50 min)
        $cacheFile = STORAGE_PATH . '/cache/fcm_token.json';
        if (file_exists($cacheFile)) {
            $cached = json_decode(file_get_contents($cacheFile), true);
            if ($cached && ($cached['expires_at'] ?? 0) > time()) {
                return $cached['access_token'];
            }
        }

        if (!file_exists($this->serviceAccountPath)) {
            error_log("[NotificationService] Firebase service account file missing");
            return null;
        }

        $sa = json_decode(file_get_contents($this->serviceAccountPath), true);
        if (!$sa || empty($sa['private_key']) || empty($sa['client_email'])) {
            error_log("[NotificationService] Invalid service account JSON");
            return null;
        }

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

        $b64Header  = rtrim(strtr(base64_encode($header), '+/', '-_'), '=');
        $b64Payload = rtrim(strtr(base64_encode($payload), '+/', '-_'), '=');
        $signingInput = $b64Header . '.' . $b64Payload;

        $privateKey = openssl_pkey_get_private($sa['private_key']);
        if (!$privateKey) {
            error_log("[NotificationService] Failed to parse private key");
            return null;
        }

        openssl_sign($signingInput, $signature, $privateKey, OPENSSL_ALGO_SHA256);
        $b64Signature = rtrim(strtr(base64_encode($signature), '+/', '-_'), '=');
        $jwt = $signingInput . '.' . $b64Signature;

        // Exchange JWT for access token
        $ch = curl_init('https://oauth2.googleapis.com/token');
        curl_setopt_array($ch, [
            CURLOPT_POST           => true,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POSTFIELDS     => http_build_query([
                'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
                'assertion'  => $jwt,
            ]),
        ]);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode !== 200) {
            error_log("[NotificationService] Token exchange failed ($httpCode): $response");
            return null;
        }

        $tokenData = json_decode($response, true);
        $accessToken = $tokenData['access_token'] ?? null;

        if ($accessToken) {
            // Cache for 50 minutes
            @file_put_contents($cacheFile, json_encode([
                'access_token' => $accessToken,
                'expires_at'   => $now + 3000,
            ]));
        }

        return $accessToken;
    }
}
