<?php
/**
 * includes/admin/smoke-tests.php
 *
 * ============================================================================
 * SMOKE TESTS — 1.5.153
 * ============================================================================
 *
 * Suite de testes rápidos, não-destrutivos, idempotentes que confirmam
 * que o essencial do plugin está funcional após cada deploy.
 *
 * Características técnicas:
 *   - NÃO-DESTRUTIVO: zero CREATE/UPDATE/DELETE em dados reais
 *   - IDEMPOTENTE: corre 100 vezes seguidas sem efeitos colaterais
 *   - RÁPIDO: alvo <10 segundos para suite completa
 *   - READ-ONLY: só SELECT, function_exists, file_exists, wp_next_scheduled
 *   - INDEPENDENTE: cada teste corre isolado; falha de um não afecta outros
 *
 * Categorias:
 *   1. Schema    — tabelas e colunas críticas existem
 *   2. Funções   — APIs públicas estão definidas
 *   3. Cron      — jobs essenciais agendados
 *   4. Filesystem — pastas críticas writable
 *   5. Notif     — Centro de Notificações operacional
 *   6. Catálogo  — DSPs disponíveis
 *
 * Cada teste devolve:
 *   ['name' => '...', 'category' => '...', 'status' => 'pass|fail|warn',
 *    'message' => '...', 'duration_ms' => N]
 *
 * INTEGRAÇÃO: secção nova "🧪 Smoke Tests" na página 🛡 Pré-Deploy.
 */

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


// ============================================================================
// REGISTRY DE TESTES
// ============================================================================

/**
 * Devolve a lista canónica de smoke tests a executar.
 * Ordem importa para legibilidade do output.
 */
function rmbz_smoke_get_test_registry() {
    return [
        // Categoria 1: Schema
        [ 'id' => 'schema_wallets',           'category' => 'Schema',     'fn' => 'rmbz_smoke_test_schema_wallets',           'name' => 'Tabela rmbz_wallets acessível' ],
        [ 'id' => 'schema_transactions',      'category' => 'Schema',     'fn' => 'rmbz_smoke_test_schema_transactions',      'name' => 'Tabela rmbz_wallet_transactions acessível' ],
        [ 'id' => 'schema_distro_releases',   'category' => 'Schema',     'fn' => 'rmbz_smoke_test_schema_distro_releases',   'name' => 'Tabela rmbz_distro_releases acessível' ],
        [ 'id' => 'schema_distro_ledger',     'category' => 'Schema',     'fn' => 'rmbz_smoke_test_schema_distro_ledger',     'name' => 'Tabela rmbz_distro_ledger com coluna amount' ],
        [ 'id' => 'schema_subscriptions',     'category' => 'Schema',     'fn' => 'rmbz_smoke_test_schema_subscriptions',     'name' => 'Tabela rmbz_subscriptions acessível' ],
        [ 'id' => 'schema_dsp_catalog',       'category' => 'Schema',     'fn' => 'rmbz_smoke_test_schema_dsp_catalog',       'name' => 'Tabela rmbz_dsp_catalog (1.5.144) instalada' ],
        [ 'id' => 'schema_deploy_log',        'category' => 'Schema',     'fn' => 'rmbz_smoke_test_schema_deploy_log',        'name' => 'Tabela rmbz_deploy_log (1.5.148) instalada' ],

        // Categoria 2: Funções core
        [ 'id' => 'fn_wallet_balance',        'category' => 'Funções',    'fn' => 'rmbz_smoke_test_fn_wallet_balance',        'name' => 'rmbz_update_wallet_balance() existe' ],
        [ 'id' => 'fn_secure_transfer',       'category' => 'Funções',    'fn' => 'rmbz_smoke_test_fn_secure_transfer',       'name' => 'rmbz_secure_transfer_usd_to_mt() existe (USD→MT)' ],
        [ 'id' => 'fn_notify',                'category' => 'Funções',    'fn' => 'rmbz_smoke_test_fn_notify',                'name' => 'rmbz_notify() existe (Centro de Notificações)' ],
        [ 'id' => 'fn_dsp_catalog',           'category' => 'Funções',    'fn' => 'rmbz_smoke_test_fn_dsp_catalog',           'name' => 'rmbz_distro_dsp_catalog() devolve catálogo' ],

        // Categoria 3: Cron
        [ 'id' => 'cron_unlock_funds',        'category' => 'Cron',       'fn' => 'rmbz_smoke_test_cron_unlock_funds',        'name' => 'Cron rmbz_unlock_funds_hook agendado' ],
        [ 'id' => 'cron_first_deposit',       'category' => 'Cron',       'fn' => 'rmbz_smoke_test_cron_first_deposit',       'name' => 'Cron de cleanup do 1º depósito agendado' ],

        // Categoria 4: Filesystem
        [ 'id' => 'fs_uploads_writable',      'category' => 'Filesystem', 'fn' => 'rmbz_smoke_test_fs_uploads',               'name' => 'Pasta /uploads/ writable' ],
        [ 'id' => 'fs_snapshots_dir',         'category' => 'Filesystem', 'fn' => 'rmbz_smoke_test_fs_snapshots',             'name' => 'Pasta de snapshots existe e é writable' ],
        [ 'id' => 'fs_imports_archive',       'category' => 'Filesystem', 'fn' => 'rmbz_smoke_test_fs_imports_archive',       'name' => 'Pasta /uploads/rmbz_imports_archive/ acessível' ],

        // Categoria 5: Centro de Notificações
        [ 'id' => 'notif_templates',          'category' => 'Notificações', 'fn' => 'rmbz_smoke_test_notif_templates',        'name' => 'Templates de notificação registados (≥11)' ],
        [ 'id' => 'notif_email_logs',         'category' => 'Notificações', 'fn' => 'rmbz_smoke_test_notif_email_logs',       'name' => 'Tabela rmbz_email_logs acessível' ],

        // Categoria 6: Catálogo
        [ 'id' => 'catalog_dsp_count',        'category' => 'Catálogo',     'fn' => 'rmbz_smoke_test_catalog_dsp_count',      'name' => 'Catálogo DSP tem ≥10 entradas activas' ],
        [ 'id' => 'catalog_dsp_tier1',        'category' => 'Catálogo',     'fn' => 'rmbz_smoke_test_catalog_dsp_tier1',      'name' => 'Tier 1 (essenciais) tem ≥6 DSPs activos' ],
    ];
}


