<?php
/**
 * includes/pwa.php
 *
 * [1.6.118.108] PWA — torna o site 1MZ instalável como app em qualquer
 * dispositivo (Android, iPhone, Windows, Mac) sem passar por loja.
 *
 * Como funciona:
 *  - Servimos o manifest e o service worker a partir da RAIZ do site
 *    (/1mz-manifest.json e /1mz-sw.js) via rewrite, para o service worker
 *    ter scope total ("/") — só assim controla o site todo.
 *  - O header Service-Worker-Allowed: / autoriza esse scope.
 *  - Injetamos as meta tags no <head> (manifest, theme-color, apple-*).
 *  - Registamos o service worker via JS no rodapé.
 *
 * Nota: PWA exige HTTPS (já é o caso de qualquer site WordPress moderno).
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

/**
 * Versão do PWA — muda isto quando alterares ícones/manifest/SW para forçar
 * os dispositivos a actualizar a cache.
 */
if ( ! defined( 'RMBZ_PWA_VERSION' ) ) {
    define( 'RMBZ_PWA_VERSION', '1.1.0' );
}

/* =============================================================================
 * 1) REWRITES — expõe o manifest e o SW na raiz do site
 * ============================================================================= */

function rmbz_pwa_add_rewrites() {
    add_rewrite_rule( '^1mz-manifest\.json$', 'index.php?rmbz_pwa=manifest', 'top' );
    add_rewrite_rule( '^1mz-sw\.js$',        'index.php?rmbz_pwa=sw',       'top' );
}
add_action( 'init', 'rmbz_pwa_add_rewrites' );

function rmbz_pwa_query_vars( $vars ) {
    $vars[] = 'rmbz_pwa';
    return $vars;
}
add_filter( 'query_vars', 'rmbz_pwa_query_vars' );

/**
 * Garante que os rewrites existem (auto-flush uma vez por versão).
 * Evita ter de ir a Definições → Links Permanentes manualmente.
 */
function rmbz_pwa_maybe_flush() {
    $done = get_option( 'rmbz_pwa_rewrites_version' );
    if ( $done !== RMBZ_PWA_VERSION ) {
        rmbz_pwa_add_rewrites();
        flush_rewrite_rules( false );
        update_option( 'rmbz_pwa_rewrites_version', RMBZ_PWA_VERSION );
    }
}
add_action( 'init', 'rmbz_pwa_maybe_flush', 99 );

/* =============================================================================
 * 2) RESPONDER aos pedidos do manifest e do SW
 * ============================================================================= */

function rmbz_pwa_handle_requests() {
    $what = get_query_var( 'rmbz_pwa' );
    if ( ! $what ) return;

    if ( $what === 'manifest' ) {
        rmbz_pwa_output_manifest();
    } elseif ( $what === 'sw' ) {
        rmbz_pwa_output_service_worker();
    } elseif ( $what === 'fcmsw' ) {
        rmbz_pwa_output_firebase_sw();
    }
}
add_action( 'template_redirect', 'rmbz_pwa_handle_requests' );

/**
 * O manifest.json — descreve a app aos dispositivos.
 */
