<?php
declare(strict_types=1);

require_once __DIR__ . '/database.php';

function push_firebase_service_account_path(): string
{
    $env = getenv('BULO_FIREBASE_SERVICE_ACCOUNT');
    if (is_string($env) && trim($env) !== '') {
        return trim($env);
    }

    return __DIR__ . '/firebase-service-account.json';
}

function push_firebase_configurado(): bool
{
    return is_file(push_firebase_service_account_path());
}

function push_enviar_aviso_para_destino(string $destino, string $titulo, string $mensagem, int $idAviso): array
{
    $tokens = push_tokens_para_destino($destino);
    $resultado = [
        'configurado' => push_firebase_configurado(),
        'tokens' => count($tokens),
        'enviados' => 0,
        'falhas' => 0,
        'mensagem' => '',
    ];

    if (!$resultado['configurado']) {
        $resultado['mensagem'] = 'Firebase ainda nao configurado.';
        return $resultado;
    }

    if (!$tokens) {
        $resultado['mensagem'] = 'Nenhum aparelho cadastrado para receber push.';
        return $resultado;
    }

    $serviceAccount = push_firebase_service_account();
    $accessToken = push_firebase_access_token($serviceAccount);
    $projectId = (string) $serviceAccount['project_id'];
    $url = 'https://fcm.googleapis.com/v1/projects/' . rawurlencode($projectId) . '/messages:send';

    foreach ($tokens as $token) {
        $payload = [
            'message' => [
                'token' => $token,
                'notification' => [
                    'title' => $titulo,
                    'body' => $mensagem,
                ],
                'data' => [
                    'tipo' => 'aviso',
                    'id_aviso' => (string) $idAviso,
                    'destino' => $destino,
                ],
                'android' => [
                    'priority' => 'HIGH',
                    'notification' => [
                        'channel_id' => 'avisos',
                        'icon' => 'ic_stat_bulo_motorista',
                        'color' => '#0A66FF',
                        'sound' => 'default',
                        'default_sound' => true,
                        'notification_priority' => 'PRIORITY_HIGH',
                    ],
                ],
            ],
        ];

        $response = push_http_json($url, $payload, [
            'Authorization: Bearer ' . $accessToken,
        ]);

        if ($response['ok']) {
            $resultado['enviados']++;
            continue;
        }

        $resultado['falhas']++;
        $codigoErro = push_firebase_error_code($response['json']);
        if (in_array($codigoErro, ['UNREGISTERED', 'INVALID_ARGUMENT'], true)) {
            push_token_inativar($token);
        }
    }

    $resultado['mensagem'] = 'Push processado.';
    return $resultado;
}

