<?php
/**
 * [1.6.118] RETRO-FIX FAIXAS SILENCIOSAS
 *
 * Ferramenta para varrer CSVs arquivados e actualizar `last_seen_in_csv`
 * retroactivamente, resolvendo faixas que foram vistas em CSVs antigos
 * (antes do FEAT 1.6.18) mas ficaram marcadas como "silenciosas".
 *
 * Fluxo:
 *   1. Admin abre sub-tab "🔧 Retro-fix"
 *   2. Vê lista de CSVs arquivados disponíveis (count + size)
 *   3. Clica "Analisar (dry-run)" → varre todos os CSVs, faz match
 *      por ISRC + UPC + título+artista, e mostra preview do que vai
 *      actualizar (sem mexer em nada)
 *   4. Vê preview com: faixa actual `last_seen_in_csv` → nova data
 *   5. Clica "Aplicar" (com confirmação) → faz UPDATE em batch
 *   6. Resultado: relatório final com nº de faixas actualizadas + log
 *
 * SAFETY:
 *   - Dry-run obrigatório antes de apply (não há "apply directo")
 *   - Só ACTUALIZA datas que sejam MAIS RECENTES (GREATEST) — nunca
 *     anda para trás no tempo
 *   - Não cria entradas em ledger/daily_stats (não afecta royalties).
 *     Só corrige `last_seen_in_csv` que é diagnóstico.
 *
 * @package 1MZ_For_Artists
 * @since 1.6.118
 */

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


/**
 * Sub-rota dentro da página "Faixas Silenciosas".
 *
 * Chamada pelo `rmbz_render_royalties_silent_tracks` quando
 * `?sub=retrofix` está na URL.
 */
