<?php

declare(strict_types=1);

namespace ServerManager\Services;

use ServerManager\Core\App;
use ServerManager\Core\Database;
use ServerManager\Core\Logger;

class FCMService
{
    private Database $db;
    private Logger $logger;
    private string $mode = 'none';
    private string $serverKey = '';
    private array $serviceAccount = [];
    private string $accessToken = '';
    private int $tokenExpiry = 0;

    public function __construct()
    {
        $this->db = Database::getInstance();
        $this->logger = Logger::getInstance();

        $keyFile = dirname(__DIR__, 2) . '/config/firebase-service-account.json';
        if (file_exists($keyFile)) {
            $account = json_decode(file_get_contents($keyFile), true);
            if ($account && !empty($account['client_email']) && !empty($account['private_key'])) {
                $this->serviceAccount = $account;
                $this->mode = 'v1';
                $this->logger->info('FCM mode: HTTP v1 (service account)');
                return;
            }
        }

        $config = App::getConfig();
        $this->serverKey = $config['fcm']['server_key'] ?? $_ENV['FCM_SERVER_KEY'] ?? '';

        if (!empty($this->serverKey)) {
            $this->mode = 'legacy';
            $this->logger->info('FCM mode: legacy HTTP API (server key)');
        } else {
            $this->logger->warning('FCM not configured — no service account or server key');
        }
    }

    public function isConfigured(): bool
    {
        return $this->mode !== 'none';
    }

    public function sendToUser(int $userId, string $title, string $body, string $type = 'info', ?int $notificationId = null): bool
    {
        if (!$this->isConfigured()) return false;

        $tokens = $this->db->fetchAll(
            'SELECT id, token FROM user_fcm_tokens WHERE user_id = ?',
            [$userId]
        );

        if (empty($tokens)) return false;

        $success = true;
        foreach ($tokens as $row) {
            $result = $this->sendToDevice($row['token'], $title, $body, $type, $notificationId);
            if ($result === false) {
                $success = false;
            } elseif ($result === 'invalid_token') {
                $this->db->delete('user_fcm_tokens', 'id = ?', [(int) $row['id']]);
                $this->logger->info('FCM: removed invalid token', ['user_id' => $userId]);
            }
        }
        return $success;
    }

    public function sendToAll(string $title, string $body, string $type = 'info', ?int $notificationId = null): int
    {
        if (!$this->isConfigured()) return 0;

        $tokens = $this->db->fetchAll('SELECT id, token FROM user_fcm_tokens');
        if (empty($tokens)) return 0;

        $sent = 0;
        foreach ($tokens as $row) {
            $result = $this->sendToDevice($row['token'], $title, $body, $type, $notificationId);
            if ($result === true) {
                $sent++;
            } elseif ($result === 'invalid_token') {
                $this->db->delete('user_fcm_tokens', 'id = ?', [(int) $row['id']]);
            }
        }
        return $sent;
    }

    private function sendToDevice(string $token, string $title, string $body, string $type, ?int $notificationId): bool|string
    {
        if ($this->mode === 'v1') {
            return $this->sendV1($token, $title, $body, $type, $notificationId);
        }
        return $this->sendLegacy($token, $title, $body, $type, $notificationId);
    }