function push_enviar_chamada_corrida_motorista(
    int $idMotorista,
    int $idCorrida,
    ?int $idOferta = null,
    array $corrida = []
): array {
    $tokens = push_tokens_para_usuario($idMotorista, 'motorista', 'bulo_motorista');
    $resultado = [
        'configurado' => push_firebase_configurado(),
        'tokens' => count($tokens),
        'enviados' => 0,
        'falhas' => 0,
        'mensagem' => '',
    ];

    if (!$resultado['configurado']) {
        $resultado['mensagem'] = 'Firebase ainda nao configurado.';
        return $resultado;
    }

    if (!$tokens) {
        $resultado['mensagem'] = 'Motorista sem aparelho cadastrado para receber push.';
        return $resultado;
    }

    $serviceAccount = push_firebase_service_account();
    $accessToken = push_firebase_access_token($serviceAccount);
    $projectId = (string) $serviceAccount['project_id'];
    $url = 'https://fcm.googleapis.com/v1/projects/' . rawurlencode($projectId) . '/messages:send';

    $valorMotorista = '';
    if (isset($corrida['valor_motorista']) && trim((string) $corrida['valor_motorista']) !== '') {
        $valorMotorista = 'R$ ' . number_format((float) $corrida['valor_motorista'], 2, ',', '.');
    }

    $distanciaEmbarque = '';
    if (isset($corrida['distancia_ate_origem_km']) && trim((string) $corrida['distancia_ate_origem_km']) !== '') {
        $distanciaEmbarque = number_format((float) $corrida['distancia_ate_origem_km'], 1, ',', '.') . ' km';
    }

    $distanciaDestino = '';
    if (isset($corrida['distancia_corrida_km']) && trim((string) $corrida['distancia_corrida_km']) !== '') {
        $distanciaDestino = number_format((float) $corrida['distancia_corrida_km'], 1, ',', '.') . ' km';
    }

    $ganhoPorKm = '';
    if (isset($corrida['ganho_por_km']) && trim((string) $corrida['ganho_por_km']) !== '') {
        $ganhoPorKm = 'R$ ' . number_format((float) $corrida['ganho_por_km'], 2, ',', '.');
    }

    $multiplicadorDinamico = 1.0;
    if (isset($corrida['multiplicador_dinamico']) && trim((string) $corrida['multiplicador_dinamico']) !== '') {
        $multiplicadorDinamico = max(1.0, (float) str_replace(',', '.', (string) $corrida['multiplicador_dinamico']));
    }

    $data = [
        'tipo' => 'corrida_oferta',
        'id_corrida' => (string) $idCorrida,
        'id_oferta' => (string) ($idOferta ?? ''),
        'id_motorista' => (string) $idMotorista,
        'passageiro' => 'Passageiro',
        'origem' => (string) ($corrida['origem_endereco'] ?? 'Embarque informado'),
        'destino' => (string) ($corrida['destino_endereco'] ?? 'Destino informado'),
        'valor_motorista' => $valorMotorista,
        'distancia_embarque' => $distanciaEmbarque,
        'distancia_destino' => $distanciaDestino,
        'ganho_por_km' => $ganhoPorKm,
        'multiplicador_dinamico' => number_format($multiplicadorDinamico, 2, '.', ''),
        'dinamica' => $multiplicadorDinamico > 1.0
            ? 'Dinamica ' . number_format($multiplicadorDinamico, 1, '.', '') . 'x'
            : '',
        'passageiro_nota' => number_format((float) ($corrida['passageiro_nota'] ?? 5), 2, '.', ''),
        'passageiro_corridas' => (string) (int) ($corrida['passageiro_total_corridas'] ?? 0),
        'segundos' => '35',
        'click_action' => 'FLUTTER_NOTIFICATION_CLICK',
    ];

    foreach ($tokens as $token) {
        $payload = [
            'message' => [
                'token' => $token,
                'data' => $data,
                'android' => [
                    'priority' => 'HIGH',
                    'ttl' => '25s',
                ],
            ],
        ];

        $response = push_http_json($url, $payload, [
            'Authorization: Bearer ' . $accessToken,
        ]);

        if ($response['ok']) {
            $resultado['enviados']++;
            continue;
        }

        $resultado['falhas']++;
        $codigoErro = push_firebase_error_code($response['json']);
        if (in_array($codigoErro, ['UNREGISTERED', 'INVALID_ARGUMENT'], true)) {
            push_token_inativar($token);
        }
    }

    $resultado['mensagem'] = 'Push de corrida processado.';
    return $resultado;
}

function push_enviar_cancelamento_corrida_motorista(int $idMotorista, int $idCorrida): array
{
    $tokens = push_tokens_para_usuario($idMotorista, 'motorista', 'bulo_motorista');
    $resultado = [
        'configurado' => push_firebase_configurado(),
        'tokens' => count($tokens),
        'enviados' => 0,
        'falhas' => 0,
        'mensagem' => '',
    ];

    if (!$resultado['configurado']) {
        $resultado['mensagem'] = 'Firebase ainda nao configurado.';
        return $resultado;
    }

    if (!$tokens) {
        $resultado['mensagem'] = 'Motorista sem aparelho cadastrado para receber push.';
        return $resultado;
    }

    $serviceAccount = push_firebase_service_account();
    $accessToken = push_firebase_access_token($serviceAccount);
    $projectId = (string) $serviceAccount['project_id'];
    $url = 'https://fcm.googleapis.com/v1/projects/' . rawurlencode($projectId) . '/messages:send';

    foreach ($tokens as $token) {
        $payload = [
            'message' => [
                'token' => $token,
                'data' => [
                    'tipo' => 'corrida_cancelada_passageiro',
                    'id_corrida' => (string) $idCorrida,
                    'click_action' => 'FLUTTER_NOTIFICATION_CLICK',
                ],
                'android' => [
                    'priority' => 'HIGH',
                    'ttl' => '30s',
                ],
            ],
        ];

        $response = push_http_json($url, $payload, [
            'Authorization: Bearer ' . $accessToken,
        ]);

        if ($response['ok']) {
            $resultado['enviados']++;
            continue;
        }

        $resultado['falhas']++;
        $codigoErro = push_firebase_error_code($response['json']);
        if (in_array($codigoErro, ['UNREGISTERED', 'INVALID_ARGUMENT'], true)) {
            push_token_inativar($token);
        }
    }

    $resultado['mensagem'] = 'Push de cancelamento processado.';
    return $resultado;
}

