<?php
// includes/distribution/distribution-track-editor.php
//
// Editor completo de metadata da faixa (modal).
// Cobre 4 fases:
//  Fase 1 - Metadata mínima: título, explícito, instrumental, idioma, duração
//  Fase 2 - Créditos e histórico: songwriters, colaboradores, cover, previously released, lyrics
//  Fase 3 - Splits: partilha de receitas por colaborador
//  Fase 4 - Avançado: preview start time, moods/tags, Dolby Atmos (placeholder)
//
// Dados guardados em:
//  rmbz_distro_tracks.extra_data (JSON) — para colaboradores, songwriters, histórico, moods
//  rmbz_track_splits (tabela própria) — para aplicação no importer de CSV

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

/* ============================================================================
 * SCHEMA — tabela de splits
 * ============================================================================ */
function rmbz_track_splits_maybe_install() {
    $stored = get_option('rmbz_track_splits_schema_v', '0');
    if ( version_compare( $stored, '1.0', '<' ) ) {
        global $wpdb;
        $charset_collate = $wpdb->get_charset_collate();
        $table = $wpdb->prefix . 'rmbz_track_splits';

        $sql = "CREATE TABLE $table (
            id bigint(20) NOT NULL AUTO_INCREMENT,
            track_id bigint(20) NOT NULL,
            release_id bigint(20) NOT NULL,
            collaborator_name varchar(150) NOT NULL,
            collaborator_telemovel varchar(20) DEFAULT NULL,
            collaborator_email varchar(150) DEFAULT NULL,
            role varchar(60) DEFAULT 'feature' NOT NULL,
            percentage decimal(5,2) DEFAULT 0.00 NOT NULL,
            invite_status varchar(20) DEFAULT 'pending' NOT NULL,
            accepted_at datetime DEFAULT NULL,
            created_at datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
            PRIMARY KEY  (id),
            KEY idx_track (track_id),
            KEY idx_release (release_id),
            KEY idx_tel (collaborator_telemovel)
        ) $charset_collate;";

        require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
        dbDelta( $sql );
        update_option('rmbz_track_splits_schema_v', '1.0');
    }
}
add_action('admin_init', 'rmbz_track_splits_maybe_install', 6);
add_action('init',       'rmbz_track_splits_maybe_install', 6);


/* ============================================================================
 * HELPERS — leitura de metadata
 * ============================================================================ */

function rmbz_track_get_extra( $track_id ) {
    global $wpdb;
    $table = $wpdb->prefix . 'rmbz_distro_tracks';
    $row = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table WHERE id = %d", $track_id ) );
    if ( ! $row ) return null;

    $extra = [];
    if ( ! empty( $row->extra_data ) ) {
        $decoded = json_decode( $row->extra_data, true );
        if ( is_array($decoded) ) $extra = $decoded;
    }

    // [1.6.52.6] Auto-captura lazy da duração — se for 0 e tivermos audio_url,
    // tenta capturar agora. Resolve faixas antigas que foram criadas antes
    // do hook existir ou via API do app (que não disparava o hook).
    // Idempotente: depois de capturar, fica gravado e não corre de novo.
    if ( empty( $extra['duration_seconds'] ) && ! empty( $row->audio_url )
         && function_exists( 'rmbz_track_capture_duration' ) ) {
        rmbz_track_capture_duration( $track_id, $row->audio_url );
        // Re-ler depois da captura
        $row = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table WHERE id = %d", $track_id ) );
        if ( $row && ! empty( $row->extra_data ) ) {
            $decoded2 = json_decode( $row->extra_data, true );
            if ( is_array( $decoded2 ) ) $extra = $decoded2;
        }
    }

    return [
        'track'  => $row,
        'extra'  => array_merge([
            'collaborators' => [],
            'songwriters'   => [],
            'is_instrumental' => 0,
            'language'      => '',
            'duration_seconds' => 0,
            'previously_released' => 0,
            'original_isrc' => '',
            'is_cover' => 0,
            'cover_original_title' => '',
            'cover_original_composer' => '',
            'has_lyrics' => 0,
            'lyrics' => '',
            'preview_start_seconds' => 0,
            'moods' => [],
            'dolby_atmos_url' => '',
        ], $extra),
    ];
}

/**
 * [META-CHECK-1] Valida a metadata mínima exigida pelas DSPs para uma faixa.
 *
 * Aceita:
 *   - track_id (int): faz a query completa
 *   - row (object): usa o row já carregado (evita query repetida)
 *
 * Retorna array:
 *   [
 *     'is_complete' => bool,
 *     'missing'     => string[]  // lista legível de problemas
 *   ]
 *
 * Regras (alinhadas com requisitos Spotify/Apple/Deezer):
 *   - Título não vazio
 *   - Pelo menos 1 songwriter (composição) — exigido por todas as PROs
 *   - Se has_lyrics = 1 → letra preenchida
 *   - Se is_cover = 1 → cover_original_title E cover_original_composer
 *   - Se previously_released = 1 → original_isrc no formato CC-XXX-YY-NNNNN
 *
 * NÃO obrigatórios: ISRC do release (gerado), idioma (default release), splits
 * (default 100% para artista principal), colaboradores (apenas se houver feats).
 */
function rmbz_track_validate_metadata( $track_or_id ) {
    $row = null;
    if ( is_object($track_or_id) ) {
        $row = $track_or_id;
    } else {
        $tid = (int) $track_or_id;
        if ( $tid <= 0 ) return [ 'is_complete' => false, 'missing' => ['ID inválido'] ];
        $info = rmbz_track_get_extra( $tid );
        if ( ! $info ) return [ 'is_complete' => false, 'missing' => ['Faixa não encontrada'] ];
        $row = $info['track'];
    }

    $extra = [];
    if ( ! empty($row->extra_data) ) {
        $dec = json_decode($row->extra_data, true);
        if ( is_array($dec) ) $extra = $dec;
    }

    $missing = [];

    // 1. Título
    if ( empty($row->track_title) || trim($row->track_title) === '' ) {
        $missing[] = 'título';
    }

    // 2. Pelo menos 1 songwriter (obrigatório SACEM/PROs)
    $songwriters = isset($extra['songwriters']) && is_array($extra['songwriters']) ? $extra['songwriters'] : [];
    $sw_count = 0;
    foreach ( $songwriters as $sw ) {
        if ( ! empty($sw['name']) && trim($sw['name']) !== '' ) $sw_count++;
    }
    if ( $sw_count === 0 ) {
        $missing[] = 'compositor (songwriter)';
    }

    // 3. Se marcou que tem letra → letra preenchida
    if ( ! empty($extra['has_lyrics']) && empty(trim($extra['lyrics'] ?? '')) ) {
        $missing[] = 'letra (marcou que tem letra mas não preencheu)';
    }

    // 4. Se é cover → título original + compositor original
    if ( ! empty($extra['is_cover']) ) {
        if ( empty(trim($extra['cover_original_title'] ?? '')) ) {
            $missing[] = 'título original do cover';
        }
        if ( empty(trim($extra['cover_original_composer'] ?? '')) ) {
            $missing[] = 'compositor original do cover';
        }
    }

    // 5. Se previously_released → ISRC original obrigatório (formato CCXYZNNNNN)
    if ( ! empty($extra['previously_released']) ) {
        $orig_isrc = strtoupper( preg_replace('/[^A-Z0-9]/', '', $extra['original_isrc'] ?? '') );
        if ( strlen($orig_isrc) !== 12 ) {
            $missing[] = 'ISRC original do lançamento anterior (12 caracteres)';
        }
    }

    return [
        'is_complete' => empty($missing),
        'missing'     => $missing,
    ];
}

function rmbz_track_get_splits( $track_id ) {
    global $wpdb;
    $table = $wpdb->prefix . 'rmbz_track_splits';
    return $wpdb->get_results( $wpdb->prepare(
        "SELECT * FROM $table WHERE track_id = %d ORDER BY percentage DESC, id ASC",
        $track_id
    ) ) ?: [];
}


/* ============================================================================
 * VOCABULÁRIOS — listas de opções
 * ============================================================================ */

function rmbz_track_collaborator_roles() {
    return [
        'primary'    => 'Artista principal',
        'feature'    => 'Featuring (feat.)',
        'producer'   => 'Produtor',
        'mix'        => 'Mistura',
        'master'     => 'Masterização',
        'vocals'     => 'Vocalista',
        'remix'      => 'Remix',
        'songwriter' => 'Compositor (letra+música)',
    ];
}