function rmbz_render_silent_retrofix() {
    if ( ! current_user_can( 'manage_options' ) ) {
        wp_die( 'Sem permissão.' );
    }

    global $wpdb;
    $t_logs = $wpdb->prefix . 'rmbz_distro_import_logs';

    // Coluna existe?
    if ( ! $wpdb->get_var( "SHOW COLUMNS FROM $t_logs LIKE 'archived_path'" ) ) {
        echo '<div class="notice notice-warning"><p>';
        echo '⚠ A coluna <code>archived_path</code> ainda não existe. Os CSVs novos serão arquivados automaticamente, mas não há histórico para retro-fix.';
        echo '</p></div>';
        return;
    }

    // Listar imports arquivados (que têm ficheiro existente no disco)
    $logs = $wpdb->get_results(
        "SELECT id, source_name, reporting_period, archived_path,
                imported_at, total_processed, total_amount_usd
           FROM $t_logs
          WHERE archived_path IS NOT NULL AND archived_path != ''
          ORDER BY imported_at DESC
          LIMIT 200"
    );

    // Filtrar apenas os que ainda têm CSV disponível
    $available = [];
    foreach ( $logs as $log ) {
        if ( function_exists( 'rmbz_archive_csv_exists' ) && rmbz_archive_csv_exists( $log->archived_path ) ) {
            $available[] = $log;
        }
    }

    rmbz_render_silent_retrofix_styles();
    ?>
    <div class="rmbz-rfx-wrap">
        <div class="rmbz-rfx-intro">
            <h2 style="margin:0 0 6px 0;">🔧 Retro-fix de Faixas Silenciosas</h2>
            <p style="margin:0; color:#666; font-size:13px;">
                Varre os CSVs arquivados e actualiza <code>last_seen_in_csv</code> retroactivamente
                para faixas que apareceram em relatórios antigos mas ficaram marcadas como silenciosas
                (bug pre-1.6.18 onde microcêntimos não actualizavam a data).
            </p>
        </div>

        <?php if ( empty( $available ) ): ?>
            <div class="notice notice-info"><p>
                Não há CSVs arquivados disponíveis para retro-fix.
            </p></div>
        <?php else: ?>
            <div class="rmbz-rfx-card">
                <h3 style="margin:0 0 12px 0; font-size:14px;">📦 CSVs disponíveis para análise</h3>
                <table class="widefat striped" style="font-size:12.5px;">
                    <thead><tr>
                        <th>Importado em</th>
                        <th>Source</th>
                        <th>Período</th>
                        <th>Linhas</th>
                        <th>Total USD</th>
                        <th>Arquivo</th>
                    </tr></thead>
                    <tbody>
                        <?php foreach ( $available as $log ):
                            $size = @filesize( $log->archived_path );
                            $size_mb = $size > 0 ? round( $size / 1024 / 1024, 2 ) : 0;
                        ?>
                            <tr>
                                <td><?php echo esc_html( date( 'd/m/Y H:i', strtotime( $log->imported_at ) ) ); ?></td>
                                <td><code><?php echo esc_html( $log->source_name ); ?></code></td>
                                <td><?php echo esc_html( $log->reporting_period ); ?></td>
                                <td><?php echo number_format( (int) $log->total_processed ); ?></td>
                                <td>$<?php echo number_format( (float) $log->total_amount_usd, 2 ); ?></td>
                                <td><?php echo $size_mb; ?> MB</td>
                            </tr>
                        <?php endforeach; ?>
                    </tbody>
                </table>
                <p style="margin-top:8px; color:#888; font-size:11.5px;">
                    Total: <strong><?php echo count( $available ); ?></strong> CSVs disponíveis.
                </p>
            </div>

            <div class="rmbz-rfx-card rmbz-rfx-actions">
                <h3 style="margin:0 0 8px 0; font-size:14px;">🔍 Analisar (dry-run)</h3>
                <p style="font-size:12.5px; color:#666; margin:0 0 14px;">
                    Varre todos os CSVs e produz um <strong>relatório do que seria actualizado</strong>.
                    Não muda nada no BD. Demora alguns segundos por CSV — sê paciente em datasets grandes.
                </p>
                <form method="post" id="rmbz-rfx-dry-form">
                    <?php wp_nonce_field( 'rmbz_rfx_dry_run', '_rmbz_rfx_nonce' ); ?>
                    <input type="hidden" name="rmbz_rfx_action" value="dry_run">
                    <button type="submit" class="button button-primary" id="rmbz-rfx-dry-btn">
                        🔍 Analisar todos os CSVs
                    </button>
                    <p style="font-size:11.5px; color:#888; margin:8px 0 0;">
                        Vai analisar <strong><?php echo count( $available ); ?></strong> CSVs com
                        match por ISRC + UPC + título+artista (fuzzy).
                    </p>
                </form>
            </div>

            <?php
            // Se foi submetido um dry-run, processar e mostrar resultados
            if ( isset( $_POST['rmbz_rfx_action'] ) && $_POST['rmbz_rfx_action'] === 'dry_run' ) {
                if ( ! wp_verify_nonce( $_POST['_rmbz_rfx_nonce'] ?? '', 'rmbz_rfx_dry_run' ) ) {
                    echo '<div class="notice notice-error"><p>Nonce inválido.</p></div>';
                } else {
                    rmbz_render_silent_retrofix_dry_run_results( $available );
                }
            }

            // Se foi submetido um apply, processar e mostrar resultado
            if ( isset( $_POST['rmbz_rfx_action'] ) && $_POST['rmbz_rfx_action'] === 'apply' ) {
                if ( ! wp_verify_nonce( $_POST['_rmbz_rfx_nonce'] ?? '', 'rmbz_rfx_apply' ) ) {
                    echo '<div class="notice notice-error"><p>Nonce inválido.</p></div>';
                } else {
                    rmbz_render_silent_retrofix_apply_results( $available );
                }
            }
            ?>
        <?php endif; ?>
    </div>
    <?php
}


/**
 * Analisa todos os CSVs e mostra preview do que seria actualizado.
 * Não muda nada no BD.
 */