function push_enviar_chat_corrida_usuario(
    int $idUsuario,
    string $tipoUsuario,
    string $app,
    int $idCorrida,
    string $titulo,
    string $mensagem
): array {
    $tokens = push_tokens_para_usuario($idUsuario, $tipoUsuario, $app);
    $resultado = [
        'configurado' => push_firebase_configurado(),
        'tokens' => count($tokens),
        'enviados' => 0,
        'falhas' => 0,
        'mensagem' => '',
    ];

    if (!$resultado['configurado']) {
        $resultado['mensagem'] = 'Firebase ainda nao configurado.';
        return $resultado;
    }

    if (!$tokens) {
        $resultado['mensagem'] = 'Usuario sem aparelho cadastrado para receber push.';
        return $resultado;
    }

    $serviceAccount = push_firebase_service_account();
    $accessToken = push_firebase_access_token($serviceAccount);
    $projectId = (string) $serviceAccount['project_id'];
    $url = 'https://fcm.googleapis.com/v1/projects/' . rawurlencode($projectId) . '/messages:send';
    $icone = $app === 'bulo_passageiro' ? 'ic_stat_bulo_passageiro' : 'ic_stat_bulo_motorista';

    foreach ($tokens as $token) {
        $payload = [
            'message' => [
                'token' => $token,
                'notification' => [
                    'title' => $titulo,
                    'body' => $mensagem,
                ],
                'data' => [
                    'tipo' => 'chat_corrida',
                    'id_corrida' => (string) $idCorrida,
                    'tipo_usuario' => $tipoUsuario,
                    'click_action' => 'FLUTTER_NOTIFICATION_CLICK',
                ],
                'android' => [
                    'priority' => 'HIGH',
                    'notification' => [
                        'channel_id' => 'mensagens_chat',
                        'icon' => $icone,
                        'color' => '#0A66FF',
                        'sound' => 'mensagem_chat',
                        'notification_priority' => 'PRIORITY_HIGH',
                    ],
                ],
            ],
        ];

        $response = push_http_json($url, $payload, [
            'Authorization: Bearer ' . $accessToken,
        ]);

        if ($response['ok']) {
            $resultado['enviados']++;
            continue;
        }

        $resultado['falhas']++;
        $codigoErro = push_firebase_error_code($response['json']);
        if (in_array($codigoErro, ['UNREGISTERED', 'INVALID_ARGUMENT'], true)) {
            push_token_inativar($token);
        }
    }

    $resultado['mensagem'] = 'Push de chat processado.';
    return $resultado;
}

function push_tokens_para_destino(string $destino): array
{
    $params = [];
    $where = 'WHERE ativo = 1';
    if ($destino === 'motoristas') {
        $where .= ' AND tipo_usuario = ?';
        $params[] = 'motorista';
    } elseif ($destino === 'passageiros') {
        $where .= ' AND tipo_usuario = ?';
        $params[] = 'passageiro';
    }

    $stmt = db()->prepare(
        'SELECT token
         FROM push_tokens
         ' . $where . '
         ORDER BY atualizado_em DESC
         LIMIT 5000'
    );
    $stmt->execute($params);

    return array_map(static fn(array $row): string => (string) $row['token'], $stmt->fetchAll());
}

function push_tokens_para_usuario(int $idUsuario, string $tipoUsuario, ?string $app = null): array
{
    if ($idUsuario <= 0 || trim($tipoUsuario) === '') {
        return [];
    }

    $params = [$idUsuario, $tipoUsuario];
    $where = 'WHERE ativo = 1 AND id_usuario = ? AND tipo_usuario = ?';
    if (is_string($app) && trim($app) !== '') {
        $where .= ' AND (app = ? OR app IS NULL OR app = "")';
        $params[] = trim($app);
    }

    $stmt = db()->prepare(
        'SELECT token
         FROM push_tokens
         ' . $where . '
         ORDER BY atualizado_em DESC
         LIMIT 20'
    );
    $stmt->execute($params);

    return array_map(static fn(array $row): string => (string) $row['token'], $stmt->fetchAll());
}

