<?php
/**
 * 1MZ For Artists API — Stats Controller (1.6.9)
 *
 * Endpoints de estatísticas/analytics para a app Android.
 *
 * Reutiliza a lógica e tabelas do sistema de analytics que já existe em
 *   includes/distribution/distribution-analytics.php
 * mas expostos como REST API em vez de HTML.
 *
 * Tabelas envolvidas:
 *   wp_rmbz_distro_stats         — agregado lifetime por artista
 *   wp_rmbz_distro_daily_stats   — granular por dia/release/track/país/store
 *   wp_rmbz_distro_releases      — releases distribuídos
 *   wp_rmbz_distro_tracks        — faixas distribuídas
 *
 * Endpoints:
 *   GET /stats/summary                          — KPIs (streams, ganhos, deltas)
 *   GET /stats/timeline?period=30d              — série temporal para gráfico
 *   GET /stats/top-tracks?period=30d&limit=10   — top faixas
 *   GET /stats/top-platforms?period=30d         — top DSPs
 *   GET /stats/top-countries?period=30d         — top países
 *
 * Períodos suportados: 7d, 30d, 90d, 12m, all (lifetime)
 *
 * @package 1MZ_For_Artists
 * @since 1.6.9
 */

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

// As funções helper vivem em distribution-analytics.php.
// Garantir que está carregado antes de chamar (defensivo — em produção
// já é carregado pelo plugin-main, mas aqui usamos apenas helpers
// puros, não rendering).
if ( ! function_exists( 'rmbz_analytics_period_range' ) ) {
    require_once dirname( __FILE__, 3 ) . '/distribution/distribution-analytics.php';
}

add_action( 'rest_api_init', 'rmbz_api_register_stats_routes' );

function rmbz_api_register_stats_routes() {
    $ns = RMBZ_API_NAMESPACE;

    register_rest_route( $ns, '/stats/summary', [
        'methods'             => 'GET',
        'callback'            => 'rmbz_api_handle_stats_summary',
        'permission_callback' => 'rmbz_api_require_auth',
        'args' => [
            'period' => [ 'type' => 'string', 'default' => '30d' ],
        ],
    ] );

    register_rest_route( $ns, '/stats/timeline', [
        'methods'             => 'GET',
        'callback'            => 'rmbz_api_handle_stats_timeline',
        'permission_callback' => 'rmbz_api_require_auth',
        'args' => [
            'period' => [ 'type' => 'string', 'default' => '30d' ],
        ],
    ] );

    register_rest_route( $ns, '/stats/top-tracks', [
        'methods'             => 'GET',
        'callback'            => 'rmbz_api_handle_stats_top_tracks',
        'permission_callback' => 'rmbz_api_require_auth',
        'args' => [
            'period' => [ 'type' => 'string', 'default' => '30d' ],
            'limit'  => [ 'type' => 'integer', 'default' => 10 ],
        ],
    ] );

    register_rest_route( $ns, '/stats/top-platforms', [
        'methods'             => 'GET',
        'callback'            => 'rmbz_api_handle_stats_top_platforms',
        'permission_callback' => 'rmbz_api_require_auth',
        'args' => [
            'period' => [ 'type' => 'string', 'default' => '30d' ],
        ],
    ] );

    register_rest_route( $ns, '/stats/top-countries', [
        'methods'             => 'GET',
        'callback'            => 'rmbz_api_handle_stats_top_countries',
        'permission_callback' => 'rmbz_api_require_auth',
        'args' => [
            'period' => [ 'type' => 'string', 'default' => '30d' ],
        ],
    ] );

    // [Polish v2] Detalhe de uma faixa específica — drill-down
    register_rest_route( $ns, '/stats/track/(?P<id>\d+)', [
        'methods'             => 'GET',
        'callback'            => 'rmbz_api_handle_stats_track_detail',
        'permission_callback' => 'rmbz_api_require_auth',
        'args' => [
            'id'     => [ 'type' => 'integer', 'required' => true ],
            'period' => [ 'type' => 'string', 'default' => '30d' ],
        ],
    ] );

    // [1.5.226] Relatório de ganhos de um RELEASE inteiro — agrega
    // todas as faixas do release + devolve a lista por faixa.
    register_rest_route( $ns, '/stats/release/(?P<id>\d+)', [
        'methods'             => 'GET',
        'callback'            => 'rmbz_api_handle_stats_release_detail',
        'permission_callback' => 'rmbz_api_require_auth',
        'args' => [
            'id'     => [ 'type' => 'integer', 'required' => true ],
            'period' => [ 'type' => 'string', 'default' => 'all' ],
        ],
    ] );

    // [1.5.210] Lista de imports do admin com info agregada para o artista.
    // Cada item representa uma importação CSV processada pelo admin que
    // gerou pelo menos uma linha de daily_stats para o artista actual.
    register_rest_route( $ns, '/stats/imports', [
        'methods'             => 'GET',
        'callback'            => 'rmbz_api_handle_stats_imports_list',
        'permission_callback' => 'rmbz_api_require_auth',
        'args' => [
            'limit' => [ 'type' => 'integer', 'default' => 20 ],
        ],
    ] );

    // [1.5.210] Detalhe de UM import específico — ganhos, top lojas,
    // top países, agregados só para o artista actual.
    register_rest_route( $ns, '/stats/imports/(?P<id>\d+)', [
        'methods'             => 'GET',
        'callback'            => 'rmbz_api_handle_stats_import_detail',
        'permission_callback' => 'rmbz_api_require_auth',
        'args' => [
            'id' => [ 'type' => 'integer', 'required' => true ],
        ],
    ] );
}

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

/**
 * Valida o período recebido. Aceita: 7d, 30d, 90d, 12m, all.
 * Se for inválido, usa default 30d.
 */
function rmbz_api_stats_validate_period( $period ) {
    $allowed = [ '7d', '30d', '90d', '12m', 'all' ];
    return in_array( $period, $allowed, true ) ? $period : '30d';
}

/**
 * Retorna o filtro SQL de aprovação (exclui linhas pending_approval=1
 * que ainda não foram aprovadas pelo admin) para o table dado.
 */