function rmbz_render_silent_retrofix_dry_run_results( $logs ) {
    $start_time = microtime( true );
    $total_rows = 0;
    $total_matched = 0;
    $candidates = []; // [ track_id => [ 'old' => ..., 'new' => ..., 'isrc' => ..., 'title' => ..., 'match_via' => ... ] ]
    $unmatched_isrcs = []; // contagem por ISRC não-matched
    $errors = [];

    foreach ( $logs as $log ) {
        $result = rmbz_silent_retrofix_scan_csv( $log->archived_path );
        if ( isset( $result['error'] ) ) {
            $errors[] = $log->source_name . ' / ' . $log->reporting_period . ': ' . $result['error'];
            continue;
        }

        foreach ( $result['rows'] as $r ) {
            $total_rows++;

            // Match por ISRC + UPC + título+artista
            $match = rmbz_silent_retrofix_find_track( $r['isrc'], $r['upc'], $r['title'], $r['artist'] );

            if ( ! $match ) {
                if ( ! empty( $r['isrc'] ) ) {
                    $key = $r['isrc'];
                    $unmatched_isrcs[ $key ] = ( $unmatched_isrcs[ $key ] ?? 0 ) + 1;
                }
                continue;
            }

            $total_matched++;
            $track_id = $match['track_id'];
            $report_date = $r['report_date'];

            $existing = $candidates[ $track_id ] ?? null;
            if ( ! $existing || strcmp( $report_date, $existing['new'] ) > 0 ) {
                $candidates[ $track_id ] = [
                    'old'       => null, // será preenchido a seguir
                    'new'       => $report_date,
                    'isrc'      => $r['isrc'],
                    'title'     => $match['track_title'],
                    'artist'    => $match['artist_telemovel'],
                    'match_via' => $match['matched_via'],
                ];
            }
        }
    }

    // Buscar `last_seen_in_csv` actual para cada candidato (e só manter
    // os que serão de facto actualizados — i.e., new > old)
    if ( ! empty( $candidates ) ) {
        global $wpdb;
        $t = $wpdb->prefix . 'rmbz_distro_tracks';
        $ids = array_keys( $candidates );
        $placeholders = implode( ',', array_fill( 0, count( $ids ), '%d' ) );
        $existing_rows = $wpdb->get_results( $wpdb->prepare(
            "SELECT id, last_seen_in_csv FROM $t WHERE id IN ($placeholders)",
            ...$ids
        ) );
        foreach ( $existing_rows as $row ) {
            $candidates[ $row->id ]['old'] = $row->last_seen_in_csv;
        }
        // Filtrar: só os que new > old (ou old é NULL)
        $candidates = array_filter( $candidates, function ( $c ) {
            return empty( $c['old'] ) || strcmp( $c['new'], $c['old'] ) > 0;
        } );
    }

    $elapsed = round( microtime( true ) - $start_time, 1 );
    $will_update = count( $candidates );
    ?>

    <div class="rmbz-rfx-results">
        <div class="rmbz-rfx-summary-grid">
            <div class="rmbz-rfx-stat">
                <div class="rmbz-rfx-stat-label">CSVs analisados</div>
                <div class="rmbz-rfx-stat-val"><?php echo count( $logs ); ?></div>
            </div>
            <div class="rmbz-rfx-stat">
                <div class="rmbz-rfx-stat-label">Linhas processadas</div>
                <div class="rmbz-rfx-stat-val"><?php echo number_format( $total_rows ); ?></div>
            </div>
            <div class="rmbz-rfx-stat">
                <div class="rmbz-rfx-stat-label">Faixas matched</div>
                <div class="rmbz-rfx-stat-val"><?php echo number_format( $total_matched ); ?></div>
            </div>
            <div class="rmbz-rfx-stat rmbz-rfx-stat-highlight">
                <div class="rmbz-rfx-stat-label">A actualizar</div>
                <div class="rmbz-rfx-stat-val"><?php echo $will_update; ?></div>
            </div>
        </div>

        <p style="color:#888; font-size:11.5px;">Tempo: <?php echo $elapsed; ?>s</p>

        <?php if ( ! empty( $errors ) ): ?>
            <div class="notice notice-warning">
                <p><strong>Avisos:</strong></p>
                <ul style="margin-left:20px;">
                    <?php foreach ( array_slice( $errors, 0, 5 ) as $err ): ?>
                        <li style="font-size:12px;"><?php echo esc_html( $err ); ?></li>
                    <?php endforeach; ?>
                </ul>
            </div>
        <?php endif; ?>

        <?php if ( $will_update === 0 ): ?>
            <div class="notice notice-success"><p>
                ✓ Nada para fazer — todas as faixas matched já têm <code>last_seen_in_csv</code> actualizado.
            </p></div>
        <?php else: ?>
            <div class="rmbz-rfx-card">
                <h3 style="margin:0 0 12px 0; font-size:14px;">📋 Faixas que serão actualizadas</h3>
                <table class="widefat striped" style="font-size:12.5px;">
                    <thead><tr>
                        <th>Track ID</th>
                        <th>Título</th>
                        <th>ISRC</th>
                        <th>Artista (tel)</th>
                        <th>last_seen actual</th>
                        <th>→ Nova data</th>
                        <th>Match via</th>
                    </tr></thead>
                    <tbody>
                        <?php
                        // Ordenar por nova data desc
                        uasort( $candidates, function ( $a, $b ) { return strcmp( $b['new'], $a['new'] ); } );
                        $shown = 0;
                        foreach ( $candidates as $tid => $c ):
                            if ( $shown++ >= 200 ) break;
                        ?>
                            <tr>
                                <td>#<?php echo (int) $tid; ?></td>
                                <td><?php echo esc_html( $c['title'] ?? '—' ); ?></td>
                                <td><code style="font-size:11px;"><?php echo esc_html( $c['isrc'] ?? '—' ); ?></code></td>
                                <td><code style="font-size:11px;"><?php echo esc_html( $c['artist'] ?? '—' ); ?></code></td>
                                <td style="color:#888;"><?php echo esc_html( $c['old'] ?? 'NULL' ); ?></td>
                                <td style="color:#10b981; font-weight:600;"><?php echo esc_html( $c['new'] ); ?></td>
                                <td>
                                    <?php
                                    $match_label = [
                                        'isrc'         => '<span style="color:#10b981;">ISRC ✓</span>',
                                        'upc_title'    => '<span style="color:#f59e0b;">UPC+título</span>',
                                        'fuzzy'        => '<span style="color:#ef4444;">FUZZY ⚠</span>',
                                    ];
                                    echo $match_label[ $c['match_via'] ?? '' ] ?? esc_html( $c['match_via'] );
                                    ?>
                                </td>
                            </tr>
                        <?php endforeach; ?>
                    </tbody>
                </table>
                <?php if ( $will_update > 200 ): ?>
                    <p style="margin-top:8px; color:#888; font-size:11px;">A mostrar primeiros 200 de <?php echo $will_update; ?>.</p>
                <?php endif; ?>
            </div>

            <div class="rmbz-rfx-card rmbz-rfx-apply-card">
                <h3 style="margin:0 0 8px 0; font-size:14px;">✅ Aplicar retro-fix</h3>
                <p style="font-size:12.5px; color:#666; margin:0 0 12px;">
                    Vai actualizar <strong><?php echo $will_update; ?></strong> faixas.
                    Usa <code>GREATEST(old, new)</code> — nunca anda para trás.
                    <strong>Não afecta royalties</strong> nem entradas no ledger; só corrige
                    o diagnóstico de "Faixas Silenciosas".
                </p>
                <form method="post" onsubmit="return confirm('Aplicar retro-fix em <?php echo $will_update; ?> faixas? Esta operação é IRREVERSÍVEL.');">
                    <?php wp_nonce_field( 'rmbz_rfx_apply', '_rmbz_rfx_nonce' ); ?>
                    <input type="hidden" name="rmbz_rfx_action" value="apply">
                    <button type="submit" class="button button-primary" style="background:#065f46; border-color:#065f46;">
                        ✅ Aplicar retro-fix (<?php echo $will_update; ?> faixas)
                    </button>
                </form>
            </div>
        <?php endif; ?>
    </div>
    <?php
}