function rmbz_pwa_output_manifest() {
    nocache_headers();
    header( 'Content-Type: application/manifest+json; charset=utf-8' );

    $icons_url = RMBZ_PLUGIN_URL . 'assets/pwa/';
    // Página onde a app abre (a área do artista). Filtrável.
    $start_url = apply_filters( 'rmbz_pwa_start_url', home_url( '/' ) );

    $manifest = [
        'name'             => '1MZ For Artists',
        'short_name'       => '1MZ',
        'description'      => 'Distribui a tua música, candidata-te a videoclipes e gere os teus projectos com a 1MusicMoz.',
        'lang'             => 'pt',
        'dir'              => 'ltr',
        'start_url'        => $start_url,
        'scope'            => home_url( '/' ),
        'display'          => 'standalone',
        'orientation'      => 'portrait-primary',
        'background_color' => '#ffffff',
        'theme_color'      => '#ffffff',
        'categories'       => [ 'music', 'entertainment' ],
        'icons'            => [
            [
                'src'     => $icons_url . 'icon-192.png',
                'sizes'   => '192x192',
                'type'    => 'image/png',
                'purpose' => 'any',
            ],
            [
                'src'     => $icons_url . 'icon-512.png',
                'sizes'   => '512x512',
                'type'    => 'image/png',
                'purpose' => 'any',
            ],
            [
                'src'     => $icons_url . 'icon-maskable-192.png',
                'sizes'   => '192x192',
                'type'    => 'image/png',
                'purpose' => 'maskable',
            ],
            [
                'src'     => $icons_url . 'icon-maskable-512.png',
                'sizes'   => '512x512',
                'type'    => 'image/png',
                'purpose' => 'maskable',
            ],
        ],
    ];

    echo wp_json_encode( $manifest, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
    exit;
}

/**
 * O service worker — dá-lhe "instalabilidade" e um fallback offline simples.
 *
 * Estratégia conservadora: network-first para navegação (para nunca servir
 * páginas desactualizadas a uma plataforma dinâmica), com fallback a uma
 * página offline só quando não há rede. Assets do PWA em cache.
 */
function rmbz_pwa_output_service_worker() {
    nocache_headers();
    header( 'Content-Type: application/javascript; charset=utf-8' );
    // CRÍTICO: autoriza o SW a ter scope "/" mesmo sendo servido da raiz.
    header( 'Service-Worker-Allowed: /' );

    $ver        = RMBZ_PWA_VERSION;
    $icons_url  = RMBZ_PLUGIN_URL . 'assets/pwa/';
    $offline    = home_url( '/' );
    $cache_name = 'onemz-pwa-v' . $ver;

    ?>
// 1MZ For Artists — Service Worker v<?php echo esc_js( $ver ); ?>

const CACHE = '<?php echo esc_js( $cache_name ); ?>';
const PRECACHE = [
    '<?php echo esc_js( $icons_url ); ?>icon-192.png',
    '<?php echo esc_js( $icons_url ); ?>icon-512.png',
];

// Instalar: pré-carrega os ícones e activa logo
self.addEventListener('install', (event) => {
    self.skipWaiting();
    event.waitUntil(
        caches.open(CACHE).then((cache) => cache.addAll(PRECACHE).catch(() => {}))
    );
});

// Activar: limpa caches antigas (de versões anteriores)
self.addEventListener('activate', (event) => {
    event.waitUntil(
        caches.keys().then((keys) =>
            Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k)))
        ).then(() => self.clients.claim())
    );
});

// Fetch:
//  - Navegação (páginas HTML): network-first, fallback à última visita em cache.
//  - Outros pedidos GET: deixa passar normalmente (não interferimos para não
//    quebrar uploads, AJAX, API, etc).
self.addEventListener('fetch', (event) => {
    const req = event.request;

    // Só tratamos GET; POST/uploads/API passam directos.
    if (req.method !== 'GET') return;

    // Navegação de páginas
    if (req.mode === 'navigate') {
        event.respondWith(
            fetch(req)
                .then((res) => {
                    // Guarda uma cópia da página para fallback offline
                    const copy = res.clone();
                    caches.open(CACHE).then((c) => c.put(req, copy)).catch(() => {});
                    return res;
                })
                .catch(() =>
                    caches.match(req).then((cached) =>
                        cached || caches.match('<?php echo esc_js( $offline ); ?>')
                    )
                )
        );
        return;
    }

    // Ícones do PWA: cache-first (mudam pouco)
    if (req.url.indexOf('/assets/pwa/') !== -1) {
        event.respondWith(
            caches.match(req).then((cached) => cached || fetch(req))
        );
    }
});
    <?php
    exit;
}

/* =============================================================================
 * 3) META TAGS no <head> — ligam o manifest e configuram iOS
 * ============================================================================= */

function rmbz_pwa_head_tags() {
    $manifest_url = home_url( '/1mz-manifest.json' );
    $apple_icon   = RMBZ_PLUGIN_URL . 'assets/pwa/apple-touch-icon.png';
    ?>
    <!-- [1.6.118.108] 1MZ PWA -->
    <link rel="manifest" href="<?php echo esc_url( $manifest_url ); ?>">
    <meta name="theme-color" content="#ffffff">
    <meta name="mobile-web-app-capable" content="yes">
    <!-- iOS / Safari -->
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="default">
    <meta name="apple-mobile-web-app-title" content="1MZ">
    <link rel="apple-touch-icon" href="<?php echo esc_url( $apple_icon ); ?>">
    <?php
}
add_action( 'wp_head', 'rmbz_pwa_head_tags', 1 );