// ============================================================================
// HELPER PARA EXECUTAR UM TESTE COM TIMING
// ============================================================================

function rmbz_smoke_run_one( $test ) {
    $start = microtime( true );
    $fn = $test['fn'];
    if ( ! function_exists( $fn ) ) {
        return [
            'id'         => $test['id'],
            'category'   => $test['category'],
            'name'       => $test['name'],
            'status'     => 'fail',
            'message'    => 'Função do teste não definida: ' . $fn,
            'duration_ms' => 0,
        ];
    }
    try {
        $r = call_user_func( $fn );
        // Esperamos: ['status' => 'pass'|'fail'|'warn', 'message' => '...']
        if ( ! is_array( $r ) || ! isset( $r['status'] ) ) {
            $r = [ 'status' => 'fail', 'message' => 'Teste devolveu formato inválido.' ];
        }
    } catch ( Throwable $e ) {
        $r = [ 'status' => 'fail', 'message' => 'Excepção: ' . $e->getMessage() ];
    }
    $duration = round( ( microtime( true ) - $start ) * 1000, 1 );

    return [
        'id'          => $test['id'],
        'category'    => $test['category'],
        'name'        => $test['name'],
        'status'      => $r['status'],
        'message'     => $r['message'] ?? '',
        'duration_ms' => $duration,
    ];
}


/**
 * Corre toda a suite. Devolve array de resultados + sumário agregado.
 */