    private function sendV1(string $token, string $title, string $body, string $type, ?int $notificationId): bool|string
    {
        $accessToken = $this->getAccessToken();
        if (!$accessToken) return false;

        $projectId = $this->serviceAccount['project_id'] ?? '';
        if (empty($projectId)) return false;

        $payload = [
            'message' => [
                'token' => $token,
                'notification' => [
                    'title' => $title,
                    'body' => $body,
                ],
                'data' => [
                    'type' => $type,
                    'notification_id' => (string) ($notificationId ?? 0),
                    'click_action' => 'OPEN_NOTIFICATIONS',
                ],
                'android' => [
                    'priority' => 'HIGH',
                    'notification' => [
                        'channel_id' => match ($type) {
                            'error', 'warning' => 'notifications_alerts',
                            default => 'notifications_general',
                        },
                        'sound' => 'default',
                    ],
                ],
            ],
        ];

        $ch = curl_init("https://fcm.googleapis.com/v1/projects/{$projectId}/messages:send");
        curl_setopt_array($ch, [
            CURLOPT_POST => true,
            CURLOPT_HTTPHEADER => [
                'Content-Type: application/json',
                'Authorization: Bearer ' . $accessToken,
            ],
            CURLOPT_POSTFIELDS => json_encode($payload),
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 15,
        ]);

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

        if ($httpCode === 200) {
            $this->logger->info('FCM v1: push sent', ['notification_id' => $notificationId]);
            return true;
        }

        if ($httpCode === 404 || $httpCode === 400) {
            $respData = json_decode($response, true);
            $errorMsg = $respData['error']['message'] ?? $response;
            if (str_contains($errorMsg, 'registration-token-not-registered') ||
                str_contains($errorMsg, 'NOT_FOUND') ||
                str_contains($errorMsg, 'INVALID_ARGUMENT')) {
                $this->logger->warning('FCM v1: invalid token, will remove', ['error' => substr($errorMsg, 0, 200)]);
                return 'invalid_token';
            }
        }

        $this->logger->error('FCM v1: send failed', [
            'http_code' => $httpCode,
            'response' => substr($response, 0, 500),
        ]);
        return false;
    }

    private function sendLegacy(string $token, string $title, string $body, string $type, ?int $notificationId): bool|string
    {
        $payload = [
            'to' => $token,
            'priority' => 'high',
            'notification' => [
                'title' => $title,
                'body' => $body,
                'sound' => 'default',
            ],
            'data' => [
                'type' => $type,
                'notification_id' => (string) ($notificationId ?? 0),
                'click_action' => 'OPEN_NOTIFICATIONS',
            ],
            'android' => [
                'priority' => 'high',
                'notification' => [
                    'channel_id' => match ($type) {
                        'error', 'warning' => 'notifications_alerts',
                        default => 'notifications_general',
                    },
                    'priority' => match ($type) {
                        'error' => 'high',
                        default => 'default',
                    },
                    'sound' => 'default',
                ],
            ],
        ];

        $ch = curl_init('https://fcm.googleapis.com/fcm/send');
        curl_setopt_array($ch, [
            CURLOPT_POST => true,
            CURLOPT_HTTPHEADER => [
                'Content-Type: application/json',
                'Authorization: key=' . $this->serverKey,
            ],
            CURLOPT_POSTFIELDS => json_encode($payload),
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 15,
        ]);

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

        if ($httpCode === 200) {
            $this->logger->info('FCM legacy: push sent', ['notification_id' => $notificationId]);
            return true;
        }

        $respData = json_decode($response, true);
        $fcmError = $respData['results'][0]['error'] ?? '';

        if ($fcmError === 'NotRegistered' || $fcmError === 'InvalidRegistration') {
            $this->logger->warning('FCM legacy: invalid token, will remove', ['error' => $fcmError]);
            return 'invalid_token';
        }

        $this->logger->error('FCM legacy: send failed', [
            'http_code' => $httpCode,
            'fcm_error' => $fcmError,
            'response' => substr($response, 0, 500),
        ]);
        return false;
    }

    private function getAccessToken(): string
    {
        if ($this->accessToken && time() < $this->tokenExpiry) {
            return $this->accessToken;
        }

        $jwt = $this->createJWT();
        if (!$jwt) return '';

        $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 => 15,
        ]);
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode !== 200) {
            $this->logger->error('FCM OAuth token request failed', ['http_code' => $httpCode, 'response' => substr($response, 0, 500)]);
            return '';
        }

        $data = json_decode($response, true);
        $this->accessToken = $data['access_token'] ?? '';
        $this->tokenExpiry = time() + (int) ($data['expires_in'] ?? 3600) - 60;
        return $this->accessToken;
    }

    private function createJWT(): string
    {
        if (empty($this->serviceAccount['client_email']) || empty($this->serviceAccount['private_key'])) {
            return '';
        }

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

        $signature = '';
        openssl_sign("$header.$payload", $signature, $this->serviceAccount['private_key'], 'sha256WithRSAEncryption');

        return "$header.$payload." . self::base64UrlEncode($signature);
    }

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