function rmbz_track_moods() {
    return [
        'chill' => 'Chill',  'danceable' => 'Dançante', 'energetic' => 'Energético',
        'happy' => 'Feliz',  'sad' => 'Triste',         'romantic' => 'Romântico',
        'motivational' => 'Motivacional', 'spiritual' => 'Espiritual',
        'party' => 'Festa',  'focus' => 'Concentração', 'sleep' => 'Dormir',
        'workout' => 'Treino', 'aggressive' => 'Agressivo', 'melancholic' => 'Melancólico',
    ];
}

function rmbz_track_languages() {
    return [
        'Portuguese' => 'Português',
        'English'    => 'Inglês',
        'Spanish'    => 'Espanhol',
        'French'     => 'Francês',
        'Swahili'    => 'Suaíli',
        'Changana'   => 'Changana',
        'Xitsonga'   => 'Xitsonga',
        'Macua'      => 'Macua',
        'Other'      => 'Outro',
    ];
}


/* ============================================================================
 * MODAL HTML — renderizado uma vez no fim do wizard
 * ============================================================================ */

function rmbz_track_editor_render_modal() {
    $roles = rmbz_track_collaborator_roles();
    $moods = rmbz_track_moods();
    $langs = rmbz_track_languages();
    ?>
    <div id="rmbz-track-modal" class="rmbz-tm-overlay" style="display:none;" aria-hidden="true">
        <div class="rmbz-tm-dialog" role="dialog" aria-labelledby="rmbz-tm-title">
            <div class="rmbz-tm-head">
                <h2 id="rmbz-tm-title">Detalhes da faixa</h2>
                <button type="button" class="rmbz-tm-close" aria-label="Fechar">✕</button>
            </div>

            <div class="rmbz-tm-tabs">
                <button type="button" class="rmbz-tm-tab active" data-tab="basic">1 · Básico</button>
                <button type="button" class="rmbz-tm-tab" data-tab="credits">2 · Créditos</button>
                <button type="button" class="rmbz-tm-tab" data-tab="splits">3 · Splits</button>
                <button type="button" class="rmbz-tm-tab" data-tab="advanced">4 · Avançado</button>
            </div>

            <div class="rmbz-tm-body">

                <div class="rmbz-tm-pane active" data-pane="basic">
                    <div class="rmbz-tm-row">
                        <label>Título da faixa <span class="req">*</span></label>
                        <input type="text" id="rtm-title" placeholder="Ex: Cansei">
                    </div>
                    <div class="rmbz-tm-row two">
                        <div>
                            <label>Idioma</label>
                            <select id="rtm-language">
                                <?php foreach ( $langs as $code => $name ): ?>
                                    <option value="<?php echo esc_attr($code); ?>"><?php echo esc_html($name); ?></option>
                                <?php endforeach; ?>
                            </select>
                        </div>
                        <div>
                            <label>Duração</label>
                            <div class="rmbz-tm-duration">
                                <span id="rtm-duration-display">--:--</span>
                                <small>Capturada automaticamente do ficheiro</small>
                            </div>
                        </div>
                    </div>
                    <div class="rmbz-tm-row two">
                        <div>
                            <label>ISRC (opcional)</label>
                            <input type="text" id="rtm-isrc" placeholder="Ex: MZ0AB2400001" maxlength="12">
                            <small>Deixa vazio para 1MZ gerar.</small>
                        </div>
                        <div>
                            <label>Estado</label>
                            <div class="rmbz-tm-checks">
                                <label><input type="checkbox" id="rtm-explicit"> Letra explícita</label>
                                <label><input type="checkbox" id="rtm-instrumental"> É instrumental</label>
                            </div>
                        </div>
                    </div>
                </div>

                <div class="rmbz-tm-pane" data-pane="credits">
                    <h3>Colaboradores / artistas</h3>
                    <p class="rmbz-tm-hint">Adiciona quem participou: featuring, produtores, mistura, etc. O artista principal é automaticamente adicionado.</p>
                    <div id="rtm-collab-list" class="rmbz-tm-list"></div>
                    <button type="button" class="rmbz-tm-btn ghost" onclick="rtmAddCollab()">+ Adicionar colaborador</button>

                    <h3 style="margin-top:24px;">Compositores (songwriters)</h3>
                    <p class="rmbz-tm-hint">Quem escreveu a música e a letra. Obrigatório pelas editoras/SACEM (mesmo que seja só o artista principal).</p>
                    <div id="rtm-sw-list" class="rmbz-tm-list"></div>
                    <button type="button" class="rmbz-tm-btn ghost" onclick="rtmAddSongwriter()">+ Adicionar compositor</button>

                    <h3 style="margin-top:24px;">Letra da música</h3>
                    <label class="rmbz-tm-check-block"><input type="checkbox" id="rtm-has-lyrics"> Esta faixa tem letra</label>
                    <div id="rtm-lyrics-wrap" style="display:none; margin-top:10px;">
                        <textarea id="rtm-lyrics" rows="6" placeholder="Cola aqui a letra completa da música..."></textarea>
                        <small>Usado em Apple Music, Musixmatch e outras plataformas de letras.</small>
                    </div>

                    <h3 style="margin-top:24px;">Histórico</h3>
                    <label class="rmbz-tm-check-block"><input type="checkbox" id="rtm-prev-released"> Esta música já foi lançada antes</label>
                    <div id="rtm-prev-wrap" style="display:none; margin-top:10px;">
                        <label>ISRC original do lançamento anterior</label>
                        <input type="text" id="rtm-original-isrc" placeholder="Ex: USUM71900001">
                    </div>

                    <label class="rmbz-tm-check-block" style="margin-top:10px;"><input type="checkbox" id="rtm-is-cover"> É um cover de outra música</label>
                    <div id="rtm-cover-wrap" style="display:none; margin-top:10px;">
                        <div class="rmbz-tm-row two">
                            <div>
                                <label>Título original</label>
                                <input type="text" id="rtm-cover-title" placeholder="Ex: Umbrella">
                            </div>
                            <div>
                                <label>Compositor(es) original(is)</label>
                                <input type="text" id="rtm-cover-composer" placeholder="Ex: The-Dream, Christopher Stewart">
                            </div>
                        </div>
                    </div>
                </div>

                <div class="rmbz-tm-pane" data-pane="splits">
                    <h3>Partilha de receitas (splits)</h3>
                    <p class="rmbz-tm-hint">Define quanto cada colaborador recebe dos royalties. A soma tem de dar exactamente 100%. O artista principal é detentor por defeito.</p>

                    <div class="rmbz-tm-splits-summary">
                        <div class="rmbz-tm-pie" id="rtm-splits-pie">
                            <span class="pct">100%</span>
                            <small>Distribuído</small>
                        </div>
                        <div id="rtm-splits-warn" class="rmbz-tm-warn" style="display:none;"></div>
                    </div>

                    <div id="rtm-splits-list" class="rmbz-tm-list"></div>
                    <button type="button" class="rmbz-tm-btn ghost" onclick="rtmAddSplit()">+ Adicionar split</button>

                    <p class="rmbz-tm-hint" style="margin-top:16px;">
                        💡 Se um colaborador tem conta 1MZ (pelo telemóvel), receberá convite para aceitar o split.
                        Caso contrário, a percentagem fica registada no lançamento e pode ser paga manualmente.
                    </p>
                </div>

                <div class="rmbz-tm-pane" data-pane="advanced">
                    <h3>Preview no Spotify / Apple Music</h3>
                    <p class="rmbz-tm-hint">Define o segundo inicial dos 30s de preview. Útil se o refrão não é logo no início.</p>
                    <div class="rmbz-tm-row two">
                        <div>
                            <label>Começar preview em (segundos)</label>
                            <input type="number" id="rtm-preview-start" min="0" value="0">
                            <small>0 = início da música.</small>
                        </div>
                    </div>

                    <h3 style="margin-top:24px;">Moods e tags</h3>
                    <p class="rmbz-tm-hint">Ajuda os algoritmos a incluir a tua música nas playlists certas (escolhe até 5).</p>
                    <div id="rtm-moods" class="rmbz-tm-chips">
                        <?php foreach ( $moods as $k => $label ): ?>
                            <label class="rmbz-tm-chip"><input type="checkbox" value="<?php echo esc_attr($k); ?>"> <?php echo esc_html($label); ?></label>
                        <?php endforeach; ?>
                    </div>

                    <h3 style="margin-top:24px;">Dolby Atmos / Imersivo</h3>
                    <p class="rmbz-tm-hint">Upload de versão espacial da faixa (opcional). Aceite por Apple Music e Tidal.</p>
                    <div class="rmbz-tm-dolby">
                        <span style="color:#666;">🎧 Em breve</span>
                        <small>Funcionalidade disponível para artistas com plano PRO</small>
                    </div>
                </div>

            </div>

            <div class="rmbz-tm-foot">
                <span class="rmbz-tm-status" id="rtm-status"></span>
                <div>
                    <button type="button" class="rmbz-tm-btn ghost rmbz-tm-close">Cancelar</button>
                    <button type="button" class="rmbz-tm-btn primary" id="rtm-save">Guardar</button>
                </div>
            </div>

            <input type="hidden" id="rtm-track-id" value="">
            <input type="hidden" id="rtm-release-id" value="">
        </div>
    </div>

    <?php rmbz_track_editor_render_css();  ?>
    <?php rmbz_track_editor_render_js();   ?>
    <?php
}