function rmbz_smoke_run_all() {
    $start = microtime( true );
    $registry = rmbz_smoke_get_test_registry();
    $results = [];
    foreach ( $registry as $test ) {
        $results[] = rmbz_smoke_run_one( $test );
    }
    $total_ms = round( ( microtime( true ) - $start ) * 1000, 1 );

    $summary = [
        'total'    => count( $results ),
        'pass'     => count( array_filter( $results, function( $r ) { return $r['status'] === 'pass'; } ) ),
        'fail'     => count( array_filter( $results, function( $r ) { return $r['status'] === 'fail'; } ) ),
        'warn'     => count( array_filter( $results, function( $r ) { return $r['status'] === 'warn'; } ) ),
        'total_ms' => $total_ms,
        'ts'       => current_time( 'mysql' ),
    ];

    return [ 'results' => $results, 'summary' => $summary ];
}


// ============================================================================
// TESTES — SCHEMA
// ============================================================================

function rmbz_smoke_test_schema_wallets() {
    global $wpdb;
    $t = $wpdb->prefix . 'rmbz_wallets';
    if ( $wpdb->get_var( "SHOW TABLES LIKE '$t'" ) !== $t ) {
        return [ 'status' => 'fail', 'message' => 'Tabela não existe.' ];
    }
    // SELECT 0-row apenas para confirmar que conseguimos ler
    $count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM $t LIMIT 1" );
    return [ 'status' => 'pass', 'message' => $count . ' wallets na tabela.' ];
}

function rmbz_smoke_test_schema_transactions() {
    global $wpdb;
    $t = $wpdb->prefix . 'rmbz_wallet_transactions';
    if ( $wpdb->get_var( "SHOW TABLES LIKE '$t'" ) !== $t ) {
        return [ 'status' => 'fail', 'message' => 'Tabela não existe.' ];
    }
    // Confirmar que tem coluna 'transaction_id' (PK correcta — bug fix 1.5.147)
    $cols = $wpdb->get_col( "SHOW COLUMNS FROM $t" );
    if ( ! in_array( 'transaction_id', $cols, true ) ) {
        return [ 'status' => 'fail', 'message' => 'Coluna transaction_id (PK) ausente.' ];
    }
    return [ 'status' => 'pass', 'message' => 'Schema OK, ' . count( $cols ) . ' colunas.' ];
}

function rmbz_smoke_test_schema_distro_releases() {
    global $wpdb;
    $t = $wpdb->prefix . 'rmbz_distro_releases';
    if ( $wpdb->get_var( "SHOW TABLES LIKE '$t'" ) !== $t ) {
        return [ 'status' => 'fail', 'message' => 'Tabela não existe.' ];
    }
    $cols = $wpdb->get_col( "SHOW COLUMNS FROM $t" );
    // [1.5.154] Coluna correcta é `artist_telemovel`, não `user_telemovel`
    // (corrigido após o próprio smoke test apanhar o bug).
    $required = [ 'id', 'title', 'artist_telemovel', 'status' ];
    $missing = array_diff( $required, $cols );
    if ( ! empty( $missing ) ) {
        return [ 'status' => 'fail', 'message' => 'Colunas em falta: ' . implode( ', ', $missing ) ];
    }
    return [ 'status' => 'pass', 'message' => count( $cols ) . ' colunas, todas críticas presentes.' ];
}

function rmbz_smoke_test_schema_distro_ledger() {
    global $wpdb;
    $t = $wpdb->prefix . 'rmbz_distro_ledger';
    if ( $wpdb->get_var( "SHOW TABLES LIKE '$t'" ) !== $t ) {
        return [ 'status' => 'fail', 'message' => 'Tabela não existe.' ];
    }
    // Confirmar coluna 'amount' (a função 1.5.145 depende dela)
    $cols = $wpdb->get_col( "SHOW COLUMNS FROM $t" );
    if ( ! in_array( 'amount', $cols, true ) ) {
        return [ 'status' => 'fail', 'message' => 'Coluna amount ausente — bridge 1.5.145 vai falhar.' ];
    }
    if ( ! in_array( 'import_hash', $cols, true ) ) {
        return [ 'status' => 'warn', 'message' => 'Coluna import_hash ausente — imports com tracking parcial.' ];
    }
    return [ 'status' => 'pass', 'message' => 'amount + import_hash OK.' ];
}