function push_token_inativar(string $token): void
{
    $stmt = db()->prepare('UPDATE push_tokens SET ativo = 0 WHERE token = ?');
    $stmt->execute([$token]);
}

function push_firebase_service_account(): array
{
    $path = push_firebase_service_account_path();
    $raw = is_file($path) ? file_get_contents($path) : false;
    $json = is_string($raw) ? json_decode($raw, true) : null;

    if (!is_array($json)) {
        throw new RuntimeException('Arquivo de credenciais do Firebase invalido.');
    }

    foreach (['project_id', 'client_email', 'private_key'] as $key) {
        if (empty($json[$key])) {
            throw new RuntimeException('Credencial Firebase sem o campo ' . $key . '.');
        }
    }

    return $json;
}

function push_firebase_access_token(array $serviceAccount): string
{
    static $cache = [];

    $clientEmail = (string) $serviceAccount['client_email'];
    if (isset($cache[$clientEmail]) && $cache[$clientEmail]['exp'] > time() + 60) {
        return $cache[$clientEmail]['token'];
    }

    $tokenUri = (string) ($serviceAccount['token_uri'] ?? 'https://oauth2.googleapis.com/token');
    $now = time();
    $header = ['alg' => 'RS256', 'typ' => 'JWT'];
    $claims = [
        'iss' => $clientEmail,
        'scope' => 'https://www.googleapis.com/auth/firebase.messaging',
        'aud' => $tokenUri,
        'iat' => $now,
        'exp' => $now + 3600,
    ];
    $unsigned = push_base64url(json_encode($header)) . '.' . push_base64url(json_encode($claims));

    $signature = '';
    $signed = openssl_sign($unsigned, $signature, (string) $serviceAccount['private_key'], OPENSSL_ALGO_SHA256);
    if (!$signed) {
        throw new RuntimeException('Nao foi possivel assinar o token do Firebase.');
    }

    $jwt = $unsigned . '.' . push_base64url($signature);
    $response = push_http_form($tokenUri, [
        'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
        'assertion' => $jwt,
    ]);

    if (!$response['ok'] || empty($response['json']['access_token'])) {
        throw new RuntimeException('Firebase recusou a autenticacao do backend.');
    }

    $cache[$clientEmail] = [
        'token' => (string) $response['json']['access_token'],
        'exp' => $now + (int) ($response['json']['expires_in'] ?? 3600),
    ];

    return $cache[$clientEmail]['token'];
}

function push_http_form(string $url, array $fields): array
{
    return push_curl($url, http_build_query($fields), [
        'Content-Type: application/x-www-form-urlencoded',
    ]);
}

function push_http_json(string $url, array $payload, array $headers = []): array
{
    $headers[] = 'Content-Type: application/json; charset=utf-8';
    return push_curl($url, json_encode($payload, JSON_UNESCAPED_UNICODE), $headers);
}

function push_curl(string $url, string $body, array $headers): array
{
    if (!function_exists('curl_init')) {
        throw new RuntimeException('Extensao cURL nao habilitada no PHP.');
    }

    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => $headers,
        CURLOPT_POSTFIELDS => $body,
        CURLOPT_TIMEOUT => 12,
    ]);

    $raw = curl_exec($ch);
    $status = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $error = curl_error($ch);
    curl_close($ch);

    $json = is_string($raw) ? json_decode($raw, true) : null;
    return [
        'ok' => $status >= 200 && $status < 300,
        'status' => $status,
        'json' => is_array($json) ? $json : [],
        'raw' => is_string($raw) ? $raw : '',
        'error' => $error,
    ];
}

function push_firebase_error_code(array $response): string
{
    $error = $response['error'] ?? null;
    if (!is_array($error)) {
        return '';
    }

    foreach (($error['details'] ?? []) as $detail) {
        if (is_array($detail) && !empty($detail['errorCode'])) {
            return (string) $detail['errorCode'];
        }
    }

    return (string) ($error['status'] ?? '');
}

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