/* =============================================================================
 * 4) REGISTAR o service worker (JS no rodapé)
 * ============================================================================= */

function rmbz_pwa_register_sw() {
    $sw_url = home_url( '/1mz-sw.js' );
    ?>
    <script>
    (function () {
        if ('serviceWorker' in navigator) {
            window.addEventListener('load', function () {
                navigator.serviceWorker.register('<?php echo esc_js( $sw_url ); ?>', { scope: '/' })
                    .catch(function (e) { /* silencioso — PWA é progressivo */ });
            });
        }
    })();
    </script>
    <?php
}
add_action( 'wp_footer', 'rmbz_pwa_register_sw' );

/* =============================================================================
 * 5) [1.6.118.109] NOTIFICAÇÕES PUSH no PWA via Firebase (FCM Web)
 * ============================================================================= */

/**
 * Configuração do Firebase para a WEB. Estas chaves vêm do Firebase Console:
 *   Configurações do projecto → Os teus apps → App Web (</>) → SDK setup.
 * E a "VAPID key" vem de: Cloud Messaging → Web configuration → Web Push
 * certificates → Key pair.
 *
 * Guardadas em opções do WordPress (configuráveis no admin). Devolve null se
 * ainda não estiverem configuradas (o push fica simplesmente inactivo).
 */
function rmbz_pwa_firebase_web_config() {
    $cfg = get_option( 'rmbz_pwa_firebase_web', [] );
    if ( ! is_array( $cfg ) ) return null;

    $required = [ 'apiKey', 'projectId', 'messagingSenderId', 'appId', 'vapidKey' ];
    foreach ( $required as $k ) {
        if ( empty( $cfg[ $k ] ) ) return null;
    }
    return $cfg;
}

/**
 * O Firebase precisa de um service worker próprio na raiz:
 * /firebase-messaging-sw.js — servimo-lo via rewrite (como o SW do PWA).
 */
function rmbz_pwa_add_firebase_sw_rewrite() {
    add_rewrite_rule( '^firebase-messaging-sw\.js$', 'index.php?rmbz_pwa=fcmsw', 'top' );
}
add_action( 'init', 'rmbz_pwa_add_firebase_sw_rewrite' );

/**
 * Serve o service worker do Firebase Messaging.
 */
function rmbz_pwa_output_firebase_sw() {
    nocache_headers();
    header( 'Content-Type: application/javascript; charset=utf-8' );
    header( 'Service-Worker-Allowed: /' );

    $cfg = rmbz_pwa_firebase_web_config();
    if ( ! $cfg ) {
        echo "// Firebase web push não configurado.\n";
        exit;
    }

    $icon = RMBZ_PLUGIN_URL . 'assets/pwa/icon-192.png';
    ?>
// 1MZ For Artists — Firebase Messaging SW
importScripts('https://www.gstatic.com/firebasejs/10.12.2/firebase-app-compat.js');
importScripts('https://www.gstatic.com/firebasejs/10.12.2/firebase-messaging-compat.js');

firebase.initializeApp({
    apiKey: '<?php echo esc_js( $cfg['apiKey'] ); ?>',
    projectId: '<?php echo esc_js( $cfg['projectId'] ); ?>',
    messagingSenderId: '<?php echo esc_js( $cfg['messagingSenderId'] ); ?>',
    appId: '<?php echo esc_js( $cfg['appId'] ); ?>'
});

const messaging = firebase.messaging();

// Mensagem recebida com a app/site fechado (background)
messaging.onBackgroundMessage(function (payload) {
    const title = (payload.notification && payload.notification.title) || '1MZ For Artists';
    const options = {
        body: (payload.notification && payload.notification.body) || '',
        icon: '<?php echo esc_js( $icon ); ?>',
        badge: '<?php echo esc_js( $icon ); ?>',
        data: payload.data || {},
    };
    self.registration.showNotification(title, options);
});

// Clicar na notificação → abrir o site (ou o link enviado em data.url)
self.addEventListener('notificationclick', function (event) {
    event.notification.close();
    const url = (event.notification.data && event.notification.data.url) || '<?php echo esc_js( home_url( '/' ) ); ?>';
    event.waitUntil(
        clients.matchAll({ type: 'window', includeUncontrolled: true }).then(function (list) {
            for (const c of list) {
                if (c.url === url && 'focus' in c) return c.focus();
            }
            if (clients.openWindow) return clients.openWindow(url);
        })
    );
});
    <?php
    exit;
}