function rmbz_smoke_test_schema_subscriptions() {
    global $wpdb;
    $t = $wpdb->prefix . 'rmbz_subscriptions';
    if ( $wpdb->get_var( "SHOW TABLES LIKE '$t'" ) !== $t ) {
        return [ 'status' => 'fail', 'message' => 'Tabela não existe.' ];
    }
    return [ 'status' => 'pass', 'message' => 'Tabela presente.' ];
}

function rmbz_smoke_test_schema_dsp_catalog() {
    global $wpdb;
    $t = $wpdb->prefix . 'rmbz_dsp_catalog';
    if ( $wpdb->get_var( "SHOW TABLES LIKE '$t'" ) !== $t ) {
        return [ 'status' => 'fail', 'message' => 'Tabela não criada — schema da 1.5.144 não correu.' ];
    }
    return [ 'status' => 'pass', 'message' => 'Catálogo DB instalado.' ];
}

function rmbz_smoke_test_schema_deploy_log() {
    global $wpdb;
    $t = $wpdb->prefix . 'rmbz_deploy_log';
    if ( $wpdb->get_var( "SHOW TABLES LIKE '$t'" ) !== $t ) {
        return [ 'status' => 'fail', 'message' => 'Tabela não criada — schema da 1.5.148 não correu.' ];
    }
    $count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM $t" );
    if ( $count === 0 ) {
        return [ 'status' => 'warn', 'message' => 'Tabela vazia — nunca registou versão (re-active o plugin?).' ];
    }
    return [ 'status' => 'pass', 'message' => $count . ' deploy(s) registados.' ];
}


// ============================================================================
// TESTES — FUNÇÕES CORE
// ============================================================================

function rmbz_smoke_test_fn_wallet_balance() {
    if ( ! function_exists( 'rmbz_update_wallet_balance' ) ) {
        return [ 'status' => 'fail', 'message' => 'API principal de wallet ausente.' ];
    }
    return [ 'status' => 'pass', 'message' => 'OK.' ];
}

function rmbz_smoke_test_fn_secure_transfer() {
    if ( ! function_exists( 'rmbz_secure_transfer_usd_to_mt' ) ) {
        return [ 'status' => 'fail', 'message' => 'Função USD→MT ausente — invariante crítico falha.' ];
    }
    return [ 'status' => 'pass', 'message' => 'OK.' ];
}

function rmbz_smoke_test_fn_notify() {
    if ( ! function_exists( 'rmbz_notify' ) ) {
        return [ 'status' => 'fail', 'message' => 'Centro de Notificações não carregado.' ];
    }
    return [ 'status' => 'pass', 'message' => 'OK.' ];
}

function rmbz_smoke_test_fn_dsp_catalog() {
    if ( ! function_exists( 'rmbz_distro_dsp_catalog' ) ) {
        return [ 'status' => 'fail', 'message' => 'Função catálogo DSP ausente.' ];
    }
    $catalog = rmbz_distro_dsp_catalog();
    if ( ! is_array( $catalog ) || empty( $catalog ) ) {
        return [ 'status' => 'fail', 'message' => 'Catálogo vazio.' ];
    }
    return [ 'status' => 'pass', 'message' => count( $catalog ) . ' DSPs no catálogo activo.' ];
}


// ============================================================================
// TESTES — CRON
// ============================================================================

function rmbz_smoke_test_cron_unlock_funds() {
    $next = wp_next_scheduled( 'rmbz_unlock_funds_hook' );
    if ( ! $next ) {
        return [ 'status' => 'fail', 'message' => 'Cron diário não agendado — re-activar plugin.' ];
    }
    $minutes_until = round( ( $next - time() ) / 60 );
    return [ 'status' => 'pass', 'message' => 'Próximo run em ~' . $minutes_until . ' min.' ];
}