/**
 * Aplica o retro-fix em batch. UPDATE com GREATEST garante que não
 * andamos para trás. Mostra resultado.
 */
function rmbz_render_silent_retrofix_apply_results( $logs ) {
    $start_time = microtime( true );
    $total_matched = 0;
    $candidates = [];

    // Re-fazer scan (não confiamos em state submetido pelo client)
    foreach ( $logs as $log ) {
        $result = rmbz_silent_retrofix_scan_csv( $log->archived_path );
        if ( isset( $result['error'] ) ) continue;
        foreach ( $result['rows'] as $r ) {
            $match = rmbz_silent_retrofix_find_track( $r['isrc'], $r['upc'], $r['title'], $r['artist'] );
            if ( ! $match ) continue;
            $total_matched++;
            $track_id = $match['track_id'];
            $report_date = $r['report_date'];
            $existing = $candidates[ $track_id ] ?? null;
            if ( ! $existing || strcmp( $report_date, $existing ) > 0 ) {
                $candidates[ $track_id ] = $report_date;
            }
        }
    }

    if ( empty( $candidates ) ) {
        echo '<div class="notice notice-info"><p>Nada para actualizar.</p></div>';
        return;
    }

    // UPDATEs por linha — usar GREATEST para nunca andar para trás
    global $wpdb;
    $t = $wpdb->prefix . 'rmbz_distro_tracks';
    $updated = 0;
    foreach ( $candidates as $tid => $new_date ) {
        $r = $wpdb->query( $wpdb->prepare(
            "UPDATE $t
                SET last_seen_in_csv = GREATEST(
                    IFNULL(last_seen_in_csv, '1900-01-01'),
                    %s
                )
              WHERE id = %d
                AND ( last_seen_in_csv IS NULL OR last_seen_in_csv < %s )",
            $new_date, (int) $tid, $new_date
        ) );
        if ( $r !== false && $r > 0 ) $updated++;
    }

    $elapsed = round( microtime( true ) - $start_time, 1 );

    // Log para auditoria
    error_log( sprintf(
        '[1MZ retro-fix] Silent tracks: %d updated of %d candidates (matched=%d) by user_id=%d',
        $updated, count( $candidates ), $total_matched, get_current_user_id()
    ) );

    ?>
    <div class="notice notice-success" style="padding:14px;">
        <h3 style="margin:0 0 8px 0;">✓ Retro-fix aplicado</h3>
        <p style="margin:0;">
            <strong><?php echo $updated; ?></strong> faixa(s) com <code>last_seen_in_csv</code> actualizado.
            <?php if ( $updated < count( $candidates ) ): ?>
                <br><span style="color:#888; font-size:12px;">
                    (<?php echo count( $candidates ) - $updated; ?> não precisaram de update — já tinham data mais recente)
                </span>
            <?php endif; ?>
        </p>
        <p style="margin:8px 0 0; font-size:12px; color:#666;">Tempo: <?php echo $elapsed; ?>s · Operação registada em log.</p>
    </div>
    <p>
        <a href="?page=rmbz-admin-distribution&tab=royalties&rsub=silent" class="button">← Voltar a Faixas Silenciosas</a>
    </p>
    <?php
}