/**
 * Injeta o JS que pede permissão e regista o token FCM web.
 * Só corre se o Firebase web estiver configurado E o utilizador estiver logado
 * (precisamos do telemóvel/sessão para associar o token).
 */
function rmbz_pwa_push_register_js() {
    $cfg = rmbz_pwa_firebase_web_config();
    if ( ! $cfg ) return;
    if ( ! is_user_logged_in() ) return; // só registamos push de quem tem conta

    // Endpoint REST de registo (o mesmo do app)
    $register_url = rest_url( 'onemz/v1/push/register-token' );
    $nonce        = wp_create_nonce( 'wp_rest' );
    $home         = home_url( '/' );
    ?>
    <script type="module">
    import { initializeApp } from 'https://www.gstatic.com/firebasejs/10.12.2/firebase-app.js';
    import { getMessaging, getToken } from 'https://www.gstatic.com/firebasejs/10.12.2/firebase-messaging.js';

    (async function () {
        try {
            if (!('serviceWorker' in navigator) || !('Notification' in window)) return;

            const app = initializeApp({
                apiKey: '<?php echo esc_js( $cfg['apiKey'] ); ?>',
                projectId: '<?php echo esc_js( $cfg['projectId'] ); ?>',
                messagingSenderId: '<?php echo esc_js( $cfg['messagingSenderId'] ); ?>',
                appId: '<?php echo esc_js( $cfg['appId'] ); ?>'
            });

            // Regista o SW do Firebase
            const swReg = await navigator.serviceWorker.register('/firebase-messaging-sw.js', { scope: '/' });

            // Pede permissão (idealmente após um clique; aqui é suave)
            let perm = Notification.permission;
            if (perm === 'default') {
                perm = await Notification.requestPermission();
            }
            if (perm !== 'granted') return;

            const messaging = getMessaging(app);
            const token = await getToken(messaging, {
                vapidKey: '<?php echo esc_js( $cfg['vapidKey'] ); ?>',
                serviceWorkerRegistration: swReg,
            });
            if (!token) return;

            // Device ID estável guardado no próprio dispositivo
            let deviceId = localStorage.getItem('onemz_device_id');
            if (!deviceId) {
                deviceId = 'web-' + Math.random().toString(36).slice(2) + Date.now().toString(36);
                localStorage.setItem('onemz_device_id', deviceId);
            }

            // Regista no servidor (mesmo endpoint do app Android)
            await fetch('<?php echo esc_js( $register_url ); ?>', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'X-WP-Nonce': '<?php echo esc_js( $nonce ); ?>',
                    'X-1MZ-Device-Id': deviceId,
                },
                credentials: 'same-origin',
                body: JSON.stringify({
                    fcm_token: token,
                    platform: 'web',
                    app_version: 'pwa',
                }),
            });
        } catch (e) {
            /* push é progressivo — falha em silêncio */
        }
    })();
    </script>
    <?php
}
add_action( 'wp_footer', 'rmbz_pwa_push_register_js', 20 );

/* =============================================================================
 * 6) [1.6.118.110] DEFINIÇÕES — chaves Firebase web (aba em Configurações)
 * ============================================================================= */
// Nota: deixou de ter submenu próprio; o conteúdo é mostrado como aba na
// página Configurações (rmbz-settings → tab=pwa).

function rmbz_pwa_handle_save_settings() {
    if ( ! current_user_can( 'manage_options' ) ) wp_die( 'Sem permissão.' );
    check_admin_referer( 'rmbz_pwa_push_settings' );

    $cfg = [
        'apiKey'            => sanitize_text_field( $_POST['apiKey'] ?? '' ),
        'projectId'         => sanitize_text_field( $_POST['projectId'] ?? '' ),
        'messagingSenderId' => sanitize_text_field( $_POST['messagingSenderId'] ?? '' ),
        'appId'             => sanitize_text_field( $_POST['appId'] ?? '' ),
        'vapidKey'          => sanitize_text_field( $_POST['vapidKey'] ?? '' ),
    ];
    update_option( 'rmbz_pwa_firebase_web', $cfg );

    wp_safe_redirect( admin_url( 'admin.php?page=rmbz-settings&tab=pwa&saved=1' ) );
    exit;
}
add_action( 'admin_post_rmbz_pwa_save_push', 'rmbz_pwa_handle_save_settings' );