function rmbz_smoke_test_cron_first_deposit() {
    // [1.5.154] Bug fix: cleanup do 1º depósito NÃO é um cron próprio —
    // é uma função (rmbz_first_deposit_cleanup_extended) hooked ao
    // rmbz_unlock_funds_hook. Logo verificar se o cron diário está
    // agendado E se o hook tem o callback registado.
    if ( ! wp_next_scheduled( 'rmbz_unlock_funds_hook' ) ) {
        return [ 'status' => 'fail', 'message' => 'Cron diário (parent) não agendado.' ];
    }
    if ( ! has_action( 'rmbz_unlock_funds_hook', 'rmbz_first_deposit_cleanup_extended' ) ) {
        return [ 'status' => 'warn', 'message' => 'Callback de cleanup não registado no cron diário.' ];
    }
    return [ 'status' => 'pass', 'message' => 'Callback registado no cron diário.' ];
}


// ============================================================================
// TESTES — FILESYSTEM
// ============================================================================

function rmbz_smoke_test_fs_uploads() {
    $upload_dir = wp_upload_dir();
    if ( ! empty( $upload_dir['error'] ) ) {
        return [ 'status' => 'fail', 'message' => $upload_dir['error'] ];
    }
    if ( ! is_writable( $upload_dir['basedir'] ) ) {
        return [ 'status' => 'fail', 'message' => 'Pasta não-writable: ' . $upload_dir['basedir'] ];
    }
    return [ 'status' => 'pass', 'message' => 'Writable.' ];
}

function rmbz_smoke_test_fs_snapshots() {
    if ( ! function_exists( 'rmbz_predeploy_snapshots_dir' ) ) {
        return [ 'status' => 'warn', 'message' => 'Função predeploy não carregada.' ];
    }
    $dir = rmbz_predeploy_snapshots_dir();
    if ( ! is_dir( $dir ) ) {
        return [ 'status' => 'fail', 'message' => 'Pasta não criada.' ];
    }
    if ( ! is_writable( $dir ) ) {
        return [ 'status' => 'fail', 'message' => 'Pasta não writable.' ];
    }
    // Verificar .htaccess de segurança
    if ( ! file_exists( $dir . '/.htaccess' ) ) {
        return [ 'status' => 'warn', 'message' => '.htaccess de protecção em falta.' ];
    }
    return [ 'status' => 'pass', 'message' => 'OK + .htaccess presente.' ];
}

function rmbz_smoke_test_fs_imports_archive() {
    $upload = wp_upload_dir();
    $dir = trailingslashit( $upload['basedir'] ) . 'rmbz_imports_archive';
    if ( ! is_dir( $dir ) ) {
        // Não-fail porque só é criada quando há primeiro import
        return [ 'status' => 'warn', 'message' => 'Não criada ainda (ok se nunca importaste).' ];
    }
    if ( ! is_writable( $dir ) ) {
        return [ 'status' => 'fail', 'message' => 'Existe mas não-writable.' ];
    }
    return [ 'status' => 'pass', 'message' => 'OK.' ];
}


// ============================================================================
// TESTES — NOTIFICAÇÕES
// ============================================================================

function rmbz_smoke_test_notif_templates() {
    // API real definida em notifications-center.php:85
    if ( function_exists( 'rmbz_notifications_keys_list' ) ) {
        $keys = rmbz_notifications_keys_list();
    } elseif ( function_exists( 'rmbz_notifications_keys_meta' ) ) {
        $meta = rmbz_notifications_keys_meta();
        $keys = is_array( $meta ) ? array_keys( $meta ) : [];
    } else {
        return [ 'status' => 'fail', 'message' => 'Centro de Notificações não carregado.' ];
    }
    $n = is_array( $keys ) ? count( $keys ) : 0;
    if ( $n < 11 ) {
        return [ 'status' => 'warn', 'message' => 'Apenas ' . $n . ' templates (esperados ≥11 desde 1.5.144).' ];
    }
    return [ 'status' => 'pass', 'message' => $n . ' templates registados.' ];
}

function rmbz_smoke_test_notif_email_logs() {
    global $wpdb;
    $t = $wpdb->prefix . 'rmbz_email_logs';
    if ( $wpdb->get_var( "SHOW TABLES LIKE '$t'" ) !== $t ) {
        return [ 'status' => 'warn', 'message' => 'Tabela não criada (created on first email send).' ];
    }
    $count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM $t" );
    return [ 'status' => 'pass', 'message' => $count . ' emails logados.' ];
}


