<?php
// includes/distribution/distribution-analytics.php
//
// Dashboard de analytics da distribuição digital.
// Funcionalidades:
//   - Selector de período (7d / 30d / 90d / 12m) com agregação auto-adaptada
//   - KPIs com comparação vs período anterior (delta %)
//   - Top faixas por track_id (com sparkline de 30 dias em batch query)
//   - Top plataformas (DSPs) com percentagens
//   - Top países com bandeiras
//   - Gráfico principal dual-axis (streams + ganhos)
//   - Drill-down por faixa em modal (AJAX)
//   - Export CSV do período seleccionado
//
// Requisitos: distribution-schema-patch.php aplicado (tabela daily_stats
// com coluna track_id) e import-processor.php a popular os dados.

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

/* =============================================================================
 * HELPERS
 * ============================================================================= */

/**
 * Retorna o intervalo de datas para o período pedido.
 *
 * Suporta 'all' (lifetime) — neste caso precisa de $telemovel para descobrir
 * o min/max real de report_date na tabela daily_stats. Bucket é adaptativo:
 *   > 365 dias → mensal
 *   > 90  dias → semanal
 *   senão       → diário
 * Para 'all' não há período anterior comparável → prev_from/prev_to ficam
 * iguais a from/to (delta vai dar 0% — caller deve esconder o pill).
 *
 * @return array [ 'from' => 'Y-m-d', 'to' => 'Y-m-d', 'prev_from' => ..., 'prev_to' => ..., 'bucket' => 'day'|'week'|'month' ]
 */
function rmbz_analytics_period_range( $period = '30d', $telemovel = '' ) {
    $today = new DateTime( current_time('Y-m-d') );
    $to    = clone $today;

    // ──────────────────────────────────────────────────────────────────
    // PERÍODO 'all' — lifetime do artista. Vai à BD descobrir o spread real.
    // Se o artista não tem dados, fallback para 30d para a UI não rebentar.
    // ──────────────────────────────────────────────────────────────────
    if ( $period === 'all' ) {
        global $wpdb;
        $t_daily = $wpdb->prefix . 'rmbz_distro_daily_stats';
        $min_date = null;
        $max_date = null;

        if ( ! empty($telemovel) && $wpdb->get_var("SHOW TABLES LIKE '$t_daily'") === $t_daily ) {
            $cols = (array) $wpdb->get_col("SHOW COLUMNS FROM $t_daily");
            $pa_clause = in_array('pending_approval', $cols, true) ? " AND pending_approval = 0" : "";
            $bounds = $wpdb->get_row( $wpdb->prepare(
                "SELECT MIN(report_date) AS min_d, MAX(report_date) AS max_d
                 FROM $t_daily WHERE artist_telemovel = %s $pa_clause",
                $telemovel
            ) );
            if ( $bounds && $bounds->min_d && $bounds->max_d ) {
                $min_date = $bounds->min_d;
                $max_date = $bounds->max_d;
            }
        }

        // Fallback: artista sem dados → usa últimos 30 dias para não quebrar a UI
        if ( empty($min_date) || empty($max_date) ) {
            $from      = (clone $today)->modify("-29 days");
            return [
                'from'      => $from->format('Y-m-d'),
                'to'        => $to->format('Y-m-d'),
                'prev_from' => $from->format('Y-m-d'),
                'prev_to'   => $to->format('Y-m-d'),
                'bucket'    => 'day',
                'days'      => 30,
                'is_all'    => true,
            ];
        }

        $from = new DateTime($min_date);
        $to   = new DateTime($max_date);
        $days_span = ((int) $from->diff($to)->days) + 1;

        // Bucket adaptativo — evita 1825 pontos no gráfico para 5 anos
        if ( $days_span > 365 )      $bucket = 'month';
        elseif ( $days_span > 90 )   $bucket = 'week';
        else                         $bucket = 'day';

        return [
            'from'      => $from->format('Y-m-d'),
            'to'        => $to->format('Y-m-d'),
            // Não há período anterior para 'all' — apontamos para o mesmo,
            // os deltas vão dar 0% e o caller esconde o pill.
            'prev_from' => $from->format('Y-m-d'),
            'prev_to'   => $to->format('Y-m-d'),
            'bucket'    => $bucket,
            'days'      => $days_span,
            'is_all'    => true,
        ];
    }

    switch ( $period ) {
        case '7d':
            $days = 7;  $bucket = 'day';  break;
        case '90d':
            $days = 90; $bucket = 'week'; break;
        case '12m':
            $days = 365; $bucket = 'month'; break;
        case '30d':
        default:
            $days = 30; $bucket = 'day'; break;
    }

    $from      = (clone $today)->modify("-" . ($days - 1) . " days");
    $prev_to   = (clone $from)->modify('-1 day');
    $prev_from = (clone $prev_to)->modify("-" . ($days - 1) . " days");

    return [
        'from'      => $from->format('Y-m-d'),
        'to'        => $to->format('Y-m-d'),
        'prev_from' => $prev_from->format('Y-m-d'),
        'prev_to'   => $prev_to->format('Y-m-d'),
        'bucket'    => $bucket,
        'days'      => $days,
        'is_all'    => false,
    ];
}

/**
 * Calcula a variação percentual entre dois valores. Lida com 0 na base.
 */
function rmbz_analytics_delta( $current, $previous ) {
    if ( $previous == 0 ) {
        return $current > 0 ? [ 'pct' => 100, 'direction' => 'up' ] : [ 'pct' => 0, 'direction' => 'flat' ];
    }
    $pct = (($current - $previous) / $previous) * 100;
    return [
        'pct'       => round( abs($pct), 1 ),
        'direction' => $pct > 0.5 ? 'up' : ($pct < -0.5 ? 'down' : 'flat'),
    ];
}

/**
 * Mapa simples de código ISO → bandeira emoji. Fallback para globo.
 */
function rmbz_country_flag( $code ) {
    $code = strtoupper( trim($code) );
    if ( strlen($code) !== 2 ) return '🌍';
    $codepoints = [];
    foreach ( str_split($code) as $ch ) {
        $codepoints[] = 127397 + ord($ch);
    }
    // mb_chr pode não existir em PHP <7.2
    if ( function_exists('mb_chr') ) {
        return mb_chr($codepoints[0], 'UTF-8') . mb_chr($codepoints[1], 'UTF-8');
    }
    return '🌍';
}

/**
 * Verifica se uma tabela existe, para falhar com gracia se o schema-patch não correu.
 */
function rmbz_analytics_table_exists( $table ) {
    global $wpdb;
    $found = $wpdb->get_var( $wpdb->prepare( "SHOW TABLES LIKE %s", $table ) );
    return $found === $table;
}

/* =============================================================================
 * [FEAT 1.5.16] EMPTY STATE EDUCATIVO
 * -----------------------------------------------------------------------------
 * Quando um artista abre "Tendências & Análises" e ainda não há dados
 * importados para ele, mostramos uma explicação transparente em vez de um
 * dashboard vazio com zeros (que confunde e parece bug).
 *
 * Explicamos o ciclo da indústria: DSPs (Spotify, Apple Music) reportam
 * com 2-3 meses de atraso — é normal, não é falha do distribuidor. Também
 * mostramos uma timeline visual do estado atual do artista: se tem 0
 * releases, convidar a criar; se tem release ainda não live, explicar que
 * não há dados até estar publicado; se já tem release live há X semanas,
 * calcular quando se espera o primeiro relatório.
 * ============================================================================= */