function rmbz_api_stats_approval_clause( $table_daily ) {
    return function_exists( 'rmbz_approval_clause' )
        ? rmbz_approval_clause( $table_daily )
        : '';
}

/* ============================================================================
 * GET /stats/summary
 *
 * Retorna KPIs do artista:
 *   - lifetime: total de streams + ganhos desde sempre
 *   - period:   streams + ganhos do período seleccionado
 *   - deltas:   percentagem de variação face ao período anterior
 * ============================================================================ */
function rmbz_api_handle_stats_summary( WP_REST_Request $request ) {
    global $wpdb;
    $telemovel = (string) $request->get_param( 'rmbz_telemovel' );
    $period    = rmbz_api_stats_validate_period( $request->get_param( 'period' ) );

    $table_daily = $wpdb->prefix . 'rmbz_distro_daily_stats';
    $table_stats = $wpdb->prefix . 'rmbz_distro_stats';

    // Verificar que tabelas existem (defensivo — em instalações novas podem
    // não estar criadas ainda)
    if ( ! rmbz_analytics_table_exists( $table_daily ) ) {
        return rmbz_api_ok( rmbz_api_stats_empty_summary() );
    }

    $range = rmbz_analytics_period_range( $period, $telemovel );
    $pa = rmbz_api_stats_approval_clause( $table_daily );

    // Lifetime
    $lifetime = $wpdb->get_row( $wpdb->prepare(
        "SELECT total_earnings, total_streams
         FROM $table_stats WHERE artist_telemovel = %s",
        $telemovel
    ) );
    $lifetime_streams  = $lifetime ? (int)   $lifetime->total_streams  : 0;
    $lifetime_earnings = $lifetime ? (float) $lifetime->total_earnings : 0.0;

    // Período actual
    $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",
        $telemovel, $range['from'], $range['to']
    ) );

    // Período anterior (para delta)
    $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",
        $telemovel, $range['prev_from'], $range['prev_to']
    ) );

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

    return rmbz_api_ok( [
        'period'   => $period,
        'date_from'=> $range['from'],
        'date_to'  => $range['to'],
        'lifetime' => [
            'streams'  => $lifetime_streams,
            'earnings' => round( $lifetime_earnings, 2 ),
        ],
        'current'  => [
            'streams'  => $streams_now,
            'earnings' => round( $earnings_now, 2 ),
        ],
        'previous' => [
            'streams'  => $streams_prev,
            'earnings' => round( $earn_prev, 2 ),
        ],
        'delta' => [
            // Pode ser null quando period='all' (sem período anterior comparável)
            'streams_pct'  => $period === 'all' ? null : rmbz_analytics_delta( $streams_now,  $streams_prev ),
            'earnings_pct' => $period === 'all' ? null : rmbz_analytics_delta( $earnings_now, $earn_prev ),
        ],
        'currency' => 'USD',  // distro_daily_stats guarda em USD (por confirmar com produto)
    ] );
}

/**
 * Resposta vazia quando não há tabelas/dados.
 */
function rmbz_api_stats_empty_summary() {
    return [
        'period'   => '30d',
        'lifetime' => [ 'streams' => 0, 'earnings' => 0.0 ],
        'current'  => [ 'streams' => 0, 'earnings' => 0.0 ],
        'previous' => [ 'streams' => 0, 'earnings' => 0.0 ],
        'delta'    => [ 'streams_pct' => 0.0, 'earnings_pct' => 0.0 ],
        'currency' => 'USD',
        'empty'    => true,
    ];
}

/* ============================================================================
 * GET /stats/timeline
 *
 * Retorna série temporal agregada por bucket (day/week/month conforme período).
 * Cada item: { date, streams, earnings }.
 *
 * Os buckets vazios são preenchidos com 0 (importante para gráficos contínuos).
 * ============================================================================ */
function rmbz_api_handle_stats_timeline( WP_REST_Request $request ) {
    global $wpdb;
    $telemovel = (string) $request->get_param( 'rmbz_telemovel' );
    $period    = rmbz_api_stats_validate_period( $request->get_param( 'period' ) );

    $table_daily = $wpdb->prefix . 'rmbz_distro_daily_stats';

    if ( ! rmbz_analytics_table_exists( $table_daily ) ) {
        return rmbz_api_ok( [ 'period' => $period, 'bucket' => 'day', 'series' => [] ] );
    }

    $range = rmbz_analytics_period_range( $period, $telemovel );
    $pa = rmbz_api_stats_approval_clause( $table_daily );

    // DATE_FORMAT tokens precisam de %% para sobreviver ao $wpdb->prepare
    // (caso contrário interpretado como placeholders vazios).
    $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'] ];

    $rows = $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
         GROUP BY bucket
         ORDER BY bucket ASC",
        $telemovel, $range['from'], $range['to']
    ) );

    // Preencher buckets vazios para gráfico contínuo
    $filled = function_exists( 'rmbz_fill_empty_buckets' )
        ? rmbz_fill_empty_buckets( $rows, $range )
        : ( is_array($rows) ? $rows : [] );

    $series = [];
    foreach ( $filled as $row ) {
        $series[] = [
            'date'     => $row->bucket,
            'streams'  => (int) $row->s,
            'earnings' => round( (float) $row->e, 2 ),
        ];
    }

    return rmbz_api_ok( [
        'period'    => $period,
        'date_from' => $range['from'],
        'date_to'   => $range['to'],
        'bucket'    => $range['bucket'],  // 'day' | 'week' | 'month'
        'series'    => $series,
    ] );
}

/* ============================================================================
 * GET /stats/top-tracks
 *
 * Retorna as top N faixas por streams no período.
 * Cada item inclui dados básicos da faixa + release + capa.
 * ============================================================================ */