// ============================================================================
// TESTES — CATÁLOGO DSP
// ============================================================================

function rmbz_smoke_test_catalog_dsp_count() {
    if ( ! function_exists( 'rmbz_distro_dsp_catalog' ) ) {
        return [ 'status' => 'fail', 'message' => 'Função ausente.' ];
    }
    $cat = rmbz_distro_dsp_catalog( true ); // só activos
    $n = is_array( $cat ) ? count( $cat ) : 0;
    if ( $n < 10 ) {
        return [ 'status' => 'warn', 'message' => 'Apenas ' . $n . ' DSPs activos.' ];
    }
    return [ 'status' => 'pass', 'message' => $n . ' DSPs activos.' ];
}

function rmbz_smoke_test_catalog_dsp_tier1() {
    if ( ! function_exists( 'rmbz_distro_dsp_catalog' ) ) {
        return [ 'status' => 'fail', 'message' => 'Função ausente.' ];
    }
    $cat = rmbz_distro_dsp_catalog( true );
    if ( ! is_array( $cat ) ) {
        return [ 'status' => 'fail', 'message' => 'Catálogo inválido.' ];
    }
    $tier1 = array_filter( $cat, function( $meta ) {
        return ( (int) ( $meta['tier'] ?? 0 ) === 1 );
    } );
    $n = count( $tier1 );
    if ( $n < 6 ) {
        return [ 'status' => 'warn', 'message' => 'Apenas ' . $n . ' DSPs Tier 1 (esperado ≥6).' ];
    }
    return [ 'status' => 'pass', 'message' => $n . ' DSPs Tier 1 (Spotify, Apple Music, etc.).' ];
}


// ============================================================================
// HANDLER POST + INTEGRAÇÃO NA PÁGINA PRÉ-DEPLOY
// ============================================================================

/**
 * Handler do POST "Correr Smoke Tests".
 * Guarda resultados em transient para mostrar na página.
 */
function rmbz_smoke_handle_run() {
    if ( ! current_user_can( 'manage_options' ) ) wp_die( 'Sem permissão.' );
    if ( ! check_admin_referer( 'rmbz_smoke_run' ) ) wp_die( 'Falha de segurança.' );

    $output = rmbz_smoke_run_all();
    set_transient( 'rmbz_smoke_last_run', $output, HOUR_IN_SECONDS );
    set_transient( 'rmbz_predeploy_notice', [
        'type' => $output['summary']['fail'] === 0 ? 'success' : 'error',
        'msg'  => sprintf(
            '🧪 Smoke tests concluídos: %d/%d PASS · %d FAIL · %d WARN · %sms',
            $output['summary']['pass'],
            $output['summary']['total'],
            $output['summary']['fail'],
            $output['summary']['warn'],
            $output['summary']['total_ms']
        ),
    ], 30 );

    wp_safe_redirect( admin_url( 'admin.php?page=rmbz-predeploy#smoke-results' ) );
    exit;
}
add_action( 'admin_post_rmbz_smoke_run', 'rmbz_smoke_handle_run' );


/**
 * Render do painel de smoke tests dentro da página Pré-Deploy.
 * Chamado via hook `rmbz_predeploy_after_health` (que vamos adicionar).
 */