function rmbz_render_analytics_empty_state( $user_telemovel, $artist_releases, $live_releases, $last_import = null ) {
    $any_release = count( $artist_releases );
    $live_count  = count( $live_releases );

    // Calcular há quanto tempo o release mais antigo está live
    $oldest_live_days = null;
    if ( $live_count > 0 ) {
        $oldest_live = end( $live_releases ); // array ordenado desc → último é o mais antigo
        if ( $oldest_live && ! empty($oldest_live->live_date) ) {
            $oldest_live_days = (int) floor(
                ( current_time('timestamp') - strtotime($oldest_live->live_date) ) / 86400
            );
        }
    }

    ?>
    <div class="distro-analytics-container rmbz-fullbleed">

        <?php /* [1.5.171] Botão de voltar para o dashboard — utilizador chegou
               aqui via "Ver Tendências" mas não tinha forma de voltar. */ ?>
        <div style="margin-bottom:16px;">
            <a href="?distro_action=dashboard"
               style="display:inline-flex; align-items:center; gap:6px; padding:8px 14px; background:var(--rmbz-bg-surface, #1a1a1a); border:1px solid #333; border-radius:6px; color:var(--rmbz-text-muted, #aaa); text-decoration:none; font-size:13px; transition:all 0.15s ease;"
               onmouseover="this.style.borderColor='#f0b90b';this.style.color='#f0b90b';"
               onmouseout="this.style.borderColor='#333';this.style.color='#aaa';">
                ← Voltar ao painel
            </a>
        </div>

        <div class="analytics-header" style="display:flex; justify-content:space-between; align-items:flex-start; flex-wrap:wrap; gap:20px; margin-bottom:30px;">
            <div>
                <h1 style="color:var(--rmbz-text-primary, #fff); margin:0; font-size:28px;">Tendências & Análises</h1>
                <p style="color:var(--rmbz-text-faint, #888); font-size:13px; margin:5px 0 0 0;">Relatórios de streams e ganhos das plataformas.</p>
            </div>
        </div>

        <!-- CARTAZ PRINCIPAL -->
        <div style="background:linear-gradient(135deg, #1a1a1a 0%, #141414 100%); border:1px solid #2a2a2a; border-radius:14px; padding:40px 32px; text-align:center; margin-bottom:28px;">
            <div style="font-size:56px; line-height:1; margin-bottom:16px;">📊</div>
            <h2 style="color:var(--rmbz-text-primary, #fff); margin:0 0 10px 0; font-size:22px; font-weight:700;">
                Ainda sem relatórios disponíveis
            </h2>
            <p style="color:var(--rmbz-text-secondary, #bbb); font-size:14px; line-height:1.6; max-width:560px; margin:0 auto 20px auto;">
                <?php if ( $any_release === 0 ): ?>
                    Ainda não tem nenhum lançamento distribuído. Assim que publicar a sua primeira música, os dados começarão a aparecer aqui <strong>cerca de 2-3 meses depois</strong> (é o tempo que os DSPs precisam para processar e reportar).
                <?php elseif ( $live_count === 0 ): ?>
                    Tem <?php echo $any_release; ?> lançamento<?php echo $any_release > 1 ? 's' : ''; ?> no sistema, mas nenhum está ainda ao vivo nas plataformas. Assim que a equipa 1MZ completar a distribuição, começa a contagem: <strong>2-3 meses depois</strong> veremos os primeiros números aqui.
                <?php elseif ( $oldest_live_days !== null && $oldest_live_days < 60 ): ?>
                    O seu lançamento mais antigo está ao vivo há <strong><?php echo $oldest_live_days; ?> dia<?php echo $oldest_live_days === 1 ? '' : 's'; ?></strong>. Os DSPs (Spotify, Apple Music, etc.) demoram tipicamente <strong>2-3 meses</strong> a enviar o primeiro relatório. Isto é normal na indústria e aplica-se a todos os distribuidores.
                <?php elseif ( $oldest_live_days !== null && $oldest_live_days < 120 ): ?>
                    O seu lançamento mais antigo está ao vivo há <strong><?php echo $oldest_live_days; ?> dias</strong>. Estamos a chegar perto — o primeiro relatório costuma chegar entre 60-90 dias após o lançamento. Assim que a equipa 1MZ importar os relatórios mensais dos DSPs, os dados aparecem aqui automaticamente.
                <?php else: ?>
                    Tem <?php echo $live_count; ?> lançamento<?php echo $live_count > 1 ? 's' : ''; ?> ao vivo há já algum tempo. Os relatórios dos DSPs ainda não foram processados pela equipa 1MZ para o seu catálogo. Se julgar que isto não está correto, contacte-nos pela área de notificações.
                <?php endif; ?>
            </p>

            <?php if ( $any_release === 0 ): ?>
                <a href="?distro_action=wizard" style="display:inline-block; background:var(--rmbz-brand, #f0b90b); color:#000; padding:12px 28px; border-radius:6px; text-decoration:none; font-weight:700; font-size:14px;">
                    Criar primeiro lançamento →
                </a>
            <?php endif; ?>
        </div>

        <!-- TIMELINE: COMO FUNCIONA O CICLO -->
        <div style="background:var(--rmbz-bg-elevated, #141414); border:1px solid #2a2a2a; border-radius:10px; padding:28px; margin-bottom:28px;">
            <h3 style="color:var(--rmbz-text-primary, #fff); margin:0 0 20px 0; font-size:16px;">🔄 Como funciona o ciclo dos relatórios</h3>

            <div class="rmbz-as-timeline">
                <div class="rmbz-as-step">
                    <div class="rmbz-as-step-icon" style="background:rgba(46,204,113,0.15); border-color:var(--rmbz-success, #10b981); color:var(--rmbz-success, #10b981);">1</div>
                    <div class="rmbz-as-step-body">
                        <strong style="color:var(--rmbz-text-primary, #fff);">Você lança a música</strong>
                        <p>Envia pelo wizard. A equipa 1MZ valida e distribui para Spotify, Apple Music, YouTube Music, TikTok, Amazon e +100 DSPs.</p>
                    </div>
                </div>

                <div class="rmbz-as-step">
                    <div class="rmbz-as-step-icon" style="background:rgba(240,185,11,0.15); border-color:var(--rmbz-brand, #f0b90b); color:var(--rmbz-brand, #f0b90b);">2</div>
                    <div class="rmbz-as-step-body">
                        <strong style="color:var(--rmbz-text-primary, #fff);">Dias depois: música fica ao vivo</strong>
                        <p>O seu lançamento aparece nas plataformas e começa a receber streams. Os DSPs registam cada reprodução em tempo real nos seus sistemas internos.</p>
                    </div>
                </div>

                <div class="rmbz-as-step">
                    <div class="rmbz-as-step-icon" style="background:rgba(52,152,219,0.15); border-color:var(--rmbz-info, #3b82f6); color:var(--rmbz-info, #3b82f6);">3</div>
                    <div class="rmbz-as-step-body">
                        <strong style="color:var(--rmbz-text-primary, #fff);">1-3 meses depois: DSPs enviam relatório</strong>
                        <p>Cada DSP compila um relatório mensal com streams e ganhos. Spotify tipicamente envia 45-60 dias após o fim do mês. Apple Music e outros podem chegar aos 90 dias.</p>
                    </div>
                </div>

                <div class="rmbz-as-step">
                    <div class="rmbz-as-step-icon" style="background:rgba(155,89,182,0.15); border-color:var(--rmbz-accent, #a855f7); color:var(--rmbz-accent, #a855f7);">4</div>
                    <div class="rmbz-as-step-body">
                        <strong style="color:var(--rmbz-text-primary, #fff);">1MZ importa e disponibiliza</strong>
                        <p>Assim que os relatórios chegam, a equipa 1MZ processa os CSVs (faixas identificadas pelo ISRC) e credita os ganhos. Os dados aparecem imediatamente aqui.</p>
                    </div>
                </div>

                <div class="rmbz-as-step">
                    <div class="rmbz-as-step-icon" style="background:rgba(231,76,60,0.15); border-color:var(--rmbz-danger, #ef4444); color:var(--rmbz-danger, #ef4444);">5</div>
                    <div class="rmbz-as-step-body">
                        <strong style="color:var(--rmbz-text-primary, #fff);">Você vê e pode levantar</strong>
                        <p>Gráficos, KPIs, top-faixas, top-países. E o dinheiro fica disponível para levantamento via M-Pesa ou E-Mola.</p>
                    </div>
                </div>
            </div>
        </div>

        <!-- INFO DO ÚLTIMO IMPORT GLOBAL (se existir) -->
        <?php /* [1.5.132] Simplificado: só mostra a data do último import.
            Antes mostrava o nome do ficheiro CSV (ex: analysis_xxx.csv) e
            um parágrafo sobre "se a música não está incluída" que era
            informação técnica/operacional sem valor para o artista —
            mais confusão do que ajuda. Mantemos só o sinal claro: "última
            actualização foi em X". */ ?>
        <?php if ( $last_import && ! empty($last_import['date']) ): ?>
        <div style="background:var(--rmbz-bg-elevated, #141414); border:1px solid #2a2a2a; border-left:3px solid #3498db; padding:14px 18px; border-radius:6px; margin-bottom:20px; display:flex; align-items:center; gap:12px;">
            <span style="font-size:20px;">📥</span>
            <div style="flex:1; min-width:0;">
                <strong style="color:var(--rmbz-text-primary, #fff); font-size:13px;">Último relatório importado</strong>
                <p style="color:var(--rmbz-text-muted, #aaa); font-size:12px; margin:2px 0 0 0;">
                    <?php echo esc_html( date_i18n('j \d\e F \d\e Y', strtotime($last_import['date'])) ); ?>
                </p>
            </div>
        </div>
        <?php endif; ?>

        <!-- AÇÃO: voltar ao dashboard -->
        <div style="text-align:center; margin-top:30px;">
            <a href="?distro_action=dashboard" style="color:var(--rmbz-text-muted, #aaa); text-decoration:none; font-size:13px; border:1px solid #333; padding:10px 18px; border-radius:6px; display:inline-block;">
                ← Voltar ao painel
            </a>
        </div>

        <style>
        .rmbz-as-timeline { display:flex; flex-direction:column; gap:18px; }
        .rmbz-as-step { display:grid; grid-template-columns:40px 1fr; gap:14px; align-items:start; }
        .rmbz-as-step-icon { width:36px; height:36px; border-radius:50%; border:2px solid; display:flex; align-items:center; justify-content:center; font-weight:bold; font-size:14px; flex-shrink:0; }
        .rmbz-as-step-body { padding-top:3px; }
        .rmbz-as-step-body strong { display:block; font-size:14px; margin-bottom:4px; }
        .rmbz-as-step-body p { color:var(--rmbz-text-muted, #aaa); font-size:13px; margin:0; line-height:1.55; }
        </style>

    </div>
    <?php
}

/* =============================================================================
 * VIEW PRINCIPAL
 * ============================================================================= */
function rmbz_render_analytics_view( $user_telemovel ) {
    global $wpdb;

    // [1.5.229] FASE 1c — Se vier ?import_id, mostra a página de
    // detalhe desse relatório em vez da vista geral.
    if ( isset( $_GET['import_id'] ) && (int) $_GET['import_id'] > 0
         && function_exists( 'rmbz_analytics_render_import_detail_page' ) ) {
        rmbz_analytics_render_import_detail_page(
            $user_telemovel, (int) $_GET['import_id']
        );
        return;
    }

    // [1.5.230] FASE 1d — Se vier ?release_stats, mostra o relatório
    // de ganhos desse lançamento.
    if ( isset( $_GET['release_stats'] ) && (int) $_GET['release_stats'] > 0
         && function_exists( 'rmbz_analytics_render_release_detail_page' ) ) {
        rmbz_analytics_render_release_detail_page(
            $user_telemovel, (int) $_GET['release_stats']
        );
        return;
    }

    $table_stats    = $wpdb->prefix . 'rmbz_distro_stats';
    $table_daily    = $wpdb->prefix . 'rmbz_distro_daily_stats';
    $table_tracks   = $wpdb->prefix . 'rmbz_distro_tracks';
    $table_releases = $wpdb->prefix . 'rmbz_distro_releases';

    // Fallback se o schema-patch não correu — tentamos correr na hora.
    if ( ! rmbz_analytics_table_exists( $table_daily ) ) {
        // Forçar a criação das tabelas em falta (não depende de admin_init)
        if ( function_exists('rmbz_distro_install_missing_tables') ) {
            // Reset da versão para que o trigger volte a correr o dbDelta
            update_option( 'rmbz_db_version', '0' );
            rmbz_distro_install_missing_tables();
            update_option( 'rmbz_db_version', '1.6' );
        }

        // Re-verificar
        if ( ! rmbz_analytics_table_exists( $table_daily ) ) {
            echo '<div class="distro-analytics-container rmbz-fullbleed"><div style="background:var(--rmbz-bg-surface, #1a1a1a); border:1px solid #e74c3c; padding:30px; border-radius:8px; color:var(--rmbz-text-secondary, #ccc);">
                    <h2 style="color:var(--rmbz-danger, #ef4444); margin-top:0;">⚠️ Analytics indisponível</h2>
                    <p>Não foi possível criar a tabela de estatísticas. Verifique se o utilizador da base de dados tem permissões CREATE TABLE, ou peça ao administrador para visitar uma página do wp-admin.</p>
                  </div></div>';
            return;
        }
    }

    // ---- 1. Período ----
    // [1.5.231] A vista geral mostra sempre TUDO (sem pills). O CSV
    // continua a poder receber ?period=X via query string.
    $allowed_periods = ['7d','30d','90d','12m','all'];
    $period = 'all';
    $range = rmbz_analytics_period_range( $period, $user_telemovel );

    // ---- 1.5. [FEAT 1.5.16] Metadata de reporting (transparência) ----
    // Queremos 2 respostas:
    //   (a) O artista TEM algum dado no sistema? (MAX/COUNT em daily_stats)
    //   (b) Qual é o último relatório que cobre a sua música e quando foi
    //       importado? (para banner "último relatório: X")
    // Também buscamos info sobre releases live para calcular quando se
    // podem esperar os primeiros dados (2-3 meses após lançamento).
    $pa_filter_meta = function_exists('rmbz_approval_clause')
        ? rmbz_approval_clause( $table_daily )
        : '';

    $reporting_meta = $wpdb->get_row( $wpdb->prepare(
        "SELECT
            COUNT(*) AS rows_total,
            MAX(report_date) AS latest_report_date,
            MIN(report_date) AS earliest_report_date
         FROM $table_daily
         WHERE artist_telemovel = %s $pa_filter_meta",
        $user_telemovel
    ) );
    $has_any_data = $reporting_meta && (int) $reporting_meta->rows_total > 0;
    $latest_report_date   = $has_any_data ? $reporting_meta->latest_report_date   : null;
    $earliest_report_date = $has_any_data ? $reporting_meta->earliest_report_date : null;

    // Último import global (do wp_options — rmbz_import_log)
    $import_log = get_option( 'rmbz_import_log', [] );
    $last_import = ! empty($import_log) ? end( $import_log ) : null;

    // Releases do artista (para o empty state ou para dizer quantas estão a gerar dados)
    $artist_releases = $wpdb->get_results( $wpdb->prepare(
        "SELECT id, title, status, created_at,
                COALESCE(live_at, submitted_at, created_at) AS live_date
         FROM $table_releases
         WHERE artist_telemovel = %s
         ORDER BY COALESCE(live_at, submitted_at, created_at) DESC",
        $user_telemovel
    ) );
    if ( ! is_array($artist_releases) ) $artist_releases = [];

    $live_releases = array_filter( $artist_releases, fn($r) => in_array($r->status, ['live','distribuido','distributed'], true) );
    $any_release   = count( $artist_releases );
    $live_count    = count( $live_releases );

    // =========================================================================
    // EMPTY STATE EDUCATIVO — quando o artista ainda não tem dados
    // =========================================================================
    // Em vez de mostrar o dashboard com zeros (que confunde e parece bug),
    // explicamos ao artista o ciclo normal da indústria: DSPs reportam com
    // 2-3 meses de atraso e só recebem relatório por mês.
    if ( ! $has_any_data ) {
        rmbz_render_analytics_empty_state(
            $user_telemovel,
            $artist_releases,
            $live_releases,
            $last_import
        );
        return;
    }
    // =========================================================================

    // ---- 2. KPIs: totais histórico + período actual + período anterior ----
    $stats_total = $wpdb->get_row( $wpdb->prepare(
        "SELECT total_earnings, total_streams FROM $table_stats WHERE artist_telemovel = %s",
        $user_telemovel
    ) );
    $lifetime_earnings = $stats_total ? (float)$stats_total->total_earnings : 0.0;
    $lifetime_streams  = $stats_total ? (int)  $stats_total->total_streams  : 0;

    // Filtro de aprovação (retira importações ainda pendentes de aprovação)
    $pa_filter = function_exists('rmbz_approval_clause')
        ? rmbz_approval_clause( $table_daily )
        : '';

    $period_now = $wpdb->get_row( $wpdb->prepare(
        "SELECT COALESCE(SUM(streams),0) s, COALESCE(SUM(sales),0) e
         FROM $table_daily
         WHERE artist_telemovel = %s AND report_date BETWEEN %s AND %s $pa_filter",
        $user_telemovel, $range['from'], $range['to']
    ) );
    $period_prev = $wpdb->get_row( $wpdb->prepare(
        "SELECT COALESCE(SUM(streams),0) s, COALESCE(SUM(sales),0) e
         FROM $table_daily
         WHERE artist_telemovel = %s AND report_date BETWEEN %s AND %s $pa_filter",
        $user_telemovel, $range['prev_from'], $range['prev_to']
    ) );

    $streams_now  = $period_now  ? (int)  $period_now->s  : 0;
    $earnings_now = $period_now  ? (float)$period_now->e  : 0.0;
    $streams_prev = $period_prev ? (int)  $period_prev->s : 0;
    $earn_prev    = $period_prev ? (float)$period_prev->e : 0.0;

    $d_streams  = rmbz_analytics_delta( $streams_now,  $streams_prev );
    $d_earnings = rmbz_analytics_delta( $earnings_now, $earn_prev );

    // ---- 3. Série temporal agregada pelo bucket ----
    // BUG CRÍTICO CORRIGIDO: os tokens DATE_FORMAT (%Y, %m, %d) eram interpretados
    // pelo $wpdb->prepare como placeholders, causando o aviso
    // "wpdb::prepare foi chamada incorretamente" e potencialmente quebrando a
    // query no MySQL. Solução: escapar como %% que o prepare decodifica para %
    // literal antes de enviar ao MySQL.
    $bucket_expr = [
        'day'   => "DATE_FORMAT(report_date, '%%Y-%%m-%%d')",
        'week'  => "DATE_FORMAT(DATE_SUB(report_date, INTERVAL WEEKDAY(report_date) DAY), '%%Y-%%m-%%d')",
        'month' => "DATE_FORMAT(report_date, '%%Y-%%m-01')",
    ][ $range['bucket'] ];

    $series = $wpdb->get_results( $wpdb->prepare(
        "SELECT $bucket_expr AS bucket, SUM(streams) AS s, SUM(sales) AS e
         FROM $table_daily
         WHERE artist_telemovel = %s AND report_date BETWEEN %s AND %s $pa_filter
         GROUP BY bucket
         ORDER BY bucket ASC",
        $user_telemovel, $range['from'], $range['to']
    ) );

    // Preencher buckets vazios (importante para o gráfico ficar contínuo)
    $series_filled = rmbz_fill_empty_buckets( $series, $range );

    $chart_labels   = array_map( function($row) use ($range) {
        if ( $range['bucket'] === 'month' ) return date('M/y', strtotime($row->bucket));
        if ( $range['bucket'] === 'week' )  return 'sem ' . date('d/m', strtotime($row->bucket));
        return date('d/m', strtotime($row->bucket));
    }, $series_filled );
    $chart_streams  = array_map( fn($r) => (int)$r->s,   $series_filled );
    $chart_earnings = array_map( fn($r) => round((float)$r->e, 2), $series_filled );

    // ---- 4. TOP FAIXAS (por track_id) com sparkline ----
    $top_tracks = $wpdb->get_results( $wpdb->prepare(
        "SELECT
            t.id           AS track_id,
            t.track_title,
            r.title        AS release_title,
            r.cover_image,
            r.id           AS release_id,
            SUM(d.streams) AS streams,
            SUM(d.sales)   AS earnings
         FROM $table_daily d
         JOIN $table_tracks   t ON d.track_id = t.id
         JOIN $table_releases r ON t.release_id = r.id
         WHERE d.artist_telemovel = %s
           AND d.report_date BETWEEN %s AND %s
           AND d.track_id IS NOT NULL
           AND ( d.pending_approval IS NULL OR d.pending_approval = 0 )
         GROUP BY t.id
         ORDER BY streams DESC
         LIMIT 10",
        $user_telemovel, $range['from'], $range['to']
    ) );

    // Sparklines — query única para todos os top tracks (batch)
    $sparklines = [];
    if ( ! empty($top_tracks) ) {
        $track_ids = array_map( fn($t) => (int)$t->track_id, $top_tracks );
        $placeholders = implode(',', array_fill(0, count($track_ids), '%d'));
        $spark_from = (new DateTime($range['to']))->modify('-29 days')->format('Y-m-d');

        $rows = $wpdb->get_results( $wpdb->prepare(
            "SELECT track_id, report_date, SUM(streams) s
             FROM $table_daily
             WHERE artist_telemovel = %s
               AND report_date BETWEEN %s AND %s
               AND track_id IN ($placeholders) $pa_filter
             GROUP BY track_id, report_date
             ORDER BY report_date ASC",
            array_merge( [ $user_telemovel, $spark_from, $range['to'] ], $track_ids )
        ) );

        // Agrupar por track_id
        $by_track = [];
        if ( is_array($rows) ) {
            foreach ( $rows as $r ) {
                $by_track[ (int)$r->track_id ][ $r->report_date ] = (int)$r->s;
            }
        }

        // Criar array de 30 valores por track (preenchendo gaps com 0)
        foreach ( $track_ids as $tid ) {
            $arr = [];
            $d = new DateTime( $spark_from );
            for ( $i = 0; $i < 30; $i++ ) {
                $arr[] = $by_track[ $tid ][ $d->format('Y-m-d') ] ?? 0;
                $d->modify('+1 day');
            }
            $sparklines[ $tid ] = $arr;
        }
    }

    // ---- 5. Top plataformas (lojas / DSPs) ----
    $top_stores = $wpdb->get_results( $wpdb->prepare(
        "SELECT store, SUM(streams) s, SUM(sales) e
         FROM $table_daily
         WHERE artist_telemovel = %s AND report_date BETWEEN %s AND %s $pa_filter
         GROUP BY store
         ORDER BY s DESC
         LIMIT 8",
        $user_telemovel, $range['from'], $range['to']
    ) );
    if ( ! is_array($top_stores) ) $top_stores = [];
    $store_total_streams = array_sum( array_map( fn($s) => (int)$s->s, $top_stores ) );

    // ---- 6. Top países ----
    $top_countries = $wpdb->get_results( $wpdb->prepare(
        "SELECT country_code, SUM(streams) s, SUM(sales) e
         FROM $table_daily
         WHERE artist_telemovel = %s AND report_date BETWEEN %s AND %s $pa_filter
         GROUP BY country_code
         ORDER BY s DESC
         LIMIT 10",
        $user_telemovel, $range['from'], $range['to']
    ) );
    if ( ! is_array($top_countries) ) $top_countries = [];
    $country_total_streams = array_sum( array_map( fn($c) => (int)$c->s, $top_countries ) );

    // ---- 7. RENDER ----
    ?>
    <div class="distro-analytics-container rmbz-fullbleed">

        <?php /* [1.5.171] Botão de voltar para o dashboard */ ?>
        <div style="margin-bottom:16px;">
            <a href="?distro_action=dashboard"
               style="display:inline-flex; align-items:center; gap:6px; padding:8px 14px; background:var(--rmbz-bg-surface, #1a1a1a); border:1px solid #333; border-radius:6px; color:var(--rmbz-text-muted, #aaa); text-decoration:none; font-size:13px; transition:all 0.15s ease;"
               onmouseover="this.style.borderColor='#f0b90b';this.style.color='#f0b90b';"
               onmouseout="this.style.borderColor='#333';this.style.color='#aaa';">
                ← Voltar ao painel
            </a>
        </div>

        <div class="analytics-header" style="display:flex; justify-content:space-between; align-items:flex-start; flex-wrap:wrap; gap:20px;">
            <div>
                <h1 style="color:var(--rmbz-text-primary, #fff); margin:0; font-size:28px;">Tendências & Análises</h1>
                <p style="color:var(--rmbz-text-faint, #888); font-size:12px; margin:5px 0 0 0;">
                    <?php echo esc_html( date('d/m/Y', strtotime($range['from'])) . ' — ' . date('d/m/Y', strtotime($range['to'])) ); ?>
                    <?php if ( ! empty($range['is_all']) ): ?>
                        <span style="color:var(--rmbz-brand, #f0b90b); font-weight:700; margin-left:6px;">• TUDO</span>
                    <?php endif; ?>
                </p>
            </div>

            <div style="display:flex; gap:8px; flex-wrap:wrap;">
                <?php
                // [1.5.231] Pills de período removidos — a vista geral
                // mostra sempre TUDO. O detalhe por período faz-se
                // escolhendo cada relatório nas secções de baixo.
                ?>
                <a href="?distro_action=analytics&export=csv&period=all"
                   style="padding:8px 14px; border-radius:20px; background:var(--rmbz-bg-surface, #1a1a1a); color:var(--rmbz-brand, #f0b90b); border:1px solid #f0b90b; text-decoration:none; font-size:12px; font-weight:700; text-transform:uppercase;">
                    ⬇ CSV
                </a>
            </div>
        </div>

        <!-- [FEAT 1.5.16] BANNER DE TRANSPARÊNCIA — último relatório importado -->
        <?php
        // Calcular há quantos dias foi o último dado do artista (report_date)
        // e quantos dias até ao próximo esperado (ciclo mensal + 60-90 dias).
        $latest_ts = $latest_report_date ? strtotime($latest_report_date) : 0;
        $days_since_latest = $latest_ts ? (int) floor( ( current_time('timestamp') - $latest_ts ) / 86400 ) : null;

        // Último import global (admin pode ter corrido agora um CSV que inclui
        // este artista). Mostrar como info adicional.
        $last_import_date = ( $last_import && ! empty($last_import['date']) )
            ? date_i18n('j \d\e F \d\e Y', strtotime($last_import['date']))
            : null;
        ?>
        <div style="background:linear-gradient(90deg, rgba(52,152,219,0.08) 0%, rgba(52,152,219,0.02) 100%); border:1px solid rgba(52,152,219,0.3); border-left:3px solid #3498db; padding:14px 18px; border-radius:6px; margin:20px 0; display:flex; align-items:center; gap:14px; flex-wrap:wrap;">
            <span style="font-size:22px;">📊</span>
            <div style="flex:1; min-width:240px;">
                <div style="color:var(--rmbz-text-primary, #fff); font-size:13px;">
                    <strong>Último relatório recebido:</strong>
                    <?php echo esc_html( date_i18n('j \d\e F \d\e Y', $latest_ts) ); ?>
                    <?php if ( $days_since_latest !== null ): ?>
                        <span style="color:var(--rmbz-text-faint, #888); font-size:12px;">
                            (há <?php echo $days_since_latest; ?> dia<?php echo $days_since_latest === 1 ? '' : 's'; ?>)
                        </span>
                    <?php endif; ?>
                </div>
                <div style="color:var(--rmbz-text-faint, #888); font-size:12px; margin-top:3px;">
                    Os DSPs (Spotify, Apple Music, etc.) enviam relatórios mensais com 45-90 dias de atraso &mdash; é o normal da indústria.
                    <?php if ( $last_import_date ): ?>
                        Último lote processado pela 1MZ: <?php echo esc_html($last_import_date); ?>.
                    <?php endif; ?>
                </div>
            </div>
        </div>
        <!-- /BANNER -->

        <!-- KPIs -->
        <div class="kpi-grid" style="display:grid; grid-template-columns:repeat(auto-fit, minmax(220px, 1fr)); gap:15px; margin:20px 0;">

            <div class="kpi-card" style="background:var(--rmbz-bg-elevated, #141414); border:1px solid #2a2a2a; padding:20px; border-radius:10px;">
                <span class="kpi-label" style="color:var(--rmbz-text-faint, #888); font-size:11px; text-transform:uppercase; letter-spacing:1px;">Ganhos (tudo)</span>
                <div class="kpi-value" style="color:var(--rmbz-brand, #f0b90b); font-size:28px; font-weight:900; margin:8px 0 4px 0;">
                    $<?php echo number_format($earnings_now, 2); ?>
                </div>
                <?php if ( empty($range['is_all']) ): ?>
                    <?php rmbz_render_delta_pill( $d_earnings ); ?>
                    <small style="color:var(--rmbz-text-faint, #666); font-size:10px; display:block; margin-top:6px;">anterior: $<?php echo number_format($earn_prev, 2); ?></small>
                <?php else: ?>
                    <small style="color:var(--rmbz-text-faint, #666); font-size:10px; display:block; margin-top:6px;">desde <?php echo esc_html(date('d/m/Y', strtotime($range['from']))); ?></small>
                <?php endif; ?>
            </div>

            <div class="kpi-card" style="background:var(--rmbz-bg-elevated, #141414); border:1px solid #2a2a2a; padding:20px; border-radius:10px;">
                <span class="kpi-label" style="color:var(--rmbz-text-faint, #888); font-size:11px; text-transform:uppercase; letter-spacing:1px;">Streams (tudo)</span>
                <div class="kpi-value" style="color:var(--rmbz-text-primary, #fff); font-size:28px; font-weight:900; margin:8px 0 4px 0;">
                    <?php echo number_format($streams_now); ?>
                </div>
                <?php if ( empty($range['is_all']) ): ?>
                    <?php rmbz_render_delta_pill( $d_streams ); ?>
                    <small style="color:var(--rmbz-text-faint, #666); font-size:10px; display:block; margin-top:6px;">anterior: <?php echo number_format($streams_prev); ?></small>
                <?php else: ?>
                    <small style="color:var(--rmbz-text-faint, #666); font-size:10px; display:block; margin-top:6px;"><?php echo (int) $range['days']; ?> dias de dados</small>
                <?php endif; ?>
            </div>

            <div class="kpi-card" style="background:var(--rmbz-bg-elevated, #141414); border:1px solid #2a2a2a; padding:20px; border-radius:10px;">
                <span class="kpi-label" style="color:var(--rmbz-text-faint, #888); font-size:11px; text-transform:uppercase; letter-spacing:1px;">RPM Médio</span>
                <div class="kpi-value" style="color:var(--rmbz-text-primary, #fff); font-size:28px; font-weight:900; margin:8px 0 4px 0;">
                    $<?php echo $streams_now > 0 ? number_format( ($earnings_now / $streams_now) * 1000, 3 ) : '0.000'; ?>
                </div>
                <small style="color:var(--rmbz-text-faint, #666); font-size:10px;">por 1000 streams</small>
            </div>

            <div class="kpi-card" style="background:var(--rmbz-bg-elevated, #141414); border:1px solid #2a2a2a; padding:20px; border-radius:10px;">
                <span class="kpi-label" style="color:var(--rmbz-text-faint, #888); font-size:11px; text-transform:uppercase; letter-spacing:1px;">Total Histórico</span>
                <div class="kpi-value" style="color:var(--rmbz-text-primary, #fff); font-size:22px; font-weight:900; margin:8px 0 4px 0;">
                    <?php echo number_format($lifetime_streams); ?> str
                </div>
                <small style="color:var(--rmbz-success, #10b981); font-size:11px;">$<?php echo number_format($lifetime_earnings, 2); ?> vida inteira</small>
            </div>
        </div>

        <!-- Gráfico principal -->
        <div class="chart-section-wrapper" style="background:var(--rmbz-bg-elevated, #141414); border:1px solid #2a2a2a; padding:20px; border-radius:10px; margin-bottom:20px;">
            <div class="chart-header" style="display:flex; justify-content:space-between; align-items:center; margin-bottom:15px;">
                <div>
                    <h2 style="color:var(--rmbz-text-primary, #fff); margin:0; font-size:18px;">Evolução</h2>
                    <small style="color:var(--rmbz-text-faint, #666);"><?php echo count($chart_labels); ?> pontos • agregação: <?php
                        echo $range['bucket'] === 'day' ? 'diária' : ($range['bucket'] === 'week' ? 'semanal' : 'mensal');
                    ?></small>
                </div>
                <div style="display:flex; gap:15px; align-items:center; font-size:12px;">
                    <span style="color:var(--rmbz-brand, #f0b90b);">● Streams</span>
                    <span style="color:var(--rmbz-success, #10b981);">● Ganhos ($)</span>
                </div>
            </div>
            <div style="height:320px; width:100%;">
                <canvas id="rmbzMainChart"></canvas>
            </div>
        </div>

        <!-- TOP FAIXAS + TOP PLATAFORMAS -->
        <div style="display:grid; grid-template-columns:2fr 1fr; gap:20px; margin-bottom:20px;" class="rmbz-two-col">

            <!-- Top faixas -->
            <div class="panel-box" style="background:var(--rmbz-bg-elevated, #141414); border:1px solid #2a2a2a; padding:20px; border-radius:10px;">
                <div class="panel-header" style="display:flex; justify-content:space-between; align-items:center; margin-bottom:15px;">
                    <h3 style="color:var(--rmbz-text-primary, #fff); margin:0; font-size:16px;">🎵 Top Faixas</h3>
                    <small style="color:var(--rmbz-text-faint, #666);">por track_id • clique para detalhes</small>
                </div>

                <?php if ( empty($top_tracks) ): ?>
                    <p style="color:var(--rmbz-text-faint, #666); text-align:center; padding:30px; font-size:13px;">Sem dados neste período.</p>
                <?php else: ?>
                    <div class="rmbz-tracks-list" style="display:flex; flex-direction:column; gap:10px;">
                        <?php foreach ( $top_tracks as $i => $t ):
                            $spark_data = $sparklines[ (int)$t->track_id ] ?? [];
                        ?>
                            <div class="rmbz-track-row"
                                 data-track-id="<?php echo (int)$t->track_id; ?>"
                                 style="display:grid; grid-template-columns:30px 50px 1fr 100px auto auto; gap:12px; align-items:center; padding:10px; background:var(--rmbz-bg-surface, #1a1a1a); border-radius:8px; cursor:pointer; transition:background 0.2s;"
                                 onmouseover="this.style.background='#222'" onmouseout="this.style.background='#1a1a1a'">

                                <div style="color:var(--rmbz-text-faint, #666); font-weight:900; text-align:center;"><?php echo $i + 1; ?></div>

                                <?php if ( $t->cover_image ): ?>
                                    <img src="<?php echo esc_url($t->cover_image); ?>"
                                         style="width:50px; height:50px; border-radius:6px; object-fit:cover;">
                                <?php else: ?>
                                    <div style="width:50px; height:50px; background:linear-gradient(135deg,#333,#555); border-radius:6px; display:flex; align-items:center; justify-content:center; color:var(--rmbz-text-faint, #888);">🎵</div>
                                <?php endif; ?>

                                <div style="min-width:0;">
                                    <strong style="color:var(--rmbz-text-primary, #fff); display:block; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;">
                                        <?php echo esc_html($t->track_title); ?>
                                    </strong>
                                    <small style="color:var(--rmbz-text-faint, #888); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; display:block;">
                                        do álbum "<?php echo esc_html($t->release_title); ?>"
                                    </small>
                                </div>

                                <canvas class="rmbz-sparkline" width="100" height="30"
                                        data-spark="<?php echo esc_attr( json_encode($spark_data) ); ?>"></canvas>

                                <div style="text-align:right; min-width:70px;">
                                    <strong style="color:var(--rmbz-brand, #f0b90b); display:block; font-size:14px;"><?php echo number_format((int)$t->streams); ?></strong>
                                    <small style="color:var(--rmbz-text-faint, #666); font-size:10px;">streams</small>
                                </div>

                                <div style="text-align:right; min-width:70px;">
                                    <strong style="color:var(--rmbz-success, #10b981); display:block; font-size:14px;">$<?php echo number_format((float)$t->earnings, 2); ?></strong>
                                    <small style="color:var(--rmbz-text-faint, #666); font-size:10px;">ganhos</small>
                                </div>
                            </div>
                        <?php endforeach; ?>
                    </div>
                <?php endif; ?>
            </div>

            <!-- Top plataformas -->
            <div class="panel-box" style="background:var(--rmbz-bg-elevated, #141414); border:1px solid #2a2a2a; padding:20px; border-radius:10px;">
                <div class="panel-header" style="margin-bottom:15px;">
                    <h3 style="color:var(--rmbz-text-primary, #fff); margin:0; font-size:16px;">📻 Top Plataformas</h3>
                </div>

                <?php if ( empty($top_stores) ): ?>
                    <p style="color:var(--rmbz-text-faint, #666); text-align:center; padding:30px; font-size:13px;">Sem dados.</p>
                <?php else: ?>
                    <div style="display:flex; flex-direction:column; gap:12px;">
                        <?php foreach ( $top_stores as $s ):
                            $pct = $store_total_streams > 0 ? ( (int)$s->s / $store_total_streams ) * 100 : 0;
                        ?>
                            <div>
                                <div style="display:flex; justify-content:space-between; margin-bottom:4px;">
                                    <span style="color:var(--rmbz-text-primary, #fff); font-size:13px; font-weight:600;">
                                        <?php echo esc_html($s->store); ?>
                                    </span>
                                    <span style="color:var(--rmbz-brand, #f0b90b); font-size:12px; font-weight:700;">
                                        <?php echo number_format((int)$s->s); ?>
                                    </span>
                                </div>
                                <div style="height:6px; background:var(--rmbz-bg-elevated, #222); border-radius:3px; overflow:hidden;">
                                    <div style="height:100%; width:<?php echo esc_attr( number_format($pct, 1) ); ?>%; background:linear-gradient(90deg,#f0b90b,#d4a000); border-radius:3px;"></div>
                                </div>
                                <small style="color:var(--rmbz-text-faint, #666); font-size:10px;">
                                    <?php echo number_format($pct, 1); ?>% • $<?php echo number_format((float)$s->e, 2); ?>
                                </small>
                            </div>
                        <?php endforeach; ?>
                    </div>
                <?php endif; ?>
            </div>
        </div>

        <!-- Top países -->
        <div class="panel-box" style="background:var(--rmbz-bg-elevated, #141414); border:1px solid #2a2a2a; padding:20px; border-radius:10px;">
            <div class="panel-header" style="margin-bottom:15px;">
                <h3 style="color:var(--rmbz-text-primary, #fff); margin:0; font-size:16px;">🌍 Top Países</h3>
            </div>

            <?php if ( empty($top_countries) ): ?>
                <p style="color:var(--rmbz-text-faint, #666); text-align:center; padding:30px; font-size:13px;">Sem dados geográficos.</p>
            <?php else: ?>
                <div style="display:grid; grid-template-columns:repeat(auto-fit, minmax(200px, 1fr)); gap:12px;">
                    <?php foreach ( $top_countries as $c ):
                        $pct = $country_total_streams > 0 ? ( (int)$c->s / $country_total_streams ) * 100 : 0;
                    ?>
                        <div style="background:var(--rmbz-bg-surface, #1a1a1a); padding:12px; border-radius:8px; display:flex; align-items:center; gap:12px;">
                            <div style="font-size:28px;"><?php echo rmbz_country_flag($c->country_code); ?></div>
                            <div style="flex:1; min-width:0;">
                                <strong style="color:var(--rmbz-text-primary, #fff); font-size:13px; display:block;">
                                    <?php echo esc_html($c->country_code ?: 'UNK'); ?>
                                </strong>
                                <small style="color:var(--rmbz-text-faint, #666); font-size:11px;"><?php echo number_format($pct, 1); ?>% do total</small>
                            </div>
                            <div style="text-align:right;">
                                <strong style="color:var(--rmbz-brand, #f0b90b); font-size:14px; display:block;">
                                    <?php echo number_format((int)$c->s); ?>
                                </strong>
                                <small style="color:var(--rmbz-success, #10b981); font-size:10px;">
                                    $<?php echo number_format((float)$c->e, 2); ?>
                                </small>
                            </div>
                        </div>
                    <?php endforeach; ?>
                </div>
            <?php endif; ?>
        </div>

        <?php
        // [1.5.227] FASE 1a — Secção "Relatórios mensais" (lista de
        // imports). Adicional — não mexe na vista antiga acima.
        if ( function_exists( 'rmbz_analytics_render_imports_section' ) ) {
            rmbz_analytics_render_imports_section( $user_telemovel );
        }
        // [1.5.228] FASE 1b — Secção "Evolução mês a mês".
        if ( function_exists( 'rmbz_analytics_render_evolution_section' ) ) {
            rmbz_analytics_render_evolution_section( $user_telemovel );
        }
        ?>

    </div>

    <!-- Modal drill-down -->
    <?php /* [1.5.172] Modal centrado no viewport.
       ANTES: position:fixed wrapper + margin:50px auto no interior →
       interior aparecia 50px do TOPO DA PÁGINA, não do viewport. Quando o
       utilizador estava scrolled, o modal ficava acima da viewport e
       parecia que abria "em cima" da página visível.
       AGORA: flex-center no wrapper (display:flex; align-items:center;
       justify-content:center) → modal centrado no viewport visível. */ ?>
    <div id="rmbz-track-modal" style="display:none; position:fixed; inset:0; background:rgba(0,0,0,0.85); z-index:99999; padding:20px; overflow-y:auto; align-items:center; justify-content:center;">
        <div style="max-width:800px; width:100%; margin:auto; background:var(--rmbz-bg-elevated, #141414); border:1px solid #2a2a2a; border-radius:12px; padding:25px; position:relative; max-height:calc(100vh - 40px); overflow-y:auto;">
            <?php /* [1.5.173] Close button moderno — SVG vector, design Vercel/Linear.
               Tema Foxiz força width/padding em buttons. Forçamos com !important e
               usamos classe específica para isolar. */ ?>
            <button id="rmbz-modal-close" class="rmbz-analytics-close-btn" type="button" aria-label="Fechar">
                <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" aria-hidden="true">
                    <line x1="18" y1="6" x2="6" y2="18"></line>
                    <line x1="6" y1="6" x2="18" y2="18"></line>
                </svg>
            </button>
            <h2 style="color:var(--rmbz-text-primary, #fff); margin:0 0 15px 0; padding-right:48px;" id="rmbz-modal-title">A carregar…</h2>
            <div style="height:300px;"><canvas id="rmbzTrackModalChart"></canvas></div>
            <div id="rmbz-modal-extra" style="margin-top:20px; color:var(--rmbz-text-muted, #aaa); font-size:13px;"></div>
        </div>
    </div>

    <style>
        @media (max-width: 860px) {
            .rmbz-two-col { grid-template-columns: 1fr !important; }
            .rmbz-track-row { grid-template-columns: 20px 40px 1fr auto !important; }
            .rmbz-track-row canvas.rmbz-sparkline { display:none !important; }
        }

        /* ============================================================
           [1.5.173] Close button moderno (Design 1 — Vercel/Linear)
           Tema Foxiz força width:auto + padding em buttons. Forçamos
           tudo com !important para isolar do tema.
           ============================================================ */
        .rmbz-analytics-close-btn {
            position: absolute !important;
            top: 12px !important;
            right: 12px !important;
            width: 32px !important;
            height: 32px !important;
            min-width: 0 !important;
            min-height: 0 !important;
            max-width: 32px !important;
            max-height: 32px !important;
            padding: 0 !important;
            margin: 0 !important;
            box-sizing: border-box !important;
            background: rgba(255, 255, 255, 0.05) !important;
            border: 1px solid rgba(255, 255, 255, 0.12) !important;
            border-radius: 8px !important;
            color: #aaa !important;
            cursor: pointer !important;
            display: inline-flex !important;
            align-items: center !important;
            justify-content: center !important;
            line-height: 1 !important;
            font-size: 0 !important;
            text-transform: none !important;
            letter-spacing: 0 !important;
            font-weight: 400 !important;
            box-shadow: none !important;
            text-shadow: none !important;
            transition: all 0.15s ease !important;
        }
        .rmbz-analytics-close-btn:hover,
        .rmbz-analytics-close-btn:focus {
            background: rgba(255, 255, 255, 0.10) !important;
            border-color: rgba(255, 255, 255, 0.25) !important;
            color: #fff !important;
            transform: none !important;
        }
        .rmbz-analytics-close-btn svg {
            width: 14px !important;
            height: 14px !important;
            flex-shrink: 0 !important;
            display: block !important;
            stroke: currentColor !important;
            fill: none !important;
        }
    </style>

    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <script>
    (function () {
        const DATA = {
            labels:   <?php echo wp_json_encode($chart_labels); ?>,
            streams:  <?php echo wp_json_encode($chart_streams); ?>,
            earnings: <?php echo wp_json_encode($chart_earnings); ?>,
            ajaxUrl:  '<?php echo esc_js( admin_url('admin-ajax.php') ); ?>',
            nonce:    '<?php echo esc_js( wp_create_nonce('rmbz_distro_nonce') ); ?>',
            period:   '<?php echo esc_js($period); ?>'
        };

        function boot() {
            if ( typeof Chart === 'undefined' ) { setTimeout(boot, 100); return; }

            // Main chart (dual axis)
            const ctx = document.getElementById('rmbzMainChart').getContext('2d');
            const g1 = ctx.createLinearGradient(0, 0, 0, 320);
            g1.addColorStop(0, 'rgba(240, 185, 11, 0.35)');
            g1.addColorStop(1, 'rgba(240, 185, 11, 0)');

            new Chart(ctx, {
                data: {
                    labels: DATA.labels,
                    datasets: [
                        {
                            type: 'line', label: 'Streams', yAxisID: 'y',
                            data: DATA.streams,
                            borderColor: '#f0b90b', backgroundColor: g1,
                            borderWidth: 3, tension: 0.35, fill: true,
                            pointRadius: 3, pointBackgroundColor: '#121212', pointBorderColor: '#f0b90b'
                        },
                        {
                            type: 'line', label: 'Ganhos ($)', yAxisID: 'y1',
                            data: DATA.earnings,
                            borderColor: '#2ecc71', borderDash: [5, 5],
                            borderWidth: 2, tension: 0.3, fill: false,
                            pointRadius: 2, pointBackgroundColor: '#2ecc71'
                        }
                    ]
                },
                options: {
                    responsive: true, maintainAspectRatio: false,
                    interaction: { mode: 'index', intersect: false },
                    plugins: {
                        legend: { display: false },
                        tooltip: {
                            backgroundColor: '#0a0a0a', borderColor: '#333', borderWidth: 1,
                            titleColor: '#fff', bodyColor: '#ccc', padding: 10
                        }
                    },
                    scales: {
                        x:  { grid: { display: false }, ticks: { color: '#666', font: { size: 10 } } },
                        y:  { position: 'left',  grid: { color: '#222' }, ticks: { color: '#f0b90b', font: { size: 10 } }, beginAtZero: true },
                        y1: { position: 'right', grid: { display: false }, ticks: { color: '#2ecc71', font: { size: 10 }, callback: v => '$' + v }, beginAtZero: true }
                    }
                }
            });

            // Sparklines
            document.querySelectorAll('canvas.rmbz-sparkline').forEach(cv => {
                const data = JSON.parse(cv.dataset.spark || '[]');
                if ( ! data.length ) return;
                new Chart(cv.getContext('2d'), {
                    type: 'line',
                    data: {
                        labels: data.map((_, i) => i),
                        datasets: [{
                            data: data, borderColor: '#f0b90b', borderWidth: 1.5,
                            pointRadius: 0, tension: 0.4, fill: false
                        }]
                    },
                    options: {
                        responsive: false, maintainAspectRatio: false,
                        plugins: { legend: { display: false }, tooltip: { enabled: false } },
                        scales: { x: { display: false }, y: { display: false } }
                    }
                });
            });

            // Drill-down modal
            let modalChart = null;
            document.querySelectorAll('.rmbz-track-row').forEach(row => {
                row.addEventListener('click', function () {
                    const trackId = this.dataset.trackId;
                    openTrackModal(trackId);
                });
            });

            document.getElementById('rmbz-modal-close').addEventListener('click', closeModal);
            document.getElementById('rmbz-track-modal').addEventListener('click', function (e) {
                if ( e.target === this ) closeModal();
            });

            function closeModal() {
                document.getElementById('rmbz-track-modal').style.display = 'none';
                if ( modalChart ) { modalChart.destroy(); modalChart = null; }
            }

            function openTrackModal(trackId) {
                const modal = document.getElementById('rmbz-track-modal');
                // [1.5.172] display:flex (não block) para flex-center funcionar
                modal.style.display = 'flex';
                document.getElementById('rmbz-modal-title').innerText = 'A carregar…';
                document.getElementById('rmbz-modal-extra').innerHTML = '';

                const fd = new FormData();
                fd.append('action', 'rmbz_track_analytics');
                fd.append('nonce', DATA.nonce);
                fd.append('track_id', trackId);
                fd.append('period', DATA.period);

                fetch(DATA.ajaxUrl, { method: 'POST', body: fd })
                    .then(r => r.json())
                    .then(res => {
                        if ( ! res.success ) { alert(res.data || 'Erro'); closeModal(); return; }
                        const d = res.data;
                        document.getElementById('rmbz-modal-title').innerText = '🎵 ' + d.title;
                        document.getElementById('rmbz-modal-extra').innerHTML =
                            '<strong>' + d.total_streams.toLocaleString() + '</strong> streams · ' +
                            '<strong style="color:var(--rmbz-success, #10b981);">$' + d.total_earnings.toFixed(2) + '</strong> no período · ' +
                            (d.isrc ? '<span style="color:var(--rmbz-text-faint, #666);">ISRC ' + d.isrc + '</span>' : '<span style="color:var(--rmbz-text-faint, #999);">sem ISRC</span>');

                        const c = document.getElementById('rmbzTrackModalChart').getContext('2d');
                        if ( modalChart ) modalChart.destroy();
                        modalChart = new Chart(c, {
                            type: 'bar',
                            data: {
                                labels: d.labels,
                                datasets: [{ label: 'Streams', data: d.streams, backgroundColor: '#f0b90b', borderRadius: 3 }]
                            },
                            options: {
                                responsive: true, maintainAspectRatio: false,
                                plugins: { legend: { display: false } },
                                scales: {
                                    x: { grid: { display: false }, ticks: { color: '#666' } },
                                    y: { grid: { color: '#222' }, ticks: { color: '#888' }, beginAtZero: true }
                                }
                            }
                        });
                    })
                    .catch(() => { alert('Erro de ligação.'); closeModal(); });
            }
        }
        boot();
    })();
    </script>
    <?php
}

/**
 * Helper visual: pílula de variação (↑ / ↓ / ─)
 */
function rmbz_render_delta_pill( $delta ) {
    $colors = [
        'up'   => [ 'bg' => 'rgba(46,204,113,0.15)', 'fg' => '#2ecc71', 'arrow' => '▲' ],
        'down' => [ 'bg' => 'rgba(231,76,60,0.15)',  'fg' => '#e74c3c', 'arrow' => '▼' ],
        'flat' => [ 'bg' => 'rgba(150,150,150,0.15)','fg' => '#999',    'arrow' => '─' ],
    ];
    $c = $colors[ $delta['direction'] ];
    printf(
        '<span style="display:inline-block; padding:2px 8px; border-radius:10px; font-size:11px; font-weight:700; background:%s; color:%s;">%s %s%%</span>',
        esc_attr($c['bg']), esc_attr($c['fg']), esc_html($c['arrow']), esc_html($delta['pct'])
    );
}

/**
 * Preenche buckets vazios entre from/to para o gráfico não ter gaps visuais.
 */
function rmbz_fill_empty_buckets( $series, $range ) {
    $map = [];
    if ( is_array($series) || is_object($series) ) {
        foreach ( $series as $row ) {
            if ( is_object($row) && isset($row->bucket) ) {
                $map[ $row->bucket ] = $row;
            }
        }
    }

    $out = [];
    $d   = new DateTime( $range['from'] );
    $end = new DateTime( $range['to'] );

    while ( $d <= $end ) {
        switch ( $range['bucket'] ) {
            case 'month':
                $key = $d->format('Y-m-01');
                $step = '+1 month';
                break;
            case 'week':
                $wkd = (int)$d->format('N') - 1;
                $d->modify("-{$wkd} days");
                $key = $d->format('Y-m-d');
                $step = '+1 week';
                break;
            default:
                $key = $d->format('Y-m-d');
                $step = '+1 day';
        }

        if ( isset($map[$key]) ) {
            $out[] = $map[$key];
        } else {
            $out[] = (object)[ 'bucket' => $key, 's' => 0, 'e' => 0 ];
        }
        $d->modify( $step );
    }
    return $out;
}


/* =============================================================================
 * AJAX: drill-down de uma faixa (modal)
 * ============================================================================= */
function rmbz_ajax_track_analytics() {
    if ( ! check_ajax_referer('rmbz_distro_nonce', 'nonce', false) ) {
        wp_send_json_error('Segurança.', 403);
    }
    if ( ! function_exists('rmbz_is_user_logged_in') || ! rmbz_is_user_logged_in() ) {
        wp_send_json_error('Sessão.', 401);
    }

    $telemovel = rmbz_get_current_user_telemovel();
    $track_id  = isset($_POST['track_id']) ? absint($_POST['track_id']) : 0;
    $period    = isset($_POST['period']) && in_array($_POST['period'], ['7d','30d','90d','12m','all'], true)
        ? $_POST['period'] : '30d';

    global $wpdb;
    $tbl_tracks = $wpdb->prefix . 'rmbz_distro_tracks';
    $tbl_rel    = $wpdb->prefix . 'rmbz_distro_releases';
    $tbl_daily  = $wpdb->prefix . 'rmbz_distro_daily_stats';

    // Confirmar que a faixa pertence ao utilizador
    $track = $wpdb->get_row( $wpdb->prepare(
        "SELECT t.id, t.track_title, t.isrc_code, r.artist_telemovel, r.title AS release_title
         FROM $tbl_tracks t
         JOIN $tbl_rel r ON t.release_id = r.id
         WHERE t.id = %d",
        $track_id
    ) );
    if ( ! $track || $track->artist_telemovel !== $telemovel ) {
        wp_send_json_error('Faixa não encontrada.', 404);
    }

    $range = rmbz_analytics_period_range( $period, $telemovel );
    // Bug igual ao do bloco principal — DATE_FORMAT precisa de %% para o
    // wpdb::prepare não interpretar %Y/%m/%d como placeholders.
    $bucket_expr = [
        'day'   => "DATE_FORMAT(report_date, '%%Y-%%m-%%d')",
        'week'  => "DATE_FORMAT(DATE_SUB(report_date, INTERVAL WEEKDAY(report_date) DAY), '%%Y-%%m-%%d')",
        'month' => "DATE_FORMAT(report_date, '%%Y-%%m-01')",
    ][ $range['bucket'] ];

    $pa_filter = function_exists('rmbz_approval_clause') ? rmbz_approval_clause($tbl_daily) : '';
    $rows = $wpdb->get_results( $wpdb->prepare(
        "SELECT $bucket_expr AS bucket, SUM(streams) s, SUM(sales) e
         FROM $tbl_daily
         WHERE track_id = %d AND report_date BETWEEN %s AND %s $pa_filter
         GROUP BY bucket ORDER BY bucket ASC",
        $track_id, $range['from'], $range['to']
    ) );
    $filled = rmbz_fill_empty_buckets( $rows, $range );

    $labels = array_map( function($r) use ($range) {
        if ( $range['bucket'] === 'month' ) return date('M/y', strtotime($r->bucket));
        if ( $range['bucket'] === 'week' )  return 'sem ' . date('d/m', strtotime($r->bucket));
        return date('d/m', strtotime($r->bucket));
    }, $filled );

    wp_send_json_success([
        'title'           => $track->track_title . ' — ' . $track->release_title,
        'isrc'            => $track->isrc_code,
        'labels'          => $labels,
        'streams'         => array_map( fn($r) => (int)$r->s,   $filled ),
        'earnings'        => array_map( fn($r) => (float)$r->e, $filled ),
        'total_streams'   => (int)   array_sum( array_map( fn($r) => (int)$r->s,   $filled ) ),
        'total_earnings'  => (float) array_sum( array_map( fn($r) => (float)$r->e, $filled ) ),
    ]);
}
add_action( 'wp_ajax_rmbz_track_analytics', 'rmbz_ajax_track_analytics' );
// [1.5.208] BUG CRÍTICO — sem `nopriv`, artistas com sessão telefone (que NÃO
// são users WP) ficavam sem resposta válida do AJAX. WordPress só dispara
// `wp_ajax_*` para users WP logados — sessão telefone do artista é vista como
// "guest" pelo WP. Resultado: AJAX devolvia 0 ou HTML, JS lia como JSON falhado
// e mostrava "(Erro)" no modal de drill-down de uma faixa.
add_action( 'wp_ajax_nopriv_rmbz_track_analytics', 'rmbz_ajax_track_analytics' );


/* =============================================================================
 * EXPORT CSV
 * ============================================================================= */
function rmbz_analytics_export_csv() {
    if ( ! isset($_GET['distro_action']) || $_GET['distro_action'] !== 'analytics' ) return;
    if ( ! isset($_GET['export']) || $_GET['export'] !== 'csv' ) return;

    if ( ! function_exists('rmbz_is_user_logged_in') || ! rmbz_is_user_logged_in() ) return;

    $telemovel = rmbz_get_current_user_telemovel();
    $period = isset($_GET['period']) && in_array($_GET['period'], ['7d','30d','90d','12m','all'], true)
        ? $_GET['period'] : '30d';
    $range = rmbz_analytics_period_range( $period, $telemovel );

    global $wpdb;
    $tbl_daily  = $wpdb->prefix . 'rmbz_distro_daily_stats';
    $tbl_tracks = $wpdb->prefix . 'rmbz_distro_tracks';
    $tbl_rel    = $wpdb->prefix . 'rmbz_distro_releases';

    $pa_filter = function_exists('rmbz_approval_clause') ? rmbz_approval_clause($tbl_daily, 'd.') : '';
    $rows = $wpdb->get_results( $wpdb->prepare(
        "SELECT d.report_date, r.title AS release_title, t.track_title, t.isrc_code,
                d.country_code, d.store, d.streams, d.sales
         FROM $tbl_daily d
         LEFT JOIN $tbl_tracks t ON d.track_id = t.id
         LEFT JOIN $tbl_rel    r ON d.release_id = r.id
         WHERE d.artist_telemovel = %s AND d.report_date BETWEEN %s AND %s $pa_filter
         ORDER BY d.report_date ASC, d.store ASC",
        $telemovel, $range['from'], $range['to']
    ), ARRAY_A );

    // Limpa qualquer output pendente
    while ( ob_get_level() ) ob_end_clean();
    nocache_headers();
    header('Content-Type: text/csv; charset=utf-8');
    header('Content-Disposition: attachment; filename="1mz-analytics-' . $period . '-' . date('Ymd') . '.csv"');

    $out = fopen('php://output', 'w');
    // BOM para Excel abrir bem UTF-8
    fprintf( $out, chr(0xEF).chr(0xBB).chr(0xBF) );
    fputcsv( $out, ['Date', 'Release', 'Track', 'ISRC', 'Country', 'Store', 'Streams', 'Sales_USD'] );
    foreach ( $rows as $r ) {
        fputcsv( $out, [
            $r['report_date'],
            $r['release_title'],
            $r['track_title'],
            $r['isrc_code'],
            $r['country_code'],
            $r['store'],
            (int)$r['streams'],
            number_format((float)$r['sales'], 4, '.', ''),
        ] );
    }
    fclose($out);
    exit;
}
// Antes do shortcode renderizar — usamos template_redirect para ter output limpo
add_action( 'template_redirect', 'rmbz_analytics_export_csv' );