function rmbz_api_handle_stats_top_tracks( WP_REST_Request $request ) {
    global $wpdb;
    $telemovel = (string) $request->get_param( 'rmbz_telemovel' );
    $period    = rmbz_api_stats_validate_period( $request->get_param( 'period' ) );
    $limit     = (int) $request->get_param( 'limit' );
    if ( $limit < 1 )  $limit = 10;
    if ( $limit > 50 ) $limit = 50;

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

    if ( ! rmbz_analytics_table_exists( $table_daily ) ) {
        return rmbz_api_ok( [ 'period' => $period, 'tracks' => [] ] );
    }

    $range = rmbz_analytics_period_range( $period, $telemovel );

    $rows = $wpdb->get_results( $wpdb->prepare(
        "SELECT
            t.id            AS track_id,
            t.track_title   AS track_title,
            r.id            AS release_id,
            r.title         AS release_title,
            r.cover_image   AS cover_image,
            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 %d",
        $telemovel, $range['from'], $range['to'], $limit
    ) );

    $tracks = [];
    if ( is_array($rows) ) {
        foreach ( $rows as $row ) {
            $tracks[] = [
                'track_id'      => (int)  $row->track_id,
                'track_title'   => (string) $row->track_title,
                'release_id'    => (int)  $row->release_id,
                'release_title' => (string) $row->release_title,
                'cover_url'     => $row->cover_image ?: null,
                'streams'       => (int) $row->streams,
                'earnings'      => round( (float) $row->earnings, 2 ),
            ];
        }
    }

    return rmbz_api_ok( [
        'period'    => $period,
        'date_from' => $range['from'],
        'date_to'   => $range['to'],
        'tracks'    => $tracks,
    ] );
}

/* ============================================================================
 * GET /stats/top-platforms
 *
 * Top DSPs por streams. Cada item: { store, streams, earnings, percentage }.
 * ============================================================================ */
function rmbz_api_handle_stats_top_platforms( WP_REST_Request $request ) {
    global $wpdb;
    $telemovel = (string) $request->get_param( 'rmbz_telemovel' );
    $period    = rmbz_api_stats_validate_period( $request->get_param( 'period' ) );

    $table_daily = $wpdb->prefix . 'rmbz_distro_daily_stats';

    if ( ! rmbz_analytics_table_exists( $table_daily ) ) {
        return rmbz_api_ok( [ 'period' => $period, 'platforms' => [] ] );
    }

    $range = rmbz_analytics_period_range( $period, $telemovel );
    $pa = rmbz_api_stats_approval_clause( $table_daily );

    $rows = $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
         GROUP BY store
         ORDER BY s DESC
         LIMIT 12",
        $telemovel, $range['from'], $range['to']
    ) );

    if ( ! is_array($rows) ) $rows = [];
    $total_streams = array_sum( array_map( fn($r) => (int) $r->s, $rows ) );

    $platforms = [];
    foreach ( $rows as $row ) {
        $streams = (int) $row->s;
        $platforms[] = [
            'store'      => (string) $row->store,
            'streams'    => $streams,
            'earnings'   => round( (float) $row->e, 2 ),
            'percentage' => $total_streams > 0
                ? round( ( $streams / $total_streams ) * 100, 1 )
                : 0.0,
        ];
    }

    return rmbz_api_ok( [
        'period'        => $period,
        'date_from'     => $range['from'],
        'date_to'       => $range['to'],
        'total_streams' => $total_streams,
        'platforms'     => $platforms,
    ] );
}

/* ============================================================================
 * GET /stats/top-countries
 *
 * Top países por streams. Cada item: { country_code, flag, streams, earnings,
 * percentage }.
 * ============================================================================ */
function rmbz_api_handle_stats_top_countries( WP_REST_Request $request ) {
    global $wpdb;
    $telemovel = (string) $request->get_param( 'rmbz_telemovel' );
    $period    = rmbz_api_stats_validate_period( $request->get_param( 'period' ) );

    $table_daily = $wpdb->prefix . 'rmbz_distro_daily_stats';

    if ( ! rmbz_analytics_table_exists( $table_daily ) ) {
        return rmbz_api_ok( [ 'period' => $period, 'countries' => [] ] );
    }

    $range = rmbz_analytics_period_range( $period, $telemovel );
    $pa = rmbz_api_stats_approval_clause( $table_daily );

    $rows = $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
         GROUP BY country_code
         ORDER BY s DESC
         LIMIT 15",
        $telemovel, $range['from'], $range['to']
    ) );

    if ( ! is_array($rows) ) $rows = [];
    $total_streams = array_sum( array_map( fn($r) => (int) $r->s, $rows ) );

    $countries = [];
    foreach ( $rows as $row ) {
        $streams = (int) $row->s;
        $code = (string) $row->country_code;
        $countries[] = [
            'country_code' => $code,
            // Bandeira emoji para o app mostrar facilmente. A função
            // rmbz_country_flag() existe em distribution-analytics.php.
            'flag'         => function_exists( 'rmbz_country_flag' )
                                ? rmbz_country_flag( $code )
                                : '',
            'streams'      => $streams,
            'earnings'     => round( (float) $row->e, 2 ),
            'percentage'   => $total_streams > 0
                ? round( ( $streams / $total_streams ) * 100, 1 )
                : 0.0,
        ];
    }

    return rmbz_api_ok( [
        'period'        => $period,
        'date_from'     => $range['from'],
        'date_to'       => $range['to'],
        'total_streams' => $total_streams,
        'countries'     => $countries,
    ] );
}

/* ============================================================================
 * GET /stats/track/{id}?period=30d
 *
 * [Polish v2] Drill-down — devolve detalhes de uma faixa específica.
 * Inclui: dados básicos da faixa, summary, timeline, top países, top platforms.
 *
 * Validação: a faixa tem que pertencer ao artista logado, caso contrário 403.
 * ============================================================================ */