function rmbz_track_editor_render_css() {
    $roles = rmbz_track_collaborator_roles();
    ?>
    <style>
    /* [DS 1.5.46] CSS do track-editor tokenizado.
     * Estrutura preservada (todas as classes .rmbz-tm-*) para o JS continuar
     * a funcionar sem alterações. Cores/spacing/tipografia agora vêm dos
     * tokens do design system para coerência com o resto do plugin.
     *
     * [MODAL-FIX-2] Overlay full-viewport. Usar top/right/bottom/left
     * explícitos em vez de `inset` (compat IE/old). Reforçar com 100vw/100vh
     * para tolerar containing blocks exóticos. O JS também move o modal
     * para <body> ao abrir (cinto + suspensórios). */
    .rmbz-tm-overlay {
        position: fixed;
        top: 0; right: 0; bottom: 0; left: 0;
        width: 100vw; height: 100vh;
        max-width: 100vw; max-height: 100vh;
        background: rgba(0, 0, 0, 0.75);
        backdrop-filter: blur(4px);
        -webkit-backdrop-filter: blur(4px);
        z-index: 100000;
        display: flex;
        align-items: center;
        justify-content: center;
        padding: var(--rmbz-space-4);
        overflow-y: auto;
        box-sizing: border-box;
        animation: rmbz-tm-fade var(--rmbz-duration-normal) var(--rmbz-ease-out);
    }
    @keyframes rmbz-tm-fade {
        from { opacity: 0; }
        to   { opacity: 1; }
    }

    .rmbz-tm-dialog {
        background: var(--rmbz-bg-elevated);
        border: 1px solid var(--rmbz-border-subtle);
        border-radius: var(--rmbz-radius-md);
        width: 100%;
        max-width: 720px;
        max-height: calc(100vh - var(--rmbz-space-8));
        display: flex;
        flex-direction: column;
        box-shadow: var(--rmbz-shadow-lg);
        position: relative;
        animation: rmbz-tm-pop var(--rmbz-duration-normal) var(--rmbz-ease-spring);
    }
    /* Barra dourada no topo (identidade visual igual aos auth-card e feature-card) */
    .rmbz-tm-dialog::before {
        content: '';
        position: absolute;
        top: 0; left: 0; right: 0;
        height: 3px;
        background: var(--rmbz-brand);
        border-radius: var(--rmbz-radius-md) var(--rmbz-radius-md) 0 0;
    }
    @keyframes rmbz-tm-pop {
        from { opacity: 0; transform: translateY(-12px) scale(0.96); }
        to   { opacity: 1; transform: translateY(0) scale(1); }
    }

    .rmbz-tm-head {
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding: var(--rmbz-space-4) var(--rmbz-space-5);
        border-bottom: 1px solid var(--rmbz-border-subtle);
    }
    .rmbz-tm-head h2 {
        margin: 0;
        font-family: var(--rmbz-font-display);
        font-size: var(--rmbz-text-lg);
        font-weight: var(--rmbz-weight-semibold);
        color: var(--rmbz-text-primary);
        letter-spacing: var(--rmbz-tracking-tight);
    }
    .rmbz-tm-close {
        background: transparent;
        border: none;
        color: var(--rmbz-text-muted);
        font-size: var(--rmbz-text-xl);
        cursor: pointer;
        padding: var(--rmbz-space-1) var(--rmbz-space-2);
        line-height: 1;
        border-radius: var(--rmbz-radius-sm);
        transition: color var(--rmbz-duration-fast) var(--rmbz-ease-out),
                    background var(--rmbz-duration-fast) var(--rmbz-ease-out);
    }
    .rmbz-tm-close:hover {
        color: var(--rmbz-text-primary);
        background: var(--rmbz-bg-hover);
    }

    /* Tabs — visual igual às tabs do dashboard (1.5.41) */
    .rmbz-tm-tabs {
        display: flex;
        padding: 0 var(--rmbz-space-2);
        border-bottom: 1px solid var(--rmbz-border-subtle);
        gap: var(--rmbz-space-1);
        overflow-x: auto;
        overflow-y: hidden;
        scrollbar-width: none;
    }
    .rmbz-tm-tabs::-webkit-scrollbar { display: none; }

    .rmbz-tm-tab {
        background: transparent;
        border: none;
        color: var(--rmbz-text-muted);
        padding: var(--rmbz-space-3) var(--rmbz-space-4);
        font-family: var(--rmbz-font-body);
        font-size: var(--rmbz-text-xs);
        font-weight: var(--rmbz-weight-semibold);
        cursor: pointer;
        position: relative;
        white-space: nowrap;
        transition: color var(--rmbz-duration-fast) var(--rmbz-ease-out);
    }
    .rmbz-tm-tab::after {
        content: '';
        position: absolute;
        left: var(--rmbz-space-2);
        right: var(--rmbz-space-2);
        bottom: -1px;
        height: 2px;
        background: var(--rmbz-brand);
        border-radius: 2px 2px 0 0;
        transform: scaleX(0);
        transform-origin: center;
        transition: transform var(--rmbz-duration-normal) var(--rmbz-ease-spring);
    }
    .rmbz-tm-tab:hover { color: var(--rmbz-text-primary); }
    .rmbz-tm-tab.active {
        color: var(--rmbz-brand);
    }
    .rmbz-tm-tab.active::after { transform: scaleX(1); }

    .rmbz-tm-body {
        padding: var(--rmbz-space-5);
        overflow-y: auto;
        flex: 1;
        min-height: 300px;
    }
    .rmbz-tm-pane { display: none; }
    .rmbz-tm-pane.active {
        display: block;
        animation: rmbz-tm-pane-fade var(--rmbz-duration-normal) var(--rmbz-ease-out);
    }
    @keyframes rmbz-tm-pane-fade {
        from { opacity: 0; transform: translateY(4px); }
        to   { opacity: 1; transform: translateY(0); }
    }
    .rmbz-tm-pane h3 {
        margin: 0 0 var(--rmbz-space-1) 0;
        font-family: var(--rmbz-font-display);
        font-size: var(--rmbz-text-sm);
        font-weight: var(--rmbz-weight-semibold);
        color: var(--rmbz-text-primary);
    }
    .rmbz-tm-hint {
        margin: 0 0 var(--rmbz-space-3) 0;
        color: var(--rmbz-text-muted);
        font-size: var(--rmbz-text-xs);
        line-height: var(--rmbz-leading-relaxed);
    }

    /* Rows e fields — alinhar com .rmbz-field do design system */
    .rmbz-tm-row { margin-bottom: var(--rmbz-space-4); }
    .rmbz-tm-row.two {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: var(--rmbz-space-3);
    }
    @media (max-width: 560px) {
        .rmbz-tm-row.two { grid-template-columns: 1fr; }
    }
    .rmbz-tm-row label {
        display: block;
        color: var(--rmbz-text-secondary);
        font-size: var(--rmbz-text-xs);
        font-weight: var(--rmbz-weight-semibold);
        margin-bottom: var(--rmbz-space-2);
    }
    .rmbz-tm-row .req { color: var(--rmbz-danger); }
    .rmbz-tm-row input[type=text],
    .rmbz-tm-row input[type=number],
    .rmbz-tm-row select,
    .rmbz-tm-body textarea {
        width: 100%;
        padding: var(--rmbz-space-3) var(--rmbz-space-3);
        background: var(--rmbz-bg-input);
        border: 1px solid var(--rmbz-border-subtle);
        border-radius: var(--rmbz-radius-sm);
        color: var(--rmbz-text-primary);
        font-family: var(--rmbz-font-body);
        font-size: var(--rmbz-text-sm);
        box-sizing: border-box;
        transition: border-color var(--rmbz-duration-fast) var(--rmbz-ease-out),
                    box-shadow var(--rmbz-duration-fast) var(--rmbz-ease-out);
    }
    .rmbz-tm-body textarea {
        resize: vertical;
        min-height: 100px;
        font-family: var(--rmbz-font-body);
    }
    .rmbz-tm-row input:focus,
    .rmbz-tm-row select:focus,
    .rmbz-tm-body textarea:focus {
        outline: none;
        border-color: var(--rmbz-brand);
        box-shadow: 0 0 0 3px rgba(240, 185, 11, 0.15);
    }
    .rmbz-tm-row small {
        display: block;
        color: var(--rmbz-text-muted);
        font-size: var(--rmbz-text-2xs);
        margin-top: var(--rmbz-space-1);
    }

    /* Bloco de duração — destaca que é auto-detectada */
    .rmbz-tm-duration {
        background: var(--rmbz-bg-input);
        border: 1px solid var(--rmbz-border-subtle);
        border-radius: var(--rmbz-radius-sm);
        padding: var(--rmbz-space-3);
    }
    .rmbz-tm-duration span {
        color: var(--rmbz-brand);
        font-size: var(--rmbz-text-base);
        font-weight: var(--rmbz-weight-bold);
        font-family: var(--rmbz-font-mono);
        font-variant-numeric: tabular-nums;
    }
    .rmbz-tm-duration small {
        display: block;
        color: var(--rmbz-text-muted);
        font-size: var(--rmbz-text-2xs);
        margin-top: var(--rmbz-space-1);
    }

    /* Checkboxes inline (Explicit / Instrumental) */
    .rmbz-tm-checks label {
        display: flex;
        align-items: center;
        gap: var(--rmbz-space-2);
        color: var(--rmbz-text-secondary);
        font-size: var(--rmbz-text-xs);
        cursor: pointer;
        padding: var(--rmbz-space-1) 0;
        font-weight: var(--rmbz-weight-normal);
        margin: 0;
    }
    .rmbz-tm-checks input { accent-color: var(--rmbz-brand); }

    /* Checkbox em "block" (tem letra / cover / já lançada) */
    .rmbz-tm-check-block {
        display: flex;
        align-items: center;
        gap: var(--rmbz-space-2);
        color: var(--rmbz-text-secondary);
        font-size: var(--rmbz-text-sm);
        cursor: pointer;
        padding: var(--rmbz-space-2) var(--rmbz-space-3);
        background: var(--rmbz-bg-input);
        border-radius: var(--rmbz-radius-sm);
        border: 1px solid var(--rmbz-border-subtle);
        transition: border-color var(--rmbz-duration-fast) var(--rmbz-ease-out);
    }
    .rmbz-tm-check-block:hover { border-color: var(--rmbz-border-strong); }
    .rmbz-tm-check-block input { accent-color: var(--rmbz-brand); }

    /* Lista de itens (colaboradores, songwriters, splits) */
    .rmbz-tm-list {
        display: flex;
        flex-direction: column;
        gap: var(--rmbz-space-2);
        margin-bottom: var(--rmbz-space-3);
    }
    .rmbz-tm-list-item {
        display: grid;
        grid-template-columns: 1fr 1fr auto;
        gap: var(--rmbz-space-2);
        align-items: center;
        padding: var(--rmbz-space-2);
        background: var(--rmbz-bg-input);
        border: 1px solid var(--rmbz-border-subtle);
        border-radius: var(--rmbz-radius-sm);
    }
    .rmbz-tm-list-item.split {
        grid-template-columns: 1fr 1fr 90px auto;
    }
    @media (max-width: 600px) {
        .rmbz-tm-list-item,
        .rmbz-tm-list-item.split {
            grid-template-columns: 1fr;
        }
    }
    .rmbz-tm-list-item input,
    .rmbz-tm-list-item select {
        padding: var(--rmbz-space-2) var(--rmbz-space-3);
        background: var(--rmbz-bg-base);
        border: 1px solid var(--rmbz-border-subtle);
        border-radius: var(--rmbz-radius-sm);
        color: var(--rmbz-text-primary);
        font-size: var(--rmbz-text-xs);
        width: 100%;
        box-sizing: border-box;
    }
    .rmbz-tm-list-item .rm-btn {
        background: transparent;
        border: 1px solid var(--rmbz-border-subtle);
        color: var(--rmbz-danger);
        padding: var(--rmbz-space-2) var(--rmbz-space-3);
        border-radius: var(--rmbz-radius-sm);
        font-size: var(--rmbz-text-2xs);
        cursor: pointer;
        white-space: nowrap;
        transition: background var(--rmbz-duration-fast) var(--rmbz-ease-out),
                    border-color var(--rmbz-duration-fast) var(--rmbz-ease-out);
    }
    .rmbz-tm-list-item .rm-btn:hover {
        background: var(--rmbz-danger-soft);
        border-color: var(--rmbz-danger);
    }

    /* Splits: pie chart + warning */
    .rmbz-tm-splits-summary {
        display: flex;
        align-items: center;
        gap: var(--rmbz-space-4);
        background: var(--rmbz-bg-input);
        border: 1px solid var(--rmbz-border-subtle);
        border-radius: var(--rmbz-radius-md);
        padding: var(--rmbz-space-4);
        margin-bottom: var(--rmbz-space-4);
    }
    .rmbz-tm-pie {
        width: 72px;
        height: 72px;
        border-radius: 50%;
        background: conic-gradient(var(--rmbz-success) 0% 100%, var(--rmbz-bg-base) 100%);
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        flex-shrink: 0;
        position: relative;
    }
    .rmbz-tm-pie::before {
        content: '';
        position: absolute;
        inset: 6px;
        background: var(--rmbz-bg-input);
        border-radius: 50%;
    }
    .rmbz-tm-pie .pct {
        position: relative;
        color: var(--rmbz-success);
        font-size: var(--rmbz-text-sm);
        font-weight: var(--rmbz-weight-bold);
        font-family: var(--rmbz-font-mono);
        font-variant-numeric: tabular-nums;
    }
    .rmbz-tm-pie small {
        position: relative;
        color: var(--rmbz-text-muted);
        font-size: var(--rmbz-text-2xs);
    }
    .rmbz-tm-pie.error {
        background: conic-gradient(var(--rmbz-danger) 0% 100%, var(--rmbz-bg-base) 100%);
    }
    .rmbz-tm-pie.error .pct { color: var(--rmbz-danger); }

    .rmbz-tm-warn {
        background: var(--rmbz-danger-soft);
        border: 1px solid var(--rmbz-danger);
        color: var(--rmbz-danger);
        padding: var(--rmbz-space-2) var(--rmbz-space-3);
        border-radius: var(--rmbz-radius-sm);
        font-size: var(--rmbz-text-xs);
        flex: 1;
    }

    /* Chips de mood — usa :has() (já moderno) */
    .rmbz-tm-chips {
        display: flex;
        flex-wrap: wrap;
        gap: var(--rmbz-space-2);
    }
    .rmbz-tm-chip {
        background: var(--rmbz-bg-input);
        border: 1px solid var(--rmbz-border-subtle);
        padding: var(--rmbz-space-2) var(--rmbz-space-3);
        border-radius: var(--rmbz-radius-pill);
        color: var(--rmbz-text-muted);
        font-size: var(--rmbz-text-2xs);
        cursor: pointer;
        display: flex;
        align-items: center;
        gap: var(--rmbz-space-2);
        user-select: none;
        transition: all var(--rmbz-duration-fast) var(--rmbz-ease-out);
    }
    .rmbz-tm-chip:hover { border-color: var(--rmbz-border-strong); }
    .rmbz-tm-chip input { display: none; }
    .rmbz-tm-chip:has(input:checked) {
        background: var(--rmbz-brand-soft);
        border-color: var(--rmbz-brand);
        color: var(--rmbz-brand);
    }

    /* Bloco "em breve" do Dolby Atmos */
    .rmbz-tm-dolby {
        background: var(--rmbz-bg-input);
        border: 1px dashed var(--rmbz-border-subtle);
        border-radius: var(--rmbz-radius-sm);
        padding: var(--rmbz-space-4);
        text-align: center;
        color: var(--rmbz-text-muted);
        font-size: var(--rmbz-text-sm);
    }
    .rmbz-tm-dolby small {
        display: block;
        color: var(--rmbz-text-muted);
        font-size: var(--rmbz-text-2xs);
        margin-top: var(--rmbz-space-1);
    }

    /* Footer — botões de acção */
    .rmbz-tm-foot {
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding: var(--rmbz-space-4) var(--rmbz-space-5);
        border-top: 1px solid var(--rmbz-border-subtle);
        gap: var(--rmbz-space-3);
        flex-wrap: wrap;
        background: var(--rmbz-bg-input);
        border-radius: 0 0 var(--rmbz-radius-md) var(--rmbz-radius-md);
    }
    .rmbz-tm-status {
        color: var(--rmbz-text-muted);
        font-size: var(--rmbz-text-xs);
    }
    .rmbz-tm-foot > div {
        display: flex;
        gap: var(--rmbz-space-2);
    }

    /* Botões — visual igual aos rmbz-btn do design system */
    .rmbz-tm-btn {
        padding: var(--rmbz-space-3) var(--rmbz-space-5);
        border-radius: var(--rmbz-radius-sm);
        font-family: var(--rmbz-font-body);
        font-size: var(--rmbz-text-xs);
        font-weight: var(--rmbz-weight-semibold);
        cursor: pointer;
        border: 1px solid transparent;
        text-transform: none;
        letter-spacing: 0;
        transition: all var(--rmbz-duration-fast) var(--rmbz-ease-out);
    }
    .rmbz-tm-btn.primary {
        background: var(--rmbz-brand);
        color: var(--rmbz-text-on-brand);
        border-color: var(--rmbz-brand);
        box-shadow: var(--rmbz-shadow-brand-sm);
    }
    .rmbz-tm-btn.primary:hover:not(:disabled) {
        background: var(--rmbz-brand-strong);
        transform: translateY(-1px);
    }
    .rmbz-tm-btn.primary:disabled {
        background: var(--rmbz-bg-hover);
        color: var(--rmbz-text-muted);
        cursor: not-allowed;
        border-color: var(--rmbz-border-subtle);
        box-shadow: none;
    }
    .rmbz-tm-btn.ghost {
        background: transparent;
        color: var(--rmbz-text-secondary);
        border-color: var(--rmbz-border-subtle);
    }
    .rmbz-tm-btn.ghost:hover {
        border-color: var(--rmbz-brand);
        color: var(--rmbz-brand);
    }

    /* Mobile — ajustes finos */
    @media (max-width: 480px) {
        .rmbz-tm-overlay { padding: var(--rmbz-space-2); }
        .rmbz-tm-dialog { max-height: calc(100vh - var(--rmbz-space-4)); }
        .rmbz-tm-head { padding: var(--rmbz-space-3) var(--rmbz-space-4); }
        .rmbz-tm-body { padding: var(--rmbz-space-4); }
        .rmbz-tm-foot { padding: var(--rmbz-space-3) var(--rmbz-space-4); }
    }
    </style>
    <?php
}