/**
 * Varre 1 CSV arquivado e devolve as linhas relevantes.
 *
 * @return array { rows: [ {isrc, upc, title, artist, report_date}, ... ] } ou { error: string }
 */
function rmbz_silent_retrofix_scan_csv( $archive_path ) {
    if ( ! file_exists( $archive_path ) || ! is_readable( $archive_path ) ) {
        return [ 'error' => 'Ficheiro não acessível: ' . basename( $archive_path ) ];
    }

    $h = @fopen( $archive_path, 'r' );
    if ( ! $h ) {
        return [ 'error' => 'Não consegui abrir ' . basename( $archive_path ) ];
    }

    $header = fgetcsv( $h );
    if ( ! $header ) {
        fclose( $h );
        return [ 'error' => 'CSV vazio ou inválido.' ];
    }
    if ( isset( $header[0] ) ) $header[0] = preg_replace( '/^\xEF\xBB\xBF/', '', $header[0] );
    $header_lower = array_map( 'strtolower', array_map( 'trim', $header ) );

    // Tentar identificar índices das colunas relevantes
    $idx = [
        'isrc'   => false,
        'upc'    => false,
        'title'  => false,
        'artist' => false,
        'date'   => false,
    ];

    // Heurísticas tolerantes para diferentes formatos (TuneCore, Amadea, Zebralution, etc.)
    foreach ( $header_lower as $i => $col ) {
        $c = str_replace( [ '_', '-', '.' ], ' ', $col );
        if ( $idx['isrc'] === false && (
            $c === 'isrc' || strpos( $c, 'isrc' ) !== false
        ) ) {
            $idx['isrc'] = $i;
        }
        if ( $idx['upc'] === false && (
            $c === 'upc' || strpos( $c, 'upc' ) !== false || strpos( $c, 'ean' ) !== false
            || strpos( $c, 'barcode' ) !== false
        ) ) {
            $idx['upc'] = $i;
        }
        if ( $idx['title'] === false && (
            strpos( $c, 'track name' ) !== false || strpos( $c, 'song title' ) !== false
            || strpos( $c, 'track title' ) !== false || strpos( $c, 'song name' ) !== false
            || $c === 'title'
        ) ) {
            $idx['title'] = $i;
        }
        if ( $idx['artist'] === false && (
            strpos( $c, 'artist' ) !== false || strpos( $c, 'performer' ) !== false
        ) ) {
            $idx['artist'] = $i;
        }
        if ( $idx['date'] === false && (
            strpos( $c, 'report' ) !== false || strpos( $c, 'sale date' ) !== false
            || strpos( $c, 'period' ) !== false || strpos( $c, 'transaction date' ) !== false
            || strpos( $c, 'usage period' ) !== false || $c === 'date'
        ) ) {
            $idx['date'] = $i;
        }
    }

    // Pelo menos ISRC OU UPC tem de existir, ou (título + artista)
    $has_id  = ( $idx['isrc'] !== false ) || ( $idx['upc'] !== false );
    $has_txt = ( $idx['title'] !== false ) && ( $idx['artist'] !== false );
    if ( ! $has_id && ! $has_txt ) {
        fclose( $h );
        return [ 'error' => 'CSV sem colunas de ISRC, UPC ou Título+Artista detectáveis.' ];
    }

    $rows = [];
    $line = 0;
    while ( ( $row = fgetcsv( $h ) ) !== false ) {
        if ( ++$line > 500000 ) break; // safety cap

        $isrc = $idx['isrc'] !== false ? strtoupper( preg_replace( '/[^A-Za-z0-9]/', '', $row[ $idx['isrc'] ] ?? '' ) ) : '';
        $upc  = $idx['upc']  !== false ? preg_replace( '/[^0-9]/', '', $row[ $idx['upc'] ] ?? '' ) : '';
        $title  = $idx['title']  !== false ? trim( $row[ $idx['title'] ]  ?? '' ) : '';
        $artist = $idx['artist'] !== false ? trim( $row[ $idx['artist'] ] ?? '' ) : '';
        $date_raw = $idx['date'] !== false ? trim( $row[ $idx['date'] ] ?? '' ) : '';

        // Sem ID nem texto utilizável → skip
        if ( empty( $isrc ) && empty( $upc ) && ( empty( $title ) || empty( $artist ) ) ) continue;

        // Parse data — vários formatos comuns
        $report_date = rmbz_silent_retrofix_parse_date( $date_raw );
        if ( empty( $report_date ) ) {
            // Fallback: usar primeira do mês do reporting_period do log? Sem
            // acesso aqui — usar 1900-01-01 e deixar GREATEST filtrar.
            $report_date = '1900-01-01';
        }

        $rows[] = [
            'isrc'        => $isrc,
            'upc'         => $upc,
            'title'       => $title,
            'artist'      => $artist,
            'report_date' => $report_date,
        ];
    }
    fclose( $h );

    return [ 'rows' => $rows ];
}