function rmbz_api_handle_stats_track_detail( WP_REST_Request $request ) {
    global $wpdb;
    $telemovel = (string) $request->get_param( 'rmbz_telemovel' );
    $track_id  = (int)    $request->get_param( 'id' );
    $period    = rmbz_api_stats_validate_period( $request->get_param( 'period' ) );

    if ( $track_id <= 0 ) {
        return rmbz_api_err( 'invalid_id', 'ID de faixa inválido.', 400 );
    }

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

    if ( ! rmbz_analytics_table_exists( $table_daily ) ) {
        return rmbz_api_err( 'no_data', 'Sistema de stats não disponível.', 503 );
    }

    // 1) Buscar info da faixa + validar que pertence a este artista
    //    (isto também serve como autorização — sem dados associados ao
    //    artist_telemovel não há resposta).
    $track = $wpdb->get_row( $wpdb->prepare(
        "SELECT t.id, t.track_title, r.id AS release_id, r.title AS release_title, r.cover_image
         FROM $table_tracks t
         JOIN $table_releases r ON t.release_id = r.id
         WHERE t.id = %d
         LIMIT 1",
        $track_id
    ) );

    if ( ! $track ) {
        return rmbz_api_err( 'track_not_found', 'Faixa não encontrada.', 404 );
    }

    // Confirmar que o artista tem dados associados a esta faixa
    $owns = (int) $wpdb->get_var( $wpdb->prepare(
        "SELECT COUNT(*) FROM $table_daily
         WHERE artist_telemovel = %s AND track_id = %d
         LIMIT 1",
        $telemovel, $track_id
    ) );
    if ( $owns === 0 ) {
        return rmbz_api_err( 'forbidden', 'Sem permissão para ver stats desta faixa.', 403 );
    }

    $range = rmbz_analytics_period_range( $period, $telemovel );
    $pa    = rmbz_api_stats_approval_clause( $table_daily );

    // 2) Lifetime da faixa (totals desde sempre)
    $lifetime = $wpdb->get_row( $wpdb->prepare(
        "SELECT COALESCE(SUM(streams),0) s, COALESCE(SUM(sales),0) e
         FROM $table_daily
         WHERE artist_telemovel = %s AND track_id = %d $pa",
        $telemovel, $track_id
    ) );
    $lifetime_streams  = $lifetime ? (int)   $lifetime->s : 0;
    $lifetime_earnings = $lifetime ? (float) $lifetime->e : 0.0;

    // 3) Período actual
    $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 track_id = %d
           AND report_date BETWEEN %s AND %s $pa",
        $telemovel, $track_id, $range['from'], $range['to']
    ) );

    // 4) Período anterior (para delta)
    $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 track_id = %d
           AND report_date BETWEEN %s AND %s $pa",
        $telemovel, $track_id, $range['prev_from'], $range['prev_to']
    ) );

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

    // 5) Timeline (mesmo padrão do summary global, mas filtrado por track_id)
    $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'] ];

    $rows = $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 track_id = %d
           AND report_date BETWEEN %s AND %s $pa
         GROUP BY bucket
         ORDER BY bucket ASC",
        $telemovel, $track_id, $range['from'], $range['to']
    ) );

    $filled = function_exists( 'rmbz_fill_empty_buckets' )
        ? rmbz_fill_empty_buckets( $rows, $range )
        : ( is_array($rows) ? $rows : [] );

    $series = [];
    foreach ( $filled as $row ) {
        $series[] = [
            'date'     => $row->bucket,
            'streams'  => (int) $row->s,
            'earnings' => round( (float) $row->e, 2 ),
        ];
    }

    // 6) Top países desta faixa
    $country_rows = $wpdb->get_results( $wpdb->prepare(
        "SELECT country_code, SUM(streams) s, SUM(sales) e
         FROM $table_daily
         WHERE artist_telemovel = %s AND track_id = %d
           AND report_date BETWEEN %s AND %s $pa
         GROUP BY country_code
         ORDER BY s DESC
         LIMIT 10",
        $telemovel, $track_id, $range['from'], $range['to']
    ) );
    if ( ! is_array($country_rows) ) $country_rows = [];
    $c_total = array_sum( array_map( fn($r) => (int) $r->s, $country_rows ) );

    $countries = [];
    foreach ( $country_rows as $row ) {
        $streams = (int) $row->s;
        $code = (string) $row->country_code;
        $countries[] = [
            'country_code' => $code,
            'flag'         => function_exists( 'rmbz_country_flag' ) ? rmbz_country_flag( $code ) : '',
            'streams'      => $streams,
            'earnings'     => round( (float) $row->e, 2 ),
            'percentage'   => $c_total > 0 ? round( ( $streams / $c_total ) * 100, 1 ) : 0.0,
        ];
    }

    // 7) Top plataformas desta faixa
    $store_rows = $wpdb->get_results( $wpdb->prepare(
        "SELECT store, SUM(streams) s, SUM(sales) e
         FROM $table_daily
         WHERE artist_telemovel = %s AND track_id = %d
           AND report_date BETWEEN %s AND %s $pa
         GROUP BY store
         ORDER BY s DESC
         LIMIT 10",
        $telemovel, $track_id, $range['from'], $range['to']
    ) );
    if ( ! is_array($store_rows) ) $store_rows = [];
    $s_total = array_sum( array_map( fn($r) => (int) $r->s, $store_rows ) );

    $platforms = [];
    foreach ( $store_rows as $row ) {
        $streams = (int) $row->s;
        $platforms[] = [
            'store'      => (string) $row->store,
            'streams'    => $streams,
            'earnings'   => round( (float) $row->e, 2 ),
            'percentage' => $s_total > 0 ? round( ( $streams / $s_total ) * 100, 1 ) : 0.0,
        ];
    }

    return rmbz_api_ok( [
        'track_id'      => (int)    $track->id,
        'track_title'   => (string) $track->track_title,
        'release_id'    => (int)    $track->release_id,
        'release_title' => (string) $track->release_title,
        'cover_url'     => $track->cover_image ?: null,
        'period'        => $period,
        'date_from'     => $range['from'],
        'date_to'       => $range['to'],
        'lifetime' => [
            'streams'  => $lifetime_streams,
            'earnings' => round( $lifetime_earnings, 2 ),
        ],
        'current' => [
            'streams'  => $streams_now,
            'earnings' => round( $earnings_now, 2 ),
        ],
        'previous' => [
            'streams'  => $streams_prev,
            'earnings' => round( $earn_prev, 2 ),
        ],
        'delta' => [
            'streams_pct'  => $period === 'all' ? null : rmbz_analytics_delta( $streams_now,  $streams_prev ),
            'earnings_pct' => $period === 'all' ? null : rmbz_analytics_delta( $earnings_now, $earn_prev ),
        ],
        'currency'  => 'USD',
        'bucket'    => $range['bucket'],
        'series'    => $series,
        'countries' => $countries,
        'platforms' => $platforms,
    ] );
}