function rmbz_smoke_render_panel() {
    $last = get_transient( 'rmbz_smoke_last_run' );

    $colors = [
        'pass' => [ 'var(--rmbz-success, #10b981)', '✓' ],
        'warn' => [ 'var(--rmbz-warning, #f59e0b)', '⚠' ],
        'fail' => [ 'var(--rmbz-danger-strong, #dc2626)', '✗' ],
    ];
    ?>
    <div class="card" id="smoke-results" style="padding:18px; max-width:none; margin-top:20px;">
        <h2 style="margin-top:0;">🧪 Smoke Tests</h2>
        <p style="color:var(--rmbz-text-muted, #666);">
            Suite rápida (<10s) que confirma que componentes essenciais do plugin
            estão a funcionar. <strong>Não-destrutivo</strong> — só lê dados,
            não escreve nada. Recomendado correr depois de cada update.
        </p>

        <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" style="margin:14px 0;">
            <input type="hidden" name="action" value="rmbz_smoke_run">
            <?php wp_nonce_field( 'rmbz_smoke_run' ); ?>
            <button type="submit" class="button button-primary">
                🧪 Correr Smoke Tests Agora
            </button>
            <?php if ( $last && ! empty( $last['summary']['ts'] ) ): ?>
                <span style="margin-left:12px; color:var(--rmbz-text-muted, #888); font-size:12px;">
                    Última execução: <?php echo esc_html( $last['summary']['ts'] ); ?>
                </span>
            <?php endif; ?>
        </form>

        <?php if ( $last && ! empty( $last['results'] ) ):
            $s = $last['summary'];
        ?>
            <div style="margin-top:18px; padding:14px; background:var(--rmbz-bg-elevated, #f9f9f9); border-radius:6px; border-left:4px solid <?php echo $s['fail'] === 0 ? 'var(--rmbz-success, #10b981)' : 'var(--rmbz-danger-strong, #dc2626)'; ?>;">
                <strong style="font-size:15px;">
                    <?php if ( $s['fail'] === 0 ): ?>
                        ✅ <?php echo (int) $s['pass']; ?>/<?php echo (int) $s['total']; ?> PASS
                    <?php else: ?>
                        ❌ <?php echo (int) $s['fail']; ?> FAIL · <?php echo (int) $s['pass']; ?>/<?php echo (int) $s['total']; ?> PASS
                    <?php endif; ?>
                </strong>
                <?php if ( $s['warn'] > 0 ): ?>
                    · <span style="color:var(--rmbz-warning, #f59e0b);">⚠ <?php echo (int) $s['warn']; ?> WARN</span>
                <?php endif; ?>
                <span style="color:var(--rmbz-text-muted, #888); margin-left:8px;">
                    · <?php echo (float) $s['total_ms']; ?>ms total
                </span>
            </div>

            <?php
            // Agrupar por categoria
            $by_cat = [];
            foreach ( $last['results'] as $r ) {
                $by_cat[ $r['category'] ][] = $r;
            }
            ?>
            <?php foreach ( $by_cat as $cat => $tests ): ?>
                <h3 style="margin:18px 0 6px; font-size:14px; color:var(--rmbz-text-secondary, #555);">
                    <?php echo esc_html( $cat ); ?>
                    <span style="font-weight:normal; color:var(--rmbz-text-muted, #888); font-size:12px;">
                        (<?php echo count( $tests ); ?>)
                    </span>
                </h3>
                <table class="widefat striped" style="font-size:13px;">
                    <tbody>
                    <?php foreach ( $tests as $r ):
                        $info = $colors[ $r['status'] ] ?? $colors['warn'];
                    ?>
                        <tr>
                            <td width="40" style="text-align:center; font-size:18px; color:<?php echo esc_attr( $info[0] ); ?>;">
                                <?php echo esc_html( $info[1] ); ?>
                            </td>
                            <td width="380"><?php echo esc_html( $r['name'] ); ?></td>
                            <td style="color:var(--rmbz-text-muted, #555); font-size:12px;">
                                <?php echo esc_html( $r['message'] ); ?>
                            </td>
                            <td width="60" style="text-align:right; color:var(--rmbz-text-muted, #888); font-family:monospace; font-size:11px;">
                                <?php echo (float) $r['duration_ms']; ?>ms
                            </td>
                        </tr>
                    <?php endforeach; ?>
                    </tbody>
                </table>
            <?php endforeach; ?>
        <?php else: ?>
            <p style="color:var(--rmbz-text-muted, #888); margin-top:18px;">
                Sem execuções registadas ainda. Carrega no botão acima para começar.
            </p>
        <?php endif; ?>
    </div>
    <?php
}
add_action( 'rmbz_predeploy_after_health', 'rmbz_smoke_render_panel' );
