<?php
/**
 * Firebase Cloud Messaging V1 API Helper
 * Works with the new FCM V1 API (recommended by Google)
 * 
 * Usage:
 *   $fcm = new FCMHelper('/path/to/firebase-service-account.json');
 *   $fcm->sendToDevice($fcmToken, ['action' => 'enable_cf']);
 */

class FCMHelper {
    
    private $serviceAccountPath;
    private $projectId;
    private $accessToken;
    private $tokenExpiry;
    
    /**
     * Constructor
     * @param string $serviceAccountPath Path to your service account JSON file
     */
    public function __construct($serviceAccountPath) {
        if (!file_exists($serviceAccountPath)) {
            throw new Exception("Service account file not found: $serviceAccountPath");
        }
        
        $this->serviceAccountPath = $serviceAccountPath;
        
        // Read project ID from service account file
        $serviceAccount = json_decode(file_get_contents($serviceAccountPath), true);
        if (!$serviceAccount || !isset($serviceAccount['project_id'])) {
            throw new Exception("Invalid service account file");
        }
        $this->projectId = $serviceAccount['project_id'];
    }
    
    /**
     * Get OAuth2 Access Token
     */
    private function getAccessToken() {
        // Return cached token if still valid
        if ($this->accessToken && $this->tokenExpiry > time()) {
            return $this->accessToken;
        }
        
        $serviceAccount = json_decode(file_get_contents($this->serviceAccountPath), true);
        
        // Create JWT
        $header = json_encode(['typ' => 'JWT', 'alg' => 'RS256']);
        $now = time();
        $payload = json_encode([
            'iss' => $serviceAccount['client_email'],
            'scope' => 'https://www.googleapis.com/auth/firebase.messaging',
            'aud' => 'https://oauth2.googleapis.com/token',
            'iat' => $now,
            'exp' => $now + 3600
        ]);
        
        $base64Header = $this->base64UrlEncode($header);
        $base64Payload = $this->base64UrlEncode($payload);
        
        $signatureInput = $base64Header . '.' . $base64Payload;
        
        openssl_sign($signatureInput, $signature, $serviceAccount['private_key'], 'SHA256');
        $base64Signature = $this->base64UrlEncode($signature);
        
        $jwt = $signatureInput . '.' . $base64Signature;
        
        // Exchange JWT for access token
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, 'https://oauth2.googleapis.com/token');
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
            'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
            'assertion' => $jwt
        ]));
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/x-www-form-urlencoded']);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
        
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        
        $data = json_decode($response, true);
        
        if (isset($data['access_token'])) {
            $this->accessToken = $data['access_token'];
            $this->tokenExpiry = time() + $data['expires_in'] - 60; // Refresh 1 min early
            return $this->accessToken;
        }
        
        throw new Exception('Failed to get access token: ' . $response);
    }
    
    /**
     * Base64 URL Encode
     */
    private function base64UrlEncode($data) {
        return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
    }
    
    /**
     * Send FCM Message to a single device
     * 
     * @param string $fcmToken Device FCM token
     * @param array $data Data payload
     * @param string $title Optional notification title
     * @param string $body Optional notification body
     * @return array Response from FCM
     */
    public function sendToDevice($fcmToken, $data = [], $title = null, $body = null) {
        $message = [
            'token' => $fcmToken,
            'data' => $this->stringifyData($data)
        ];
        
        // Add notification if title provided (shows in notification bar)
        if ($title) {
            $message['notification'] = [
                'title' => $title,
                'body' => $body ?? ''
            ];
        }
        
        // High priority for immediate delivery
        $message['android'] = [
            'priority' => 'high'
        ];
        
        return $this->sendMessage($message);
    }
    
    /**
     * Send data-only message (silent push)
     * This wakes up your app WITHOUT showing notification
     * Perfect for triggers!
     */
    public function sendDataMessage($fcmToken, $data) {
        $message = [
            'token' => $fcmToken,
            'data' => $this->stringifyData($data),
            'android' => [
                'priority' => 'high'
            ]
        ];
        
        return $this->sendMessage($message);
    }
    
    /**
     * Send to multiple devices
     * 
     * @param array $fcmTokens Array of FCM tokens
     * @param array $data Data payload
     * @return array Results for each token
     */
    public function sendToMultipleDevices($fcmTokens, $data) {
        $results = [];
        foreach ($fcmTokens as $token) {
            $results[$token] = $this->sendDataMessage($token, $data);
        }
        return $results;
    }
    
    /**
     * Convert all data values to strings (FCM requirement)
     */
    private function stringifyData($data) {
        $stringData = [];
        foreach ($data as $key => $value) {
            $stringData[$key] = is_string($value) ? $value : json_encode($value);
        }
        return $stringData;
    }
    
    /**
     * Send message to FCM
     */
    private function sendMessage($message) {
        try {
            $accessToken = $this->getAccessToken();
            
            $url = "https://fcm.googleapis.com/v1/projects/{$this->projectId}/messages:send";
            
            $payload = json_encode(['message' => $message]);
            
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
            curl_setopt($ch, CURLOPT_HTTPHEADER, [
                'Authorization: Bearer ' . $accessToken,
                'Content-Type: application/json'
            ]);
            
            $response = curl_exec($ch);
            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            $curlError = curl_error($ch);
            curl_close($ch);
            
            if ($curlError) {
                return [
                    'success' => false,
                    'http_code' => $httpCode,
                    'error' => $curlError
                ];
            }
            
            return [
                'success' => $httpCode === 200,
                'http_code' => $httpCode,
                'response' => json_decode($response, true)
            ];
        } catch (Exception $e) {
            return [
                'success' => false,
                'error' => $e->getMessage()
            ];
        }
    }
    
    /**
     * Get project ID
     */
    public function getProjectId() {
        return $this->projectId;
    }
}