/**
 * Tenta parsear uma data de várias formatos comuns nos CSVs.
 * Devolve 'YYYY-MM-DD' ou string vazia.
 */
function rmbz_silent_retrofix_parse_date( $raw ) {
    $raw = trim( (string) $raw );
    if ( empty( $raw ) ) return '';

    // Tentar formatos comuns
    $formats = [
        'Y-m-d', 'Y-m-d H:i:s', 'd/m/Y', 'm/d/Y', 'Y/m/d',
        'd-m-Y', 'M Y', 'F Y', 'Y-m', 'Y',
    ];
    foreach ( $formats as $f ) {
        $dt = DateTime::createFromFormat( $f, $raw );
        if ( $dt !== false ) {
            return $dt->format( 'Y-m-d' );
        }
    }

    // Strtotime fallback
    $ts = strtotime( $raw );
    if ( $ts !== false && $ts > 0 ) {
        return date( 'Y-m-d', $ts );
    }

    return '';
}


/**
 * Encontra um track no BD via cascata de critérios:
 *   1. ISRC normalizado (mais fiável)
 *   2. UPC + título normalizados (fallback)
 *   3. Título + artista normalizados (fuzzy — último recurso)
 *
 * @return array|null { track_id, track_title, artist_telemovel, matched_via } ou null
 */