/* ============================================================================
 * [1.5.226] GET /stats/release/{id}?period=all
 *
 * Relatório de ganhos de um RELEASE inteiro. Igual ao track detail
 * mas filtrado por release_id (via JOIN tracks→daily) e com a lista
 * de faixas do release, cada uma com os seus totais.
 *
 * Se o release ainda não tem nenhuma linha de daily_stats, devolve
 * has_data=false — o app mostra "ainda a acumular ganhos".
 * ============================================================================ */
function rmbz_api_handle_stats_release_detail( WP_REST_Request $request ) {
    global $wpdb;
    $telemovel  = (string) $request->get_param( 'rmbz_telemovel' );
    $release_id = (int)    $request->get_param( 'id' );
    $period     = rmbz_api_stats_validate_period( $request->get_param( 'period' ) );

    if ( $release_id <= 0 ) {
        return rmbz_api_err( 'invalid_id', 'ID de release inválido.', 400 );
    }

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

    if ( ! rmbz_analytics_table_exists( $table_daily ) ) {
        return rmbz_api_err( 'no_data', 'Sistema de stats não disponível.', 503 );
    }

    // 1) Info do release
    $release = $wpdb->get_row( $wpdb->prepare(
        "SELECT id, title, cover_image
         FROM $table_releases WHERE id = %d LIMIT 1",
        $release_id
    ) );
    if ( ! $release ) {
        return rmbz_api_err( 'release_not_found', 'Release não encontrado.', 404 );
    }

    $pa = rmbz_api_stats_approval_clause( $table_daily );

    // 2) Verificar se há dados — todas as linhas das faixas deste release.
    //    Usamos o track_id IN (faixas do release).
    $track_ids = (array) $wpdb->get_col( $wpdb->prepare(
        "SELECT id FROM $table_tracks WHERE release_id = %d",
        $release_id
    ) );
    $track_ids = array_map( 'intval', $track_ids );

    // Sem faixas ou sem linhas → "ainda a acumular".
    if ( empty( $track_ids ) ) {
        return rmbz_api_ok( [
            'release_id'    => (int) $release->id,
            'release_title' => (string) $release->title,
            'cover_url'     => $release->cover_image ?: null,
            'has_data'      => false,
        ] );
    }

    $ids_placeholder = implode( ',', array_fill( 0, count( $track_ids ), '%d' ) );

    // Quantas linhas de stats existem para este release/artista
    $has_rows = (int) $wpdb->get_var( $wpdb->prepare(
        "SELECT COUNT(*) FROM $table_daily
         WHERE artist_telemovel = %s
           AND track_id IN ($ids_placeholder) $pa",
        array_merge( [ $telemovel ], $track_ids )
    ) );

    if ( $has_rows === 0 ) {
        return rmbz_api_ok( [
            'release_id'    => (int) $release->id,
            'release_title' => (string) $release->title,
            'cover_url'     => $release->cover_image ?: null,
            'has_data'      => false,
        ] );
    }

    $range = rmbz_analytics_period_range( $period, $telemovel );

    // Helper: cláusula + params do "track_id IN (...)"
    $in_clause = "track_id IN ($ids_placeholder)";

    // 3) Lifetime (desde sempre)
    $lifetime = $wpdb->get_row( $wpdb->prepare(
        "SELECT COALESCE(SUM(streams),0) s, COALESCE(SUM(sales),0) e
         FROM $table_daily
         WHERE artist_telemovel = %s AND $in_clause $pa",
        array_merge( [ $telemovel ], $track_ids )
    ) );
    $lifetime_streams  = $lifetime ? (int)   $lifetime->s : 0;
    $lifetime_earnings = $lifetime ? (float) $lifetime->e : 0.0;

    // 4) Período actual
    $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 $in_clause
           AND report_date BETWEEN %s AND %s $pa",
        array_merge( [ $telemovel ], $track_ids, [ $range['from'], $range['to'] ] )
    ) );
    $streams_now  = $now ? (int)   $now->s : 0;
    $earnings_now = $now ? (float) $now->e : 0.0;

    // 5) Período anterior (delta)
    $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 $in_clause
           AND report_date BETWEEN %s AND %s $pa",
        array_merge( [ $telemovel ], $track_ids, [ $range['prev_from'], $range['prev_to'] ] )
    ) );
    $streams_prev = $prev ? (int)   $prev->s : 0;
    $earn_prev    = $prev ? (float) $prev->e : 0.0;

    // 6) Timeline
    $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'] ];

    $rows = $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 $in_clause
           AND report_date BETWEEN %s AND %s $pa
         GROUP BY bucket
         ORDER BY bucket ASC",
        array_merge( [ $telemovel ], $track_ids, [ $range['from'], $range['to'] ] )
    ) );
    $filled = function_exists( 'rmbz_fill_empty_buckets' )
        ? rmbz_fill_empty_buckets( $rows, $range )
        : ( is_array($rows) ? $rows : [] );

    $series = [];
    foreach ( $filled as $row ) {
        $series[] = [
            'date'     => $row->bucket,
            'streams'  => (int) $row->s,
            'earnings' => round( (float) $row->e, 2 ),
        ];
    }

    // 7) Top países
    $country_rows = $wpdb->get_results( $wpdb->prepare(
        "SELECT country_code, SUM(streams) s, SUM(sales) e
         FROM $table_daily
         WHERE artist_telemovel = %s AND $in_clause
           AND report_date BETWEEN %s AND %s $pa
         GROUP BY country_code
         ORDER BY s DESC
         LIMIT 10",
        array_merge( [ $telemovel ], $track_ids, [ $range['from'], $range['to'] ] )
    ) );
    if ( ! is_array($country_rows) ) $country_rows = [];
    $c_total = array_sum( array_map( fn($r) => (int) $r->s, $country_rows ) );

    $countries = [];
    foreach ( $country_rows as $row ) {
        $streams = (int) $row->s;
        $code = (string) $row->country_code;
        $countries[] = [
            'country_code' => $code,
            'flag'         => function_exists( 'rmbz_country_flag' ) ? rmbz_country_flag( $code ) : '',
            'streams'      => $streams,
            'earnings'     => round( (float) $row->e, 2 ),
            'percentage'   => $c_total > 0 ? round( ( $streams / $c_total ) * 100, 1 ) : 0.0,
        ];
    }

    // 8) Top plataformas
    $store_rows = $wpdb->get_results( $wpdb->prepare(
        "SELECT store, SUM(streams) s, SUM(sales) e
         FROM $table_daily
         WHERE artist_telemovel = %s AND $in_clause
           AND report_date BETWEEN %s AND %s $pa
         GROUP BY store
         ORDER BY s DESC
         LIMIT 10",
        array_merge( [ $telemovel ], $track_ids, [ $range['from'], $range['to'] ] )
    ) );
    if ( ! is_array($store_rows) ) $store_rows = [];
    $s_total = array_sum( array_map( fn($r) => (int) $r->s, $store_rows ) );

    $platforms = [];
    foreach ( $store_rows as $row ) {
        $streams = (int) $row->s;
        $platforms[] = [
            'store'      => (string) $row->store,
            'streams'    => $streams,
            'earnings'   => round( (float) $row->e, 2 ),
            'percentage' => $s_total > 0 ? round( ( $streams / $s_total ) * 100, 1 ) : 0.0,
        ];
    }

    // 9) Breakdown por faixa — lifetime de cada faixa do release.
    $track_rows = $wpdb->get_results( $wpdb->prepare(
        "SELECT t.id AS track_id, t.track_title,
                COALESCE(SUM(d.streams),0) AS streams,
                COALESCE(SUM(d.sales),0)   AS earnings
         FROM $table_tracks t
         LEFT JOIN $table_daily d
                ON d.track_id = t.id
               AND d.artist_telemovel = %s $pa
         WHERE t.release_id = %d
         GROUP BY t.id
         ORDER BY earnings DESC",
        $telemovel, $release_id
    ) );

    $tracks = [];
    foreach ( (array) $track_rows as $row ) {
        $tracks[] = [
            'track_id'    => (int) $row->track_id,
            'track_title' => (string) ( $row->track_title ?: 'Faixa' ),
            'streams'     => (int) $row->streams,
            'earnings'    => round( (float) $row->earnings, 2 ),
        ];
    }

    return rmbz_api_ok( [
        'release_id'    => (int) $release->id,
        'release_title' => (string) $release->title,
        'cover_url'     => $release->cover_image ?: null,
        'has_data'      => true,
        'period'        => $period,
        'date_from'     => $range['from'],
        'date_to'       => $range['to'],
        'lifetime' => [
            'streams'  => $lifetime_streams,
            'earnings' => round( $lifetime_earnings, 2 ),
        ],
        'current' => [
            'streams'  => $streams_now,
            'earnings' => round( $earnings_now, 2 ),
        ],
        'previous' => [
            'streams'  => $streams_prev,
            'earnings' => round( $earn_prev, 2 ),
        ],
        'delta' => [
            'streams_pct'  => $period === 'all' ? null : rmbz_analytics_delta( $streams_now,  $streams_prev ),
            'earnings_pct' => $period === 'all' ? null : rmbz_analytics_delta( $earnings_now, $earn_prev ),
        ],
        'currency'  => 'USD',
        'bucket'    => $range['bucket'],
        'series'    => $series,
        'countries' => $countries,
        'platforms' => $platforms,
        'tracks'    => $tracks,
    ] );
}