function rmbz_pwa_render_settings_tab() {
    $cfg = get_option( 'rmbz_pwa_firebase_web', [] );
    if ( ! is_array( $cfg ) ) $cfg = [];
    $fields = [
        'apiKey'            => [ 'API Key', 'Configurações do projecto → App Web → apiKey' ],
        'projectId'         => [ 'Project ID', 'O ID do teu projecto Firebase' ],
        'messagingSenderId' => [ 'Messaging Sender ID', 'App Web → messagingSenderId' ],
        'appId'             => [ 'App ID', 'App Web → appId' ],
        'vapidKey'          => [ 'VAPID Key (Web Push)', 'Cloud Messaging → Web configuration → Web Push certificates → Key pair' ],
    ];
    $ready = rmbz_pwa_firebase_web_config() !== null;
    ?>
    <div>
        <h2 style="margin-top:0;">🔔 Notificações PWA (Web Push via Firebase)</h2>

        <?php if ( isset( $_GET['saved'] ) ): ?>
            <div class="notice notice-success"><p>Configuração guardada.</p></div>
        <?php endif; ?>

        <div style="background:<?php echo $ready ? '#e6f4ea' : '#fff8e1'; ?>; border:1px solid <?php echo $ready ? '#34a853' : '#f0b90b'; ?>; border-radius:8px; padding:12px 16px; max-width:760px; margin:14px 0;">
            <strong><?php echo $ready ? '✅ Push web activo' : '⚠️ Push web ainda não configurado'; ?></strong>
            <p style="margin:6px 0 0; font-size:13px;">
                Preenche as chaves do teu projecto Firebase (a mesma conta que usas para o app Android).
                As chaves <strong>web</strong> obtêm-se no Firebase Console:<br>
                1. <em>Configurações do projecto → Os teus apps</em> → se não tiveres um <strong>App Web (&lt;/&gt;)</strong>, cria um.<br>
                2. Copia apiKey, projectId, messagingSenderId, appId do "SDK setup and configuration".<br>
                3. <em>Cloud Messaging → Web configuration → Web Push certificates</em> → gera/copia a <strong>Key pair</strong> (é a VAPID Key).
            </p>
        </div>

        <form method="post" action="<?php echo admin_url( 'admin-post.php' ); ?>" style="max-width:760px;">
            <input type="hidden" name="action" value="rmbz_pwa_save_push">
            <?php wp_nonce_field( 'rmbz_pwa_push_settings' ); ?>
            <table class="form-table">
                <?php foreach ( $fields as $key => $meta ): ?>
                    <tr>
                        <th scope="row"><label for="<?php echo esc_attr( $key ); ?>"><?php echo esc_html( $meta[0] ); ?></label></th>
                        <td>
                            <input type="text" id="<?php echo esc_attr( $key ); ?>" name="<?php echo esc_attr( $key ); ?>"
                                   value="<?php echo esc_attr( $cfg[ $key ] ?? '' ); ?>"
                                   class="regular-text" style="width:100%; max-width:520px; font-family:monospace;">
                            <p class="description"><?php echo esc_html( $meta[1] ); ?></p>
                        </td>
                    </tr>
                <?php endforeach; ?>
            </table>
            <?php submit_button( 'Guardar configuração' ); ?>
        </form>

        <hr style="max-width:760px; margin:24px 0;">
        <h3 style="max-width:760px;">Como funciona</h3>
        <div style="max-width:760px; font-size:13px; color:#444;">
            <p>Depois de configurado, qualquer artista com sessão iniciada que use o site/PWA é convidado a permitir notificações. O token dele é guardado na mesma tabela do app Android (<code>rmbz_app_push_tokens</code>, plataforma <code>web</code>).</p>
            <p>As notificações que já envias pelo app (projecto aprovado, novas mensagens, etc.) passam a chegar <strong>automaticamente</strong> também ao PWA — porque usam a mesma função de envio (<code>rmbz_fcm_send_to_user</code>), que percorre todos os tokens activos do utilizador, web incluídos.</p>
            <p><strong>iPhone:</strong> só recebe se tiver instalado o PWA ("Adicionar ao ecrã principal") e iOS 16.4+.</p>
        </div>
    </div>
    <?php
}
