<?php
/**
 * BONJUTERİ — Firebase Firestore Güvenli İstemcisi
 * 
 * Bu sınıf cPanel PHP ortamında ek Composer kütüphanesine ihtiyaç duymadan:
 * 1. Firebase ID Token'larını Google'ın resmi genel RSA sertifikalarıyla
 *    imza, süre (exp), başlangıç (iat), veren (iss) ve hedef kitle (aud) düzeyinde doğrular.
 * 2. Google Cloud REST API üzerinden Service Account JWT (OAuth 2.0) üreterek
 *    Firestore koleksiyonlarını okur ve günceller.
 */

declare(strict_types=1);

class FirebaseClient {
    private string $projectId;
    private ?string $serviceAccountPath;
    private ?string $cachedAccessToken = null;
    private int $tokenExpiresAt = 0;

    public function __construct() {
        $this->projectId = (string)(getEnvValue('FIREBASE_PROJECT_ID', 'bonjuter') ?: 'bonjuter');
        $this->serviceAccountPath = getEnvValue('FIREBASE_SERVICE_ACCOUNT_JSON');
    }

    /**
     * Firebase ID Token'ını kapsamlı şekilde doğrular:
     * - JWT yapısı (header.payload.signature)
     * - Süre geçerliliği: exp > time()
     * - Verilme zamanı: iat <= time() + 300
     * - Hedef kitle (audience): aud === $projectId
     * - Veren makam (issuer): iss === https://securetoken.google.com/{$projectId}
     * - Google RSA 256 kriptografik imza doğrulaması
     */
    public function verifyIdToken(string $idToken): ?array {
        if (empty($idToken)) {
            return null;
        }

        $parts = explode('.', $idToken);
        if (count($parts) !== 3) {
            logEvent('WARNING', 'Geçersiz JWT token yapısı (3 parça bekleniyor).');
            return null;
        }

        [$rawHeader, $rawPayload, $rawSignature] = $parts;

        $header = json_decode((string)$this->base64UrlDecode($rawHeader), true);
        $payload = json_decode((string)$this->base64UrlDecode($rawPayload), true);

        if (!is_array($header) || !is_array($payload)) {
            logEvent('WARNING', 'JWT header veya payload JSON olarak çözülemedi.');
            return null;
        }

        $now = time();

        // 1. Süre Kontrolü (Expiration check)
        $exp = (int)($payload['exp'] ?? 0);
        if ($exp <= $now) {
            logEvent('WARNING', 'Firebase ID token süresi dolmuş.', ['exp' => $exp, 'now' => $now]);
            return null;
        }

        // 2. Başlangıç Zamanı Kontrolü (Issued at check, 5 dk saat farkı toleransı)
        $iat = (int)($payload['iat'] ?? 0);
        if ($iat > ($now + 300)) {
            logEvent('WARNING', 'Firebase ID token başlangıç tarihi gelecekte.', ['iat' => $iat, 'now' => $now]);
            return null;
        }

        // 3. Hedef Kitle Kontrolü (Audience check)
        $aud = (string)($payload['aud'] ?? '');
        if ($aud !== $this->projectId) {
            logEvent('WARNING', 'Firebase ID token audience eşleşmiyor.', ['aud' => $aud, 'expected' => $this->projectId]);
            return null;
        }

        // 4. Veren Makam Kontrolü (Issuer check)
        $iss = (string)($payload['iss'] ?? '');
        $expectedIss = 'https://securetoken.google.com/' . $this->projectId;
        if ($iss !== $expectedIss) {
            logEvent('WARNING', 'Firebase ID token issuer eşleşmiyor.', ['iss' => $iss, 'expected' => $expectedIss]);
            return null;
        }

        // 5. Kullanıcı Kimliği Kontrolü (Subject check)
        $sub = (string)($payload['sub'] ?? $payload['user_id'] ?? '');
        if (empty($sub)) {
            logEvent('WARNING', 'Firebase ID token subject (sub) alanı boş.');
            return null;
        }

        // 6. Kriptografik İmza Doğrulaması (Google Public Certificates)
        $kid = (string)($header['kid'] ?? '');
        $alg = (string)($header['alg'] ?? '');

        if ($alg !== 'RS256' || empty($kid)) {
            logEvent('WARNING', 'Desteklenmeyen imza algoritması veya kid eksik.', ['alg' => $alg, 'kid' => $kid]);
            return null;
        }

        $signatureVerified = $this->verifySignatureWithGooglePublicKeys(
            $rawHeader . '.' . $rawPayload,
            $this->base64UrlDecode($rawSignature),
            $kid
        );

        if ($signatureVerified) {
            return [
                'uid' => $sub,
                'email' => (string)($payload['email'] ?? '')
            ];
        }

        // 7. Yedek Doğrulama: Google OAuth2 / Identity Toolkit API
        return $this->verifyWithGoogleApiFallback($idToken, $sub);
    }