/* ============================================================================
 * [1.5.210] GET /stats/imports
 *
 * Lista os imports do admin onde este artista tem pelo menos 1 linha
 * de daily_stats (i.e., relatórios que afectaram este artista).
 *
 * Cada item:
 *   {
 *     "id":              123,
 *     "source_name":     "1MZ For Artists",
 *     "reporting_period": "2026-04",
 *     "imported_at":     "2026-05-10 14:32:11",
 *     "approval_status": "approved",
 *     "earnings_usd":    642.92,
 *     "streams":         34521,
 *     "stores_count":    5,
 *     "countries_count": 12
 *   }
 *
 * Ordenado por imported_at DESC.
 * ============================================================================ */
function rmbz_api_handle_stats_imports_list( WP_REST_Request $request ) {
    global $wpdb;
    $telemovel = (string) $request->get_param( 'rmbz_telemovel' );
    $limit     = max( 1, min( 100, (int) $request->get_param( 'limit' ) ) );

    $t_logs  = $wpdb->prefix . 'rmbz_distro_import_logs';
    $t_daily = $wpdb->prefix . 'rmbz_distro_daily_stats';

    // Defensivo — tabelas podem não existir
    if ( ! rmbz_analytics_table_exists( $t_logs ) ||
         ! rmbz_analytics_table_exists( $t_daily ) ) {
        return rmbz_api_ok( [ 'imports' => [] ] );
    }

    // Verificar colunas em daily_stats — algumas instalações antigas
    // podem não ter import_log_id ou pending_approval ainda.
    $cols_daily = (array) $wpdb->get_col( "SHOW COLUMNS FROM $t_daily" );
    $has_log_id  = in_array( 'import_log_id', $cols_daily, true );
    $has_hash    = in_array( 'import_hash',   $cols_daily, true );
    $has_pending = in_array( 'pending_approval', $cols_daily, true );
    $approval_where = $has_pending ? 'AND ds.pending_approval = 0' : '';

    // [1.5.210.1] BUGFIX: linhas antigas têm import_log_id = NULL.
    // Têm de ser identificadas via import_hash (= file_hash do log).
    // Sem este fallback, artistas com ganhos só de imports antigos
    // viam "Sem relatórios ainda" mesmo tendo histórico real.
    if ( $has_log_id && $has_hash ) {
        $join_clause = "ds.import_log_id = il.id
                        OR ( ds.import_log_id IS NULL AND ds.import_hash = il.file_hash )";
    } elseif ( $has_hash ) {
        // Tabela ainda sem import_log_id — só liga por hash
        $join_clause = "ds.import_hash = il.file_hash";
    } else {
        // Pior caso: só liga por log_id (perde linhas antigas)
        $join_clause = "ds.import_log_id = il.id";
    }

    $sql = $wpdb->prepare(
        "SELECT
             il.id,
             il.source_name,
             il.reporting_period,
             il.imported_at,
             il.approval_status,
             COALESCE(SUM(ds.sales), 0)   AS earnings_usd,
             COALESCE(SUM(ds.streams), 0) AS streams,
             COUNT(DISTINCT ds.store)        AS stores_count,
             COUNT(DISTINCT ds.country_code) AS countries_count
         FROM $t_logs il
         INNER JOIN $t_daily ds ON ( $join_clause )
         WHERE il.status = 'active'
           AND ds.artist_telemovel = %s
           $approval_where
         GROUP BY il.id
         HAVING earnings_usd > 0 OR streams > 0
         ORDER BY il.imported_at DESC
         LIMIT %d",
        $telemovel, $limit
    );

    $rows = $wpdb->get_results( $sql );

    $imports = [];
    foreach ( (array) $rows as $r ) {
        $imports[] = [
            'id'              => (int) $r->id,
            // O fornecedor do relatório é informação operacional interna.
            'source_name'     => '1MZ For Artists',
            'reporting_period'=> (string) ( $r->reporting_period ?? '' ),
            'imported_at'     => (string) $r->imported_at,
            'approval_status' => (string) ( $r->approval_status ?? 'approved' ),
            'earnings_usd'    => round( (float) $r->earnings_usd, 2 ),
            'streams'         => (int) $r->streams,
            'stores_count'    => (int) $r->stores_count,
            'countries_count' => (int) $r->countries_count,
        ];
    }

    return rmbz_api_ok( [
        'imports' => $imports,
        'currency'=> 'USD',
    ] );
}