function rmbz_track_editor_render_js() {
    $roles = rmbz_track_collaborator_roles();
    ?>
    <script>
    window.RMBZ_TRACK_ROLES = <?php echo wp_json_encode($roles); ?>;
    window.RMBZ_TRACK_EDITOR = (function () {
        var ajaxUrl = '<?php echo esc_js( admin_url('admin-ajax.php') ); ?>';
        var nonce   = '<?php echo esc_js( wp_create_nonce('rmbz_distro_nonce') ); ?>';

        var state = { collaborators: [], songwriters: [], splits: [], moods: [] };

        function $(sel) { return document.querySelector(sel); }
        function $$(sel) { return document.querySelectorAll(sel); }

        function renderCollabList() {
            var wrap = $('#rtm-collab-list');
            if ( ! wrap ) return;
            wrap.innerHTML = '';
            state.collaborators.forEach(function(c, i) {
                var div = document.createElement('div');
                div.className = 'rmbz-tm-list-item';
                var roleOpts = Object.entries(window.RMBZ_TRACK_ROLES)
                    .map(function(e) { return '<option value="'+e[0]+'"'+(c.role===e[0]?' selected':'')+'>'+e[1]+'</option>'; })
                    .join('');
                div.innerHTML = '<input type="text" placeholder="Nome do artista" value="'+escAttr(c.name||'')+'" data-k="name" data-i="'+i+'">'
                    + '<select data-k="role" data-i="'+i+'">'+roleOpts+'</select>'
                    + '<button type="button" class="rm-btn" data-rm="'+i+'">Remover</button>';
                wrap.appendChild(div);
            });
            wrap.querySelectorAll('input, select').forEach(function(el) {
                el.addEventListener('input', function() {
                    var i = +el.dataset.i, k = el.dataset.k;
                    state.collaborators[i][k] = el.value;
                });
            });
            wrap.querySelectorAll('[data-rm]').forEach(function(b) {
                b.addEventListener('click', function() {
                    state.collaborators.splice(+b.dataset.rm, 1);
                    renderCollabList();
                });
            });
        }

        function renderSongwriters() {
            var wrap = $('#rtm-sw-list');
            if ( ! wrap ) return;
            wrap.innerHTML = '';
            state.songwriters.forEach(function(s, i) {
                var div = document.createElement('div');
                div.className = 'rmbz-tm-list-item';
                div.style.gridTemplateColumns = '1fr auto';
                div.innerHTML = '<input type="text" placeholder="Nome completo do compositor" value="'+escAttr(s.name||'')+'" data-k="name" data-i="'+i+'">'
                    + '<button type="button" class="rm-btn" data-rm="'+i+'">Remover</button>';
                wrap.appendChild(div);
            });
            wrap.querySelectorAll('input').forEach(function(el) {
                el.addEventListener('input', function() {
                    state.songwriters[+el.dataset.i][el.dataset.k] = el.value;
                });
            });
            wrap.querySelectorAll('[data-rm]').forEach(function(b) {
                b.addEventListener('click', function() {
                    state.songwriters.splice(+b.dataset.rm, 1);
                    renderSongwriters();
                });
            });
        }

        function renderSplits() {
            var wrap = $('#rtm-splits-list');
            if ( ! wrap ) return;
            wrap.innerHTML = '';
            state.splits.forEach(function(s, i) {
                var div = document.createElement('div');
                div.className = 'rmbz-tm-list-item split';
                div.innerHTML = '<input type="text" placeholder="Nome do colaborador" value="'+escAttr(s.name||'')+'" data-k="name" data-i="'+i+'">'
                    + '<input type="text" placeholder="Telemóvel (ex: 258...)" value="'+escAttr(s.telemovel||'')+'" data-k="telemovel" data-i="'+i+'">'
                    + '<input type="number" min="0" max="100" step="0.01" placeholder="%" value="'+(s.percentage||0)+'" data-k="percentage" data-i="'+i+'">'
                    + '<button type="button" class="rm-btn" data-rm="'+i+'">✕</button>';
                wrap.appendChild(div);
            });
            wrap.querySelectorAll('input').forEach(function(el) {
                el.addEventListener('input', function() {
                    var i = +el.dataset.i, k = el.dataset.k;
                    state.splits[i][k] = (k === 'percentage') ? parseFloat(el.value || 0) : el.value;
                    updateSplitsSummary();
                });
            });
            wrap.querySelectorAll('[data-rm]').forEach(function(b) {
                b.addEventListener('click', function() {
                    state.splits.splice(+b.dataset.rm, 1);
                    renderSplits();
                    updateSplitsSummary();
                });
            });
            updateSplitsSummary();
        }

        function updateSplitsSummary() {
            var total = state.splits.reduce(function(s, x) { return s + (parseFloat(x.percentage) || 0); }, 0);
            var remaining = 100 - total;
            var pie  = $('#rtm-splits-pie');
            var warn = $('#rtm-splits-warn');
            if ( ! pie ) return;
            var pctSpan = pie.querySelector('.pct');
            pctSpan.innerText = remaining.toFixed(2).replace(/\.?0+$/, '') + '%';
            var label = pie.querySelector('small');
            if ( label ) label.innerText = remaining >= 0 ? 'Restante' : 'Excedente';
            pie.classList.toggle('error', Math.abs(total - 100) > 0.01 && state.splits.length > 0);
            pie.style.background = 'conic-gradient(#2ecc71 0% '+Math.min(total,100)+'%, #2a2a2a '+Math.min(total,100)+'% 100%)';

            if ( state.splits.length === 0 ) {
                warn.style.display = 'none';
            } else if ( Math.abs(total - 100) < 0.01 ) {
                warn.style.display = 'block';
                warn.style.background = 'rgba(46,204,113,0.1)';
                warn.style.borderColor = '#2ecc71';
                warn.style.color = '#2ecc71';
                warn.innerText = '✓ Splits totalizam 100% — podes guardar.';
            } else {
                warn.style.display = 'block';
                warn.style.background = 'rgba(231,76,60,0.1)';
                warn.style.borderColor = '#e74c3c';
                warn.style.color = '#e74c3c';
                warn.innerText = (total > 100)
                    ? '⚠ Splits excedem 100% em ' + (total - 100).toFixed(2) + '% — reduz para guardar.'
                    : '⚠ Splits somam ' + total.toFixed(2) + '% — faltam ' + (100 - total).toFixed(2) + '% para 100%.';
            }
        }

        window.rtmAddCollab = function() {
            state.collaborators.push({ name: '', role: 'feature' });
            renderCollabList();
        };
        window.rtmAddSongwriter = function() {
            state.songwriters.push({ name: '' });
            renderSongwriters();
        };
        window.rtmAddSplit = function() {
            state.splits.push({ name: '', telemovel: '', percentage: 0, role: 'feature' });
            renderSplits();
        };

        function escAttr(s) { return String(s).replace(/"/g, '&quot;').replace(/</g, '&lt;'); }

        function switchTab(name) {
            $$('.rmbz-tm-tab').forEach(function(t) { t.classList.toggle('active', t.dataset.tab === name); });
            $$('.rmbz-tm-pane').forEach(function(p) { p.classList.toggle('active', p.dataset.pane === name); });
        }

        function open(trackId, releaseId) {
            // [MODAL-FIX-1] Garantir que o modal está como filho directo do
            // <body>. Quando renderizado dentro de containers que tenham
            // transform/filter/will-change, o `position: fixed` fica relativo
            // a esse container em vez do viewport — o overlay aparece partido.
            // Mover para body uma vez resolve em qualquer tema/layout.
            var modal = $('#rmbz-track-modal');
            if ( modal && modal.parentNode !== document.body ) {
                document.body.appendChild(modal);
            }

            $('#rtm-track-id').value = trackId;
            $('#rtm-release-id').value = releaseId;
            $('#rtm-status').innerText = 'A carregar dados da faixa...';
            modal.style.display = 'flex';
            modal.setAttribute('aria-hidden', 'false');

            // Bloquear scroll do body enquanto o modal está aberto.
            document.body.style.overflow = 'hidden';

            switchTab('basic');

            var fd = new FormData();
            fd.append('action', 'rmbz_track_editor_load');
            fd.append('nonce', nonce);
            fd.append('track_id', trackId);

            fetch(ajaxUrl, { method: 'POST', body: fd })
                .then(function(r) { return r.json(); })
                .then(function(d) {
                    if ( ! d.success ) throw new Error(d.data || 'Erro');
                    populate(d.data);
                    $('#rtm-status').innerText = '';
                })
                .catch(function(err) {
                    $('#rtm-status').innerText = '❌ ' + err.message;
                });
        }

        function populate(data) {
            var t = data.track, e = data.extra, splits = data.splits || [];
            $('#rtm-title').value = t.track_title || '';
            $('#rtm-isrc').value = t.isrc_code || '';
            $('#rtm-explicit').checked = !!+t.is_explicit;
            $('#rtm-instrumental').checked = !!+e.is_instrumental;
            $('#rtm-language').value = e.language || 'Portuguese';

            var dur = e.duration_seconds || 0;
            var m = Math.floor(dur / 60), s = dur % 60;
            $('#rtm-duration-display').innerText = dur > 0 ? (m<10?'0'+m:m)+':'+(s<10?'0'+s:s) : '--:--';

            state.collaborators = Array.isArray(e.collaborators) ? e.collaborators : [];
            state.songwriters   = Array.isArray(e.songwriters)   ? e.songwriters   : [];
            state.splits        = splits.map(function(sp) {
                return { name: sp.collaborator_name, telemovel: sp.collaborator_telemovel || '', percentage: parseFloat(sp.percentage), role: sp.role };
            });
            state.moods         = Array.isArray(e.moods) ? e.moods : [];

            renderCollabList();
            renderSongwriters();
            renderSplits();

            $('#rtm-has-lyrics').checked = !!+e.has_lyrics;
            $('#rtm-lyrics').value = e.lyrics || '';
            $('#rtm-lyrics-wrap').style.display = e.has_lyrics ? 'block' : 'none';

            $('#rtm-prev-released').checked = !!+e.previously_released;
            $('#rtm-original-isrc').value = e.original_isrc || '';
            $('#rtm-prev-wrap').style.display = e.previously_released ? 'block' : 'none';

            $('#rtm-is-cover').checked = !!+e.is_cover;
            $('#rtm-cover-title').value = e.cover_original_title || '';
            $('#rtm-cover-composer').value = e.cover_original_composer || '';
            $('#rtm-cover-wrap').style.display = e.is_cover ? 'block' : 'none';

            $('#rtm-preview-start').value = e.preview_start_seconds || 0;

            $$('#rtm-moods input').forEach(function(cb) {
                cb.checked = state.moods.indexOf(cb.value) !== -1;
            });
        }

        function close() {
            $('#rmbz-track-modal').style.display = 'none';
            $('#rmbz-track-modal').setAttribute('aria-hidden', 'true');
            // [MODAL-FIX-1] Restaurar scroll do body.
            document.body.style.overflow = '';
        }

        function save() {
            var btn = $('#rtm-save');
            var status = $('#rtm-status');
            status.innerText = '';

            var title = $('#rtm-title').value.trim();
            if ( ! title ) { status.innerText = '⚠ Título obrigatório.'; switchTab('basic'); return; }

            // Validar splits
            if ( state.splits.length > 0 ) {
                var total = state.splits.reduce(function(s, x) { return s + (parseFloat(x.percentage) || 0); }, 0);
                if ( Math.abs(total - 100) > 0.01 ) {
                    status.innerText = '⚠ Splits têm de somar 100%.'; switchTab('splits'); return;
                }
            }

            var moods = [];
            $$('#rtm-moods input:checked').forEach(function(cb) { moods.push(cb.value); });

            var payload = {
                track_id: $('#rtm-track-id').value,
                release_id: $('#rtm-release-id').value,
                track_title: title,
                isrc_code: $('#rtm-isrc').value.trim(),
                is_explicit: $('#rtm-explicit').checked ? 1 : 0,
                is_instrumental: $('#rtm-instrumental').checked ? 1 : 0,
                language: $('#rtm-language').value,
                collaborators: state.collaborators.filter(function(c) { return c.name; }),
                songwriters:   state.songwriters.filter(function(s) { return s.name; }),
                has_lyrics: $('#rtm-has-lyrics').checked ? 1 : 0,
                lyrics: $('#rtm-lyrics').value,
                previously_released: $('#rtm-prev-released').checked ? 1 : 0,
                original_isrc: $('#rtm-original-isrc').value.trim(),
                is_cover: $('#rtm-is-cover').checked ? 1 : 0,
                cover_original_title: $('#rtm-cover-title').value.trim(),
                cover_original_composer: $('#rtm-cover-composer').value.trim(),
                preview_start_seconds: parseInt($('#rtm-preview-start').value || 0, 10),
                moods: moods,
                splits: state.splits.filter(function(s) { return s.name && parseFloat(s.percentage) > 0; }),
            };

            btn.disabled = true;
            btn.innerText = 'A guardar...';

            var fd = new FormData();
            fd.append('action', 'rmbz_track_editor_save');
            fd.append('nonce', nonce);
            fd.append('payload', JSON.stringify(payload));

            fetch(ajaxUrl, { method: 'POST', body: fd })
                .then(function(r) { return r.json(); })
                .then(function(d) {
                    if ( ! d.success ) throw new Error(d.data || 'Erro');
                    status.innerText = '✓ Guardado!';
                    setTimeout(function() { window.location.reload(); }, 500);
                })
                .catch(function(err) {
                    btn.disabled = false;
                    btn.innerText = 'Guardar';
                    status.innerText = '❌ ' + err.message;
                });
        }

        // Bind de todos os eventos
        document.addEventListener('DOMContentLoaded', function() {
            $$('.rmbz-tm-tab').forEach(function(t) {
                t.addEventListener('click', function() { switchTab(t.dataset.tab); });
            });
            $$('.rmbz-tm-close').forEach(function(b) {
                b.addEventListener('click', close);
            });
            $('#rmbz-track-modal').addEventListener('click', function(e) {
                if ( e.target.id === 'rmbz-track-modal' ) close();
            });
            $('#rtm-save').addEventListener('click', save);

            $('#rtm-has-lyrics').addEventListener('change', function() {
                $('#rtm-lyrics-wrap').style.display = this.checked ? 'block' : 'none';
            });
            $('#rtm-prev-released').addEventListener('change', function() {
                $('#rtm-prev-wrap').style.display = this.checked ? 'block' : 'none';
            });
            $('#rtm-is-cover').addEventListener('change', function() {
                $('#rtm-cover-wrap').style.display = this.checked ? 'block' : 'none';
            });
            $('#rtm-instrumental').addEventListener('change', function() {
                if ( this.checked ) {
                    $('#rtm-has-lyrics').checked = false;
                    $('#rtm-lyrics-wrap').style.display = 'none';
                }
            });
        });

        return { open: open, close: close };
    })();
    </script>
    <?php
}


/* ============================================================================
 * AJAX — carregar dados da faixa
 * ============================================================================ */
function rmbz_ajax_track_editor_load() {
    if ( ! check_ajax_referer('rmbz_distro_nonce', 'nonce', false) ) {
        wp_send_json_error('Erro de segurança.', 403);
    }
    if ( ! function_exists('rmbz_is_user_logged_in') || ! rmbz_is_user_logged_in() ) {
        wp_send_json_error('Sessão expirada.', 401);
    }

    $telemovel = rmbz_get_current_user_telemovel();
    // [1.5.198] Detectar se é WP admin (override mode)
    $is_admin_override = function_exists('current_user_can') && current_user_can('manage_options');

    // Rate limit (anti-scraping de metadata)
    // Admin não tem rate limit (uso interno legítimo)
    if ( ! $is_admin_override && function_exists('rmbz_rate_limit') && ! rmbz_rate_limit( 'track_load_' . $telemovel, 60 ) ) {
        wp_send_json_error('Demasiados pedidos.', 429);
    }

    $track_id  = isset($_POST['track_id']) ? absint($_POST['track_id']) : 0;
    if ( $track_id <= 0 ) wp_send_json_error('ID inválido.');

    $data = rmbz_track_get_extra( $track_id );
    if ( ! $data ) wp_send_json_error('Faixa não encontrada.', 404);

    // Ownership check — OK aqui porque release_id é lido da própria track,
    // não recebido do cliente.
    // [1.5.198] Admin bypass — admin pode aceder a qualquer faixa
    global $wpdb;
    $table_releases = $wpdb->prefix . 'rmbz_distro_releases';
    if ( ! $is_admin_override ) {
        $owner = $wpdb->get_var( $wpdb->prepare(
            "SELECT id FROM $table_releases WHERE id = %d AND artist_telemovel = %s",
            $data['track']->release_id, $telemovel
        ) );
        if ( ! $owner ) wp_send_json_error('Sem permissão.', 403);
    } else {
        // Admin: validar apenas que o release existe (defensivo)
        $rel_exists = $wpdb->get_var( $wpdb->prepare(
            "SELECT id FROM $table_releases WHERE id = %d",
            $data['track']->release_id
        ) );
        if ( ! $rel_exists ) wp_send_json_error('Release inexistente.', 404);
    }

    $splits = rmbz_track_get_splits( $track_id );

    wp_send_json_success([
        'track'  => $data['track'],
        'extra'  => $data['extra'],
        'splits' => $splits,
    ]);
}
add_action('wp_ajax_rmbz_track_editor_load',        'rmbz_ajax_track_editor_load');
add_action('wp_ajax_nopriv_rmbz_track_editor_load', 'rmbz_ajax_track_editor_load');


/* ============================================================================
 * AJAX — salvar dados da faixa
 * ============================================================================ */
function rmbz_ajax_track_editor_save() {
    if ( ! check_ajax_referer('rmbz_distro_nonce', 'nonce', false) ) {
        wp_send_json_error('Erro de segurança.', 403);
    }
    if ( ! function_exists('rmbz_is_user_logged_in') || ! rmbz_is_user_logged_in() ) {
        wp_send_json_error('Sessão expirada.', 401);
    }

    $telemovel = rmbz_get_current_user_telemovel();
    // [1.5.198] Detectar se é WP admin (override mode)
    $is_admin_override = function_exists('current_user_can') && current_user_can('manage_options');

    // FIX #5 — rate limit (30 saves/min é generoso mesmo para editar várias faixas)
    // Admin não tem rate limit (uso interno legítimo)
    if ( ! $is_admin_override && function_exists('rmbz_rate_limit') && ! rmbz_rate_limit( 'track_save_' . $telemovel, 30 ) ) {
        wp_send_json_error('Demasiadas alterações num curto período. Aguarda um minuto.', 429);
    }

    $payload_raw = isset($_POST['payload']) ? wp_unslash($_POST['payload']) : '';
    $payload = json_decode( $payload_raw, true );
    if ( ! is_array($payload) ) wp_send_json_error('Payload inválido.');

    $track_id   = absint( $payload['track_id']   ?? 0 );
    $release_id = absint( $payload['release_id'] ?? 0 );
    if ( $track_id <= 0 || $release_id <= 0 ) wp_send_json_error('IDs inválidos.');

    global $wpdb;
    $table_releases = $wpdb->prefix . 'rmbz_distro_releases';
    $table_tracks   = $wpdb->prefix . 'rmbz_distro_tracks';
    $table_splits   = $wpdb->prefix . 'rmbz_track_splits';

    // ========================================================================
    // FIX CRÍTICO #1 (IDOR) — ownership em três camadas:
    //   1. O release existe e pertence ao artista autenticado.
    //   2. A track existe.
    //   3. A track pertence a ESSE release.
    //
    // ANTES: só validava (1). Um atacante podia enviar o release_id dele +
    // o track_id de OUTRO artista e o UPDATE aplicava-se à track do outro.
    //
    // A query com JOIN valida tudo de uma vez:
    //   - track com este id existe
    //   - o release dessa track pertence ao artista logado
    //   - o release_id enviado no payload bate certo com o da track
    //
    // [1.5.198] Para admin (manage_options): bypass do match com telemovel,
    // mas mantém validação de que track pertence ao release_id do payload
    // (proteção IDOR continua activa — só altera quem é considerado "owner").
    // ========================================================================
    if ( $is_admin_override ) {
        $owner_check = $wpdb->get_var( $wpdb->prepare(
            "SELECT t.id
               FROM $table_tracks   AS t
               JOIN $table_releases AS r ON r.id = t.release_id
              WHERE t.id = %d
                AND r.id = %d",
            $track_id, $release_id
        ) );
    } else {
        $owner_check = $wpdb->get_var( $wpdb->prepare(
            "SELECT t.id
               FROM $table_tracks   AS t
               JOIN $table_releases AS r ON r.id = t.release_id
              WHERE t.id = %d
                AND r.id = %d
                AND r.artist_telemovel = %s",
            $track_id, $release_id, $telemovel
        ) );
    }
    if ( ! $owner_check ) wp_send_json_error('Sem permissão.', 403);

    // Impedir edição de tracks em releases já submetidos/live (redundante mas
    // defensivo — a UI já bloqueia, mas o servidor confirma).
    $rel_status = $wpdb->get_var( $wpdb->prepare(
        "SELECT status FROM $table_releases WHERE id = %d", $release_id
    ) );
    if ( ! $is_admin_override && ! in_array( $rel_status, ['incomplete', 'draft', 'returned'], true ) ) {
        wp_send_json_error('Este lançamento já foi submetido e não pode ser editado.', 403);
    }

    // Validações básicas
    $title = sanitize_text_field( $payload['track_title'] ?? '' );
    if ( empty($title) ) wp_send_json_error('Título obrigatório.');

    $isrc = strtoupper( preg_replace('/[^A-Z0-9]/', '', sanitize_text_field( $payload['isrc_code'] ?? '' )) );

    // Sanitizar listas
    $collaborators = [];
    if ( ! empty($payload['collaborators']) && is_array($payload['collaborators']) ) {
        $valid_roles = array_keys( rmbz_track_collaborator_roles() );
        foreach ( $payload['collaborators'] as $c ) {
            $name = sanitize_text_field( $c['name'] ?? '' );
            $role = in_array( $c['role'] ?? '', $valid_roles, true ) ? $c['role'] : 'feature';
            if ( $name ) $collaborators[] = [ 'name' => $name, 'role' => $role ];
        }
    }

    $songwriters = [];
    if ( ! empty($payload['songwriters']) && is_array($payload['songwriters']) ) {
        foreach ( $payload['songwriters'] as $s ) {
            $name = sanitize_text_field( $s['name'] ?? '' );
            if ( $name ) $songwriters[] = [ 'name' => $name ];
        }
    }

    // Validar splits (soma 100%)
    $splits_supplied = array_key_exists( 'splits', $payload );
    $splits_in = is_array( $payload['splits'] ?? null ) ? $payload['splits'] : [];
    $splits_clean = [];
    $total_pct = 0.0;
    foreach ( $splits_in as $sp ) {
        $pct = (float) ($sp['percentage'] ?? 0);
        if ( $pct <= 0 ) continue;
        $splits_clean[] = [
            'name'      => sanitize_text_field( $sp['name'] ?? '' ),
            'telemovel' => preg_replace('/[^0-9+]/', '', sanitize_text_field( $sp['telemovel'] ?? '' )),
            'percentage'=> round($pct, 2),
            'role'      => sanitize_key( $sp['role'] ?? 'feature' ),
        ];
        $total_pct += $pct;
    }
    if ( count($splits_clean) > 0 && abs( $total_pct - 100 ) > 0.01 ) {
        wp_send_json_error( 'Splits têm de somar 100% (actualmente: ' . round($total_pct, 2) . '%).' );
    }

    // Montar extra_data JSON
    $extra = [
        'is_instrumental'        => empty($payload['is_instrumental']) ? 0 : 1,
        'language'               => sanitize_text_field( $payload['language'] ?? '' ),
        'duration_seconds'       => 0, // preservado abaixo
        'collaborators'          => $collaborators,
        'songwriters'            => $songwriters,
        'has_lyrics'             => empty($payload['has_lyrics']) ? 0 : 1,
        'lyrics'                 => sanitize_textarea_field( $payload['lyrics'] ?? '' ),
        'previously_released'    => empty($payload['previously_released']) ? 0 : 1,
        'original_isrc'          => strtoupper( preg_replace('/[^A-Z0-9]/', '', sanitize_text_field( $payload['original_isrc'] ?? '' )) ),
        'is_cover'               => empty($payload['is_cover']) ? 0 : 1,
        'cover_original_title'   => sanitize_text_field( $payload['cover_original_title'] ?? '' ),
        'cover_original_composer'=> sanitize_text_field( $payload['cover_original_composer'] ?? '' ),
        'preview_start_seconds'  => absint( $payload['preview_start_seconds'] ?? 0 ),
        'moods'                  => array_map( 'sanitize_key', (array) ($payload['moods'] ?? []) ),
    ];

    // Preservar duração (capturada no upload)
    $existing = rmbz_track_get_extra( $track_id );
    if ( $existing ) {
        $existing_extra = is_array( $existing['extra'] ?? null ) ? $existing['extra'] : [];
        foreach ( [ 'is_instrumental', 'language', 'collaborators', 'songwriters', 'has_lyrics', 'lyrics', 'previously_released', 'original_isrc', 'is_cover', 'cover_original_title', 'cover_original_composer', 'preview_start_seconds', 'moods' ] as $extra_key ) {
            if ( ! array_key_exists( $extra_key, $payload ) && array_key_exists( $extra_key, $existing_extra ) ) {
                $extra[ $extra_key ] = $existing_extra[ $extra_key ];
            }
        }
        if ( ! empty( $existing_extra['duration_seconds'] ) ) {
            $extra['duration_seconds'] = (int) $existing_extra['duration_seconds'];
        }
    }

    // UPDATE track
    $updated = $wpdb->update(
        $table_tracks,
        [
            'track_title' => $title,
            'isrc_code'   => $isrc,
            'is_explicit' => empty($payload['is_explicit']) ? 0 : 1,
            'extra_data'  => wp_json_encode($extra),
        ],
        [ 'id' => $track_id ],
        [ '%s', '%s', '%d', '%s' ],
        [ '%d' ]
    );

    if ( $updated === false ) {
        error_log( '[1MZ] track_editor_save DB error: ' . $wpdb->last_error );
        wp_send_json_error( 'Erro ao guardar. Tenta novamente em instantes.' );
    }

    // Regravar splits (delete + insert)
    if ( $splits_supplied ) {
        $wpdb->delete( $table_splits, [ 'track_id' => $track_id ], [ '%d' ] );
        foreach ( $splits_clean as $sp ) {
            $wpdb->insert( $table_splits, [
                'track_id'               => $track_id,
                'release_id'             => $release_id,
                'collaborator_name'      => $sp['name'],
                'collaborator_telemovel' => $sp['telemovel'],
                'role'                   => $sp['role'],
                'percentage'             => $sp['percentage'],
                'invite_status'          => 'pending',
            ], [ '%d', '%d', '%s', '%s', '%s', '%f', '%s' ] );
        }
    }

    // Hook — dá ao módulo distribution-splits.php a chance de classificar
    // cada split (auto-accept para internos, external_hold para externos)
    // e enviar notificações.
    if ( $splits_supplied ) do_action('rmbz_track_splits_saved', $track_id);

    wp_send_json_success([
        'track_id'     => $track_id,
        'splits_count' => count($splits_clean),
        'message'      => 'Metadata guardada.',
    ]);
}
add_action('wp_ajax_rmbz_track_editor_save',        'rmbz_ajax_track_editor_save');
add_action('wp_ajax_nopriv_rmbz_track_editor_save', 'rmbz_ajax_track_editor_save');


/* ============================================================================
 * CAPTURA DE DURAÇÃO NO UPLOAD (integra com onemz_save_distro_track)
 * ============================================================================
 * Hook para capturar duração do MP3 usando getID3 (vem no WP core).
 * Esta função deve ser chamada após o INSERT da faixa.
 */
function rmbz_track_capture_duration( $track_id, $audio_url ) {
    if ( ! $audio_url ) return;

    // Converter URL para path local
    $upload_dir = wp_upload_dir();
    $audio_path = str_replace( $upload_dir['baseurl'], $upload_dir['basedir'], $audio_url );
    if ( ! file_exists( $audio_path ) ) return;

    require_once( ABSPATH . 'wp-admin/includes/media.php' );
    if ( ! function_exists('wp_read_audio_metadata') ) return;

    $meta = wp_read_audio_metadata( $audio_path );
    if ( empty($meta['length']) ) return;

    $duration = (int) round( $meta['length'] );

    global $wpdb;
    $table = $wpdb->prefix . 'rmbz_distro_tracks';
    $row = $wpdb->get_row( $wpdb->prepare( "SELECT extra_data FROM $table WHERE id = %d", $track_id ) );
    $extra = [];
    if ( $row && $row->extra_data ) {
        $decoded = json_decode( $row->extra_data, true );
        if ( is_array($decoded) ) $extra = $decoded;
    }
    $extra['duration_seconds'] = $duration;

    $wpdb->update( $table,
        [ 'extra_data' => wp_json_encode($extra) ],
        [ 'id' => $track_id ],
        [ '%s' ], [ '%d' ]
    );
}
add_action('rmbz_track_saved', 'rmbz_track_capture_duration', 10, 2);