    /**
     * Google'ın resmi genel x509 sertifikaları ile RSA-SHA256 imzasını teyit eder.
     */
    private function verifySignatureWithGooglePublicKeys(string $data, string $signature, string $kid): bool {
        $certs = $this->getGooglePublicCertificates();
        if (!isset($certs[$kid])) {
            logEvent('WARNING', "Google genel sertifikalarında eşleşen kid bulunamadı: {$kid}");
            return false;
        }

        $certPem = $certs[$kid];
        $pubKey = openssl_pkey_get_public($certPem);
        if (!$pubKey) {
            logEvent('ERROR', "Google x509 sertifikasından açık anahtar yüklenemedi: {$kid}");
            return false;
        }

        $verifyResult = openssl_verify($data, $signature, $pubKey, OPENSSL_ALGO_SHA256);
        return ($verifyResult === 1);
    }

    /**
     * Google Firebase Public Keys (Önbellekli okur)
     */
    private function getGooglePublicCertificates(): array {
        $cacheFile = sys_get_temp_dir() . '/firebase_public_certs.json';
        if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 3600)) {
            $cached = json_decode((string)file_get_contents($cacheFile), true);
            if (is_array($cached)) {
                return $cached;
            }
        }

        $url = 'https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com';
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 6,
            CURLOPT_SSL_VERIFYPEER => true
        ]);
        $response = curl_exec($ch);
        curl_close($ch);

        $decoded = json_decode((string)$response, true);
        if (is_array($decoded) && !empty($decoded)) {
            @file_put_contents($cacheFile, json_encode($decoded), LOCK_EX);
            return $decoded;
        }

        return [];
    }

    private function verifyWithGoogleApiFallback(string $idToken, string $expectedUid): ?array {
        $url = 'https://oauth2.googleapis.com/tokeninfo?id_token=' . urlencode($idToken);
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 6,
            CURLOPT_SSL_VERIFYPEER => true
        ]);
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode === 200) {
            $payload = json_decode((string)$response, true);
            if (is_array($payload)) {
                $uid = (string)($payload['user_id'] ?? $payload['sub'] ?? '');
                if ($uid === $expectedUid) {
                    return [
                        'uid' => $uid,
                        'email' => (string)($payload['email'] ?? '')
                    ];
                }
            }
        }
        return null;
    }

    /**
     * Firebase Service Account JSON kullanarak Google OAuth 2.0 Access Token üretir.
     */
    private function getAccessToken(): ?string {
        if ($this->cachedAccessToken && time() < ($this->tokenExpiresAt - 60)) {
            return $this->cachedAccessToken;
        }

        if (!$this->serviceAccountPath || !file_exists($this->serviceAccountPath)) {
            logEvent('WARNING', 'Firebase serviceAccountKey.json bulunamadı. Yapılandırma eksik.');
            return null;
        }

        $realPath = realpath($this->serviceAccountPath);
        $publicDir = realpath(BASE_DIR . '/public');
        if ($realPath && $publicDir && str_starts_with($realPath, $publicDir)) {
            logEvent('EMERGENCY', 'KRİTİK GÜVENLİK UYARISI: serviceAccountKey.json public_html içinde tespit edildi! Erişim engellendi.');
            return null;
        }

        $serviceAccount = json_decode((string)file_get_contents($this->serviceAccountPath), true);
        if (!$serviceAccount || !isset($serviceAccount['private_key'], $serviceAccount['client_email'])) {
            logEvent('ERROR', 'Geçersiz serviceAccountKey.json dosyası.');
            return null;
        }

        $now = time();
        $header = ['alg' => 'RS256', 'typ' => 'JWT'];
        $claims = [
            'iss' => $serviceAccount['client_email'],
            'scope' => 'https://www.googleapis.com/auth/datastore',
            'aud' => 'https://oauth2.googleapis.com/token',
            'exp' => $now + 3600,
            'iat' => $now
        ];

        $base64Header = $this->base64UrlEncode((string)json_encode($header));
        $base64Claims = $this->base64UrlEncode((string)json_encode($claims));
        $signaturePayload = $base64Header . '.' . $base64Claims;

        $privateKey = openssl_pkey_get_private($serviceAccount['private_key']);
        if (!$privateKey) {
            logEvent('ERROR', 'Özel anahtar yüklenemedi.');
            return null;
        }

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

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

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

        if ($httpCode !== 200) {
            logEvent('ERROR', 'Google OAuth token alınamadı', ['httpCode' => $httpCode, 'response' => $response]);
            return null;
        }

        $tokenData = json_decode((string)$response, true);
        $this->cachedAccessToken = $tokenData['access_token'] ?? null;
        $this->tokenExpiresAt = $now + (int)($tokenData['expires_in'] ?? 3600);

        return $this->cachedAccessToken;
    }

    /**
     * Firestore'dan sipariş belgesini çeker.
     * Koleksiyon: orders/{orderId}
     */
    public function getOrder(string $orderId): ?array {
        $token = $this->getAccessToken();
        if (!$token) {
            return null;
        }

        $url = "https://firestore.googleapis.com/v1/projects/{$this->projectId}/databases/(default)/documents/orders/" . urlencode($orderId);
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_HTTPHEADER => [
                'Authorization: Bearer ' . $token,
                'Content-Type: application/json'
            ],
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 10
        ]);

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

        if ($httpCode !== 200) {
            logEvent('WARNING', "Sipariş Firestore'da bulunamadı: {$orderId}", ['httpCode' => $httpCode]);
            return null;
        }

        $doc = json_decode((string)$response, true);
        return $this->parseFirestoreDocument($doc);
    }

    /**
     * Eşzamanlı ödeme oturumunu kaydeder (Çifte form engelleme kilidi).
     */
    public function recordPaymentSessionLock(string $orderId): bool {
        $token = $this->getAccessToken();
        if (!$token) {
            return false;
        }

        $url = "https://firestore.googleapis.com/v1/projects/{$this->projectId}/databases/(default)/documents/orders/" . urlencode($orderId) .
            "?updateMask.fieldPaths=paymentSessionStartedAt";

        $fields = [
            'paymentSessionStartedAt' => ['timestampValue' => gmdate('Y-m-d\TH:i:s\Z')]
        ];

        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_CUSTOMREQUEST => 'PATCH',
            CURLOPT_POSTFIELDS => json_encode(['fields' => $fields]),
            CURLOPT_HTTPHEADER => [
                'Authorization: Bearer ' . $token,
                'Content-Type: application/json'
            ],
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 8
        ]);

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

        return ($httpCode >= 200 && $httpCode < 300);
    }

    /**
     * Firestore'daki siparişin ödeme durumunu günceller.
     * Alanlar: paymentStatus, paymentMethod, updatedAt, adminNotes
     */
    public function updateOrderPaymentStatus(string $orderId, string $newPaymentStatus, array $additionalData = []): bool {
        $token = $this->getAccessToken();
        if (!$token) {
            return false;
        }

        $url = "https://firestore.googleapis.com/v1/projects/{$this->projectId}/databases/(default)/documents/orders/" . urlencode($orderId) .
            "?updateMask.fieldPaths=paymentStatus&updateMask.fieldPaths=updatedAt&updateMask.fieldPaths=adminNotes&updateMask.fieldPaths=paymentMethod";
        
        $fields = [
            'paymentStatus' => ['stringValue' => $newPaymentStatus],
            'paymentMethod' => ['stringValue' => 'iyzico Checkout Form'],
            'updatedAt' => ['timestampValue' => gmdate('Y-m-d\TH:i:s\Z')],
            'adminNotes' => ['stringValue' => $additionalData['adminNotes'] ?? 'iyzico ödeme durumu güncellendi.']
        ];

        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_CUSTOMREQUEST => 'PATCH',
            CURLOPT_POSTFIELDS => json_encode(['fields' => $fields]),
            CURLOPT_HTTPHEADER => [
                'Authorization: Bearer ' . $token,
                'Content-Type: application/json'
            ],
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 10
        ]);

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

        $success = ($httpCode >= 200 && $httpCode < 300);
        logEvent($success ? 'INFO' : 'ERROR', "Sipariş ödeme durumu güncellendi: {$orderId} -> {$newPaymentStatus}", [
            'httpCode' => $httpCode
        ]);

        return $success;
    }

    private function parseFirestoreDocument(array $doc): array {
        $result = [];
        if (!isset($doc['fields'])) return $result;

        foreach ($doc['fields'] as $key => $val) {
            $type = array_key_first($val);
            $result[$key] = match ($type) {
                'stringValue' => $val['stringValue'],
                'integerValue' => (int)$val['integerValue'],
                'doubleValue' => (float)$val['doubleValue'],
                'booleanValue' => (bool)$val['booleanValue'],
                'timestampValue' => $val['timestampValue'],
                'arrayValue' => array_map(function($item) {
                    if (isset($item['mapValue']['fields'])) {
                        return $this->parseFirestoreDocument($item['mapValue']);
                    }
                    return $item;
                }, $val['arrayValue']['values'] ?? []),
                'mapValue' => $this->parseFirestoreDocument($val['mapValue']),
                default => null
            };
        }
        return $result;
    }

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

    private function base64UrlDecode(string $data): string {
        $remainder = strlen($data) % 4;
        if ($remainder) {
            $padlen = 4 - $remainder;
            $data .= str_repeat('=', $padlen);
        }
        return (string)base64_decode(strtr($data, '-_', '+/'));
    }
}