/* ============================================================================
 * [1.5.210] GET /stats/imports/{id}
 *
 * Detalhe de UM import específico, com agregados para o artista actual.
 *
 * Devolve:
 *   {
 *     "import": {
 *       "id": 123,
 *       "source_name": "1MZ For Artists",
 *       "reporting_period": "2026-04",
 *       "imported_at": "...",
 *       "approval_status": "approved",
 *       "earnings_usd": 642.92,
 *       "streams": 34521
 *     },
 *     "top_stores":    [ { "name": "Spotify", "earnings_usd": 218.5, "share_pct": 34.0 }, ... 5 ],
 *     "top_countries": [ { "code": "PT", "name": "Portugal", "earnings_usd": 145.2, "share_pct": 22.6 }, ... 5 ],
 *     "currency": "USD"
 *   }
 * ============================================================================ */
function rmbz_api_handle_stats_import_detail( WP_REST_Request $request ) {
    global $wpdb;
    $telemovel = (string) $request->get_param( 'rmbz_telemovel' );
    $import_id = (int) $request->get_param( 'id' );

    if ( $import_id <= 0 ) {
        return rmbz_api_error( 'ID de import inválido.', 400 );
    }

    $t_logs  = $wpdb->prefix . 'rmbz_distro_import_logs';
    $t_daily = $wpdb->prefix . 'rmbz_distro_daily_stats';

    if ( ! rmbz_analytics_table_exists( $t_logs ) ||
         ! rmbz_analytics_table_exists( $t_daily ) ) {
        return rmbz_api_error( 'Sem dados de relatórios.', 404 );
    }

    // [1.5.210.2] BUGFIX: incluir file_hash no SELECT.
    // Sem isto, o fallback por hash (para linhas antigas com
    // import_log_id NULL) recebia string vazia e zerava os totais
    // — o detail mostrava 0 mesmo quando a lista mostrava valor real.
    $log = $wpdb->get_row( $wpdb->prepare(
        "SELECT id, source_name, reporting_period, imported_at,
                approval_status, status, file_hash
         FROM $t_logs WHERE id = %d",
        $import_id
    ) );

    if ( ! $log ) {
        return rmbz_api_error( 'Relatório não encontrado.', 404 );
    }

    if ( $log->status !== 'active' ) {
        return rmbz_api_error( 'Este relatório foi anulado.', 410 );
    }

    $cols_daily = (array) $wpdb->get_col( "SHOW COLUMNS FROM $t_daily" );
    $has_log_id  = in_array( 'import_log_id', $cols_daily, true );
    $has_hash    = in_array( 'import_hash',   $cols_daily, true );
    $has_pending = in_array( 'pending_approval', $cols_daily, true );
    $approval_where = $has_pending ? 'AND pending_approval = 0' : '';

    // [1.5.210.1] BUGFIX — fallback por hash para linhas antigas
    // (ver explicação na função list acima).
    if ( $has_log_id && $has_hash ) {
        $row_match = "( import_log_id = %d OR ( import_log_id IS NULL AND import_hash = %s ) )";
        $row_params = [ $import_id, (string) ( $log->file_hash ?? '' ) ];
    } elseif ( $has_hash ) {
        $row_match = "import_hash = %s";
        $row_params = [ (string) ( $log->file_hash ?? '' ) ];
    } else {
        $row_match = "import_log_id = %d";
        $row_params = [ $import_id ];
    }

    // Totais
    $totals_params = array_merge( $row_params, [ $telemovel ] );
    $totals = $wpdb->get_row( $wpdb->prepare(
        "SELECT
             COALESCE(SUM(sales), 0)   AS earnings_usd,
             COALESCE(SUM(streams), 0) AS streams
         FROM $t_daily
         WHERE $row_match
           AND artist_telemovel = %s
           $approval_where",
        $totals_params
    ) );

    $earnings = $totals ? (float) $totals->earnings_usd : 0.0;
    $streams  = $totals ? (int)   $totals->streams      : 0;

    // Top 5 lojas
    $top_stores_rows = $wpdb->get_results( $wpdb->prepare(
        "SELECT store, SUM(sales) AS earnings_usd, SUM(streams) AS streams
         FROM $t_daily
         WHERE $row_match
           AND artist_telemovel = %s
           $approval_where
         GROUP BY store
         ORDER BY earnings_usd DESC
         LIMIT 5",
        $totals_params
    ) );

    $top_stores = [];
    foreach ( (array) $top_stores_rows as $r ) {
        $row_earnings = (float) $r->earnings_usd;
        $top_stores[] = [
            'name'         => (string) ( $r->store ?: 'Outro' ),
            'earnings_usd' => round( $row_earnings, 2 ),
            'streams'      => (int) $r->streams,
            'share_pct'    => $earnings > 0
                ? round( ( $row_earnings / $earnings ) * 100, 1 )
                : 0.0,
        ];
    }

    // Top 5 países
    $top_countries_rows = $wpdb->get_results( $wpdb->prepare(
        "SELECT country_code, SUM(sales) AS earnings_usd, SUM(streams) AS streams
         FROM $t_daily
         WHERE $row_match
           AND artist_telemovel = %s
           $approval_where
         GROUP BY country_code
         ORDER BY earnings_usd DESC
         LIMIT 5",
        $totals_params
    ) );

    $top_countries = [];
    foreach ( (array) $top_countries_rows as $r ) {
        $row_earnings = (float) $r->earnings_usd;
        $code = (string) ( $r->country_code ?: '' );
        $top_countries[] = [
            'code'         => $code,
            'name'         => rmbz_api_country_name( $code ),
            'earnings_usd' => round( $row_earnings, 2 ),
            'streams'      => (int) $r->streams,
            'share_pct'    => $earnings > 0
                ? round( ( $row_earnings / $earnings ) * 100, 1 )
                : 0.0,
        ];
    }

    // [1.5.224] Top 5 faixas — streams + ganhos por música deste import.
    // JOIN com as tabelas de tracks/releases para obter o título.
    $t_tracks   = $wpdb->prefix . 'rmbz_distro_tracks';
    $t_releases = $wpdb->prefix . 'rmbz_distro_releases';
    $top_tracks = [];

    if ( rmbz_analytics_table_exists( $t_tracks ) &&
         rmbz_analytics_table_exists( $t_releases ) ) {
        $top_tracks_rows = $wpdb->get_results( $wpdb->prepare(
            "SELECT t.id            AS track_id,
                    t.track_title   AS track_title,
                    r.title         AS release_title,
                    r.cover_image   AS cover_image,
                    SUM(d.sales)    AS earnings_usd,
                    SUM(d.streams)  AS streams
             FROM $t_daily d
             JOIN $t_tracks   t ON d.track_id = t.id
             JOIN $t_releases r ON t.release_id = r.id
             WHERE $row_match
               AND d.artist_telemovel = %s
               AND d.track_id IS NOT NULL
               $approval_where
             GROUP BY t.id
             ORDER BY earnings_usd DESC
             LIMIT 5",
            $totals_params
        ) );

        foreach ( (array) $top_tracks_rows as $r ) {
            $row_earnings = (float) $r->earnings_usd;
            $top_tracks[] = [
                'track_id'      => (int) $r->track_id,
                'track_title'   => (string) ( $r->track_title ?: 'Faixa' ),
                'release_title' => (string) ( $r->release_title ?? '' ),
                'cover_url'     => $r->cover_image ?: null,
                'earnings_usd'  => round( $row_earnings, 2 ),
                'streams'       => (int) $r->streams,
                'share_pct'     => $earnings > 0
                    ? round( ( $row_earnings / $earnings ) * 100, 1 )
                    : 0.0,
            ];
        }
    }

    // [1.5.225] Timeline diária — streams + ganhos por dia deste import.
    // Permite ao app desenhar o "gráfico detalhado" de cada relatório.
    $daily_rows = $wpdb->get_results( $wpdb->prepare(
        "SELECT report_date AS day,
                SUM(sales)   AS earnings_usd,
                SUM(streams) AS streams
         FROM $t_daily
         WHERE $row_match
           AND artist_telemovel = %s
           $approval_where
         GROUP BY report_date
         ORDER BY report_date ASC",
        $totals_params
    ) );

    $daily = [];
    foreach ( (array) $daily_rows as $r ) {
        $daily[] = [
            'day'          => (string) $r->day,
            'earnings_usd' => round( (float) $r->earnings_usd, 2 ),
            'streams'      => (int) $r->streams,
        ];
    }

    return rmbz_api_ok( [
        'import' => [
            'id'              => (int) $log->id,
            // Nunca expor ao artista o parceiro usado no processamento interno.
            'source_name'     => '1MZ For Artists',
            'reporting_period'=> (string) ( $log->reporting_period ?? '' ),
            'imported_at'     => (string) $log->imported_at,
            'approval_status' => (string) ( $log->approval_status ?? 'approved' ),
            'earnings_usd'    => round( $earnings, 2 ),
            'streams'         => $streams,
        ],
        'daily'         => $daily,
        'top_tracks'    => $top_tracks,
        'top_stores'    => $top_stores,
        'top_countries' => $top_countries,
        'currency'      => 'USD',
    ] );
}