function rmbz_silent_retrofix_find_track( $isrc, $upc, $title, $artist ) {
    static $cache = [];

    // Cache hit?
    $cache_key = "$isrc|$upc|$title|$artist";
    if ( isset( $cache[ $cache_key ] ) ) {
        return $cache[ $cache_key ];
    }

    global $wpdb;
    $t_tracks   = $wpdb->prefix . 'rmbz_distro_tracks';
    $t_releases = $wpdb->prefix . 'rmbz_distro_releases';
    $t_wallets  = $wpdb->prefix . 'rmbz_wallets';

    // ─ 1. ISRC normalizado ─────────────────────────────────────────
    if ( ! empty( $isrc ) ) {
        $row = $wpdb->get_row( $wpdb->prepare(
            "SELECT t.id AS track_id, t.track_title, r.artist_telemovel
               FROM $t_tracks t
               JOIN $t_releases r ON r.id = t.release_id
              WHERE UPPER(REPLACE(REPLACE(t.isrc_code, '-', ''), ' ', '')) = %s
              LIMIT 1",
            $isrc
        ) );
        if ( $row ) {
            $out = [
                'track_id'         => (int) $row->track_id,
                'track_title'      => $row->track_title,
                'artist_telemovel' => $row->artist_telemovel,
                'matched_via'      => 'isrc',
            ];
            return $cache[ $cache_key ] = $out;
        }
    }

    // ─ 2. UPC + título ────────────────────────────────────────────
    if ( ! empty( $upc ) && ! empty( $title ) ) {
        $title_norm = mb_strtolower( trim( $title ) );
        $row = $wpdb->get_row( $wpdb->prepare(
            "SELECT t.id AS track_id, t.track_title, r.artist_telemovel
               FROM $t_tracks t
               JOIN $t_releases r ON r.id = t.release_id
              WHERE REPLACE(REPLACE(r.upc_code, '-', ''), ' ', '') = %s
                AND LOWER(t.track_title) = %s
              LIMIT 1",
            $upc, $title_norm
        ) );
        if ( $row ) {
            $out = [
                'track_id'         => (int) $row->track_id,
                'track_title'      => $row->track_title,
                'artist_telemovel' => $row->artist_telemovel,
                'matched_via'      => 'upc_title',
            ];
            return $cache[ $cache_key ] = $out;
        }
    }

    // ─ 3. Fuzzy título + artista (último recurso) ────────────────
    // Match exacto título (da track) + nome do artista (da wallet do
    // owner do release). Case-insensitive e trimmed.
    // Cuidado: se vários artistas têm o mesmo nome, fica ambíguo —
    // rejeitamos para evitar atribuir wrongly.
    if ( ! empty( $title ) && ! empty( $artist ) ) {
        $title_norm  = mb_strtolower( trim( $title ) );
        $artist_norm = mb_strtolower( trim( $artist ) );

        // LIMIT 2 — detectar ambiguidade
        $matches = $wpdb->get_results( $wpdb->prepare(
            "SELECT t.id AS track_id, t.track_title, r.artist_telemovel,
                    w.user_nome
               FROM $t_tracks t
               JOIN $t_releases r ON r.id = t.release_id
               LEFT JOIN $t_wallets w ON w.user_telemovel = r.artist_telemovel
              WHERE LOWER(t.track_title) = %s
                AND LOWER(IFNULL(w.user_nome, '')) = %s
              LIMIT 2",
            $title_norm, $artist_norm
        ) );

        if ( count( $matches ) === 1 ) {
            $row = $matches[0];
            $out = [
                'track_id'         => (int) $row->track_id,
                'track_title'      => $row->track_title,
                'artist_telemovel' => $row->artist_telemovel,
                'matched_via'      => 'fuzzy',
            ];
            return $cache[ $cache_key ] = $out;
        }
        // 0 matches ou 2+ matches (ambíguo) → falha
    }

    return $cache[ $cache_key ] = null;
}


function rmbz_render_silent_retrofix_styles() {
    ?>
    <style>
    .rmbz-rfx-wrap { max-width: 1200px; }
    .rmbz-rfx-intro {
        background: #fffbeb; border-left: 4px solid #f59e0b;
        padding: 14px 18px; margin-bottom: 16px;
        border-radius: 4px;
    }
    .rmbz-rfx-card {
        background: #fff; border: 1px solid #e5e5e5;
        border-radius: 8px; padding: 16px 20px; margin-bottom: 14px;
    }
    .rmbz-rfx-apply-card { border-left: 4px solid #065f46; background: #f0fdf4; }
    .rmbz-rfx-summary-grid {
        display: grid; grid-template-columns: repeat(4, 1fr);
        gap: 10px; margin-bottom: 14px;
    }
    .rmbz-rfx-stat {
        background: #fff; border: 1px solid #e5e5e5;
        border-radius: 8px; padding: 12px 14px;
    }
    .rmbz-rfx-stat-highlight { border-left: 4px solid #065f46; background: #f0fdf4; }
    .rmbz-rfx-stat-label {
        font-size: 10px; text-transform: uppercase; color: #666;
        font-weight: 600; letter-spacing: 0.06em;
    }
    .rmbz-rfx-stat-val {
        font-size: 24px; font-weight: 700; line-height: 1.1; margin-top: 4px;
    }
    </style>
    <?php
}