/**
 * [1.5.210] Helper — nome legível de país a partir de ISO code.
 * Cobre os países mais comuns; resto cai num fallback com o próprio code.
 */
function rmbz_api_country_name( $code ) {
    $code = strtoupper( trim( (string) $code ) );
    if ( $code === '' ) return 'Desconhecido';

    static $map = [
        'PT' => 'Portugal',           'MZ' => 'Moçambique',
        'BR' => 'Brasil',             'AO' => 'Angola',
        'CV' => 'Cabo Verde',         'GW' => 'Guiné-Bissau',
        'ST' => 'São Tomé e Príncipe','TL' => 'Timor-Leste',
        'US' => 'Estados Unidos',     'GB' => 'Reino Unido',
        'FR' => 'França',             'DE' => 'Alemanha',
        'ES' => 'Espanha',            'IT' => 'Itália',
        'NL' => 'Países Baixos',      'BE' => 'Bélgica',
        'CH' => 'Suíça',              'SE' => 'Suécia',
        'NO' => 'Noruega',            'DK' => 'Dinamarca',
        'FI' => 'Finlândia',          'PL' => 'Polónia',
        'RU' => 'Rússia',             'UA' => 'Ucrânia',
        'CA' => 'Canadá',             'MX' => 'México',
        'AR' => 'Argentina',          'CL' => 'Chile',
        'CO' => 'Colômbia',           'PE' => 'Peru',
        'VE' => 'Venezuela',
        'JP' => 'Japão',              'CN' => 'China',
        'KR' => 'Coreia do Sul',      'IN' => 'Índia',
        'AU' => 'Austrália',          'NZ' => 'Nova Zelândia',
        'ZA' => 'África do Sul',      'NG' => 'Nigéria',
        'KE' => 'Quénia',             'EG' => 'Egipto',
        'MA' => 'Marrocos',
    ];
    return $map[ $code ] ?? $code;
}
