<?php
/**
 * 1MZ For Artists — Submissions Controller (REST)
 *
 * Expõe à app o sistema de submissões de publicação que existia apenas
 * no site (tab "tab-submissions" da minha-conta + form-handler.php).
 *
 * Endpoints:
 *   GET  /submissions                   Lista submissões do artista
 *   GET  /submissions/options           Géneros + canais sociais
 *   GET  /submissions/{id}              Detalhe completo
 *   POST /submissions/site              Cria "Publicação Site"
 *   POST /submissions/social            Cria "Redes Sociais"
 *   POST /submissions/{id}/correction   Submete correcção
 *   POST /uploads/submission-asset      Upload de imagem/áudio (devolve URL)
 *
 * Regras (mantém paridade com o site):
 *   - Subscrição VIP activa OBRIGATÓRIA
 *   - Permissão 'site' para Publicação Site, 'social' para Redes Sociais
 *   - Apenas 1 submissão pendente por artista de cada vez
 *
 * @package 1MZ_For_Artists
 * @since 1.6.26
 */

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

if ( ! defined( 'RMBZ_API_SUBMISSIONS_VERSION' ) ) {
    define( 'RMBZ_API_SUBMISSIONS_VERSION', '1.6.33.0' );
}


/* ============================================================================
 * REGISTO DOS ENDPOINTS
 * ============================================================================ */

add_action( 'rest_api_init', 'rmbz_api_register_submissions_routes' );

function rmbz_api_register_submissions_routes() {
    $ns = RMBZ_API_NAMESPACE;
    $auth = 'rmbz_api_require_auth';

    register_rest_route( $ns, '/submissions', [
        'methods'             => 'GET',
        'callback'            => 'rmbz_api_handle_submissions_list',
        'permission_callback' => $auth,
    ] );

    register_rest_route( $ns, '/submissions/options', [
        'methods'             => 'GET',
        'callback'            => 'rmbz_api_handle_submissions_options',
        'permission_callback' => $auth,
    ] );

    register_rest_route( $ns, '/submissions/(?P<id>\d+)', [
        'methods'             => 'GET',
        'callback'            => 'rmbz_api_handle_submission_detail',
        'permission_callback' => $auth,
        'args' => [
            'id' => [ 'required' => true, 'validate_callback' => fn( $v ) => is_numeric( $v ) ],
        ],
    ] );

    register_rest_route( $ns, '/submissions/site', [
        'methods'             => 'POST',
        'callback'            => 'rmbz_api_handle_submission_create_site',
        'permission_callback' => $auth,
    ] );

    register_rest_route( $ns, '/submissions/social', [
        'methods'             => 'POST',
        'callback'            => 'rmbz_api_handle_submission_create_social',
        'permission_callback' => $auth,
    ] );

    register_rest_route( $ns, '/submissions/(?P<id>\d+)/correction', [
        'methods'             => 'POST',
        'callback'            => 'rmbz_api_handle_submission_correction',
        'permission_callback' => $auth,
        'args' => [
            'id' => [ 'required' => true, 'validate_callback' => fn( $v ) => is_numeric( $v ) ],
        ],
    ] );

    register_rest_route( $ns, '/uploads/submission-asset', [
        'methods'             => 'POST',
        'callback'            => 'rmbz_api_handle_submission_asset_upload',
        'permission_callback' => $auth,
    ] );
}


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

/**
 * Resolve o telemovel do request, com fallback de re-auth (mesma técnica
 * usada no subscription-controller para evitar bug de param vazio em
 * POSTs JSON).
 */
function rmbz_api_subm_resolve_tel( WP_REST_Request $request ) {
    $tel = (string) $request->get_param( 'rmbz_telemovel' );
    if ( $tel === '' && function_exists( 'rmbz_api_authenticate_request' ) ) {
        $auth = rmbz_api_authenticate_request();
        if ( ! is_wp_error( $auth ) ) {
            $tel = (string) $auth;
        }
    }
    return $tel;
}

/**
 * Verifica se o user tem subscrição activa + permissão concreta.
 *
 * @param string $tel
 * @param string $required_perm  'site' ou 'social'
 * @return WP_REST_Response|null  null = OK, response = erro a propagar
 */
function rmbz_api_subm_require_perm( $tel, $required_perm ) {
    if ( ! function_exists( 'rmbz_user_has_active_subscription' ) ||
         ! rmbz_user_has_active_subscription( $tel ) ) {
        return rmbz_api_error(
            'Precisas de uma subscrição VIP activa para submeter.',
            403,
            'no_active_subscription'
        );
    }

    global $wpdb;
    $extra_raw = $wpdb->get_var( $wpdb->prepare(
        "SELECT extra_data FROM {$wpdb->prefix}rmbz_wallets WHERE user_telemovel = %s",
        $tel
    ) );
    $extra = $extra_raw ? json_decode( $extra_raw, true ) : [];
    if ( ! is_array( $extra ) ) $extra = [];
    $perms = isset( $extra['vip_permissions'] ) && is_array( $extra['vip_permissions'] )
        ? $extra['vip_permissions']
        : [];

    if ( empty( $perms[ $required_perm ] ) ) {
        $label = $required_perm === 'site' ? 'Publicação no site' : 'Redes sociais';
        return rmbz_api_error(
            "O teu plano não inclui \"$label\". Faz upgrade para usar.",
            403,
            'missing_permission',
            [ 'missing_perm' => $required_perm ]
        );
    }

    return null;
}

/**
 * Bloquear se já existe uma submissão pendente OU em correcção.
 * (Mantém paridade com o site.)
 */
function rmbz_api_subm_require_no_pending( $tel ) {
    global $wpdb;
    $table = $wpdb->prefix . 'rmbz_artist_submissions';
    $count = (int) $wpdb->get_var( $wpdb->prepare(
        "SELECT COUNT(*) FROM $table
         WHERE artist_telemovel = %s
           AND review_status IN ('pendente', 'correcao')",
        $tel
    ) );
    if ( $count > 0 ) {
        return rmbz_api_error(
            'Já tens uma submissão em análise. Aguarda pela avaliação antes de enviar outra.',
            409,
            'already_pending'
        );
    }
    return null;
}

/**
 * Validador simples de URLs por plataforma (réplica de
 * rmbz_validar_link_plataforma do form-handler).
 */
function rmbz_api_subm_validate_link( $url, $platform ) {
    if ( empty( $url ) ) return true; // opcional

    $url_lower = strtolower( $url );
    switch ( $platform ) {
        case 'youtube':
            return strpos( $url_lower, 'youtube.com' ) !== false ||
                   strpos( $url_lower, 'youtu.be' )    !== false;
        case 'spotify':
            return strpos( $url_lower, 'spotify.com' ) !== false ||
                   strpos( $url_lower, 'open.spotify' ) !== false;
        case 'apple':
            return strpos( $url_lower, 'music.apple.com' ) !== false ||
                   strpos( $url_lower, 'apple.co' )        !== false;
        case 'drive':
            return strpos( $url_lower, 'drive.google.com' ) !== false ||
                   strpos( $url_lower, 'dropbox.com' )      !== false ||
                   strpos( $url_lower, 'wetransfer.com' )   !== false ||
                   strpos( $url_lower, 'mega.nz' )          !== false ||
                   strpos( $url_lower, 'mega.io' )          !== false;
    }
    return true;
}

/**
 * Normaliza uma submissão (linha BD) para resposta REST.
 */
function rmbz_api_subm_format_for_list( $row ) {
    $extra = ! empty( $row->extra_data ) ? json_decode( $row->extra_data, true ) : [];
    if ( ! is_array( $extra ) ) $extra = [];

    $cover = $extra['cover_image']  ?? $extra['thumbnail_url'] ?? null;
    $needs_correction = $row->review_status === 'correcao';

    return [
        'id'               => (int) $row->id,
        'music_title'      => (string) $row->music_title,
        'scenario_type'    => (string) $row->scenario_type,
        'review_status'    => (string) $row->review_status,
        'submission_date'  => (string) $row->submission_date,
        'cover_url'        => $cover,
        'needs_correction' => $needs_correction,
    ];
}

function rmbz_api_subm_format_detail( $row ) {
    $extra = ! empty( $row->extra_data ) ? json_decode( $row->extra_data, true ) : [];
    if ( ! is_array( $extra ) ) $extra = [];

    // [1.6.118.128] Publicação agendada: se há scheduled_publish_at e ainda
    // não chegou a hora, os links ficam ESCONDIDOS (null) e o app/site mostram
    // a contagem decrescente. Quando a hora chega, aparecem sozinhos.
    $sched_raw = $extra['scheduled_publish_at'] ?? '';
    $publish_ts = $sched_raw !== '' ? strtotime( $sched_raw . ' UTC' ) : 0;
    $is_pending_schedule = ( $publish_ts > 0 && $publish_ts > time() );

    $facebook_url  = ! empty( $extra['facebook_url'] )  ? esc_url_raw( $extra['facebook_url'] )  : null;
    $youtube_url   = ! empty( $extra['youtube_url'] )   ? esc_url_raw( $extra['youtube_url'] )   : null;
    $published_url = ! empty( $extra['published_url'] ) ? esc_url_raw( $extra['published_url'] ) : null;
    if ( $is_pending_schedule || (string) $row->review_status !== 'finalizado' ) {
        // Esconder os links até à publicação final e à hora agendada.
        $facebook_url = null; $youtube_url = null; $published_url = null;
    }

    // Normalizar shape do extra para o que a app espera
    $extra_normalized = [
        'artist_name'   => $extra['artist_name']   ?? null,
        'has_feat'      => $extra['has_feat']      ?? null,
        'feat_name'     => $extra['feat_name']     ?? null,
        'release_date'  => $extra['release_date']  ?? null,
        'cover_image'   => $extra['cover_image']   ?? null,
        'is_album'      => ! empty( $extra['is_album'] ),
        'album_tracks'  => isset( $extra['album_tracks'] ) && is_array( $extra['album_tracks'] )
            ? array_map( fn( $t ) => [
                'title' => (string) ( $t['title'] ?? '' ),
                'url'   => (string) ( $t['url']   ?? '' ),
            ], $extra['album_tracks'] )
            : [],
        'youtube_link'  => $extra['youtube_link']  ?? null,
        'spotify_link'  => $extra['spotify_link']  ?? null,
        'apple_link'    => $extra['apple_link']    ?? null,
        'social_link'   => $extra['social_link']   ?? null,
        'description'   => $extra['description']   ?? null,
        'video_link_hd' => $extra['video_link_hd'] ?? null,
        'thumbnail_url' => $extra['thumbnail_url'] ?? null,
        'channels'      => $extra['channels']      ?? null,
        'social_caption'=> $extra['social_caption']?? null,
        // [1.6.33] Links oficiais publicados pelo admin quando a submissão
        // é marcada como "finalizado". Antes não eram expostos ao app —
        // o artista só os via no site. Agora aparecem destacados na tela
        // de detalhe da submissão.
        //   - facebook_url / youtube_url: Redes Sociais
        //   - published_url:              Publicação Site
        'facebook_url'  => $facebook_url,
        'youtube_url'   => $youtube_url,
        'published_url' => $published_url,
        // [1.6.118.128] Agendamento: data ISO UTC (ou null) + flag de pendente.
        'scheduled_publish_at' => $publish_ts > 0 ? gmdate( 'Y-m-d\TH:i:s\Z', $publish_ts ) : null,
        'is_scheduled_pending' => $is_pending_schedule,
    ];

    $correction_flags = null;
    if ( $row->review_status === 'correcao' ) {
        $correction_flags = [
            'needs_audio' => ! empty( $extra['needs_audio_fix'] ),
            'needs_cover' => ! empty( $extra['needs_cover_fix'] ),
            'needs_link'  => ! empty( $extra['needs_link_fix'] ),
        ];
    }

    return [
        'id'               => (int) $row->id,
        'music_title'      => (string) $row->music_title,
        'music_genre'      => (string) $row->music_genre,
        'scenario_type'    => (string) $row->scenario_type,
        'review_status'    => (string) $row->review_status,
        'submission_date'  => (string) $row->submission_date,
        'rejection_reason' => $row->rejection_reason ? (string) $row->rejection_reason : null,
        'music_file_url'   => $row->music_file_url   ? (string) $row->music_file_url   : null,
        'extra'            => $extra_normalized,
        'correction_flags' => $correction_flags,
    ];
}


/* ============================================================================
 * GET /submissions
 * ============================================================================ */

function rmbz_api_handle_submissions_list( WP_REST_Request $request ) {
    $tel = rmbz_api_subm_resolve_tel( $request );
    if ( $tel === '' ) return rmbz_api_error( 'Sessão inválida.', 401, 'auth_required' );

    global $wpdb;
    $table = $wpdb->prefix . 'rmbz_artist_submissions';

    $rows = $wpdb->get_results( $wpdb->prepare(
        "SELECT id, music_title, scenario_type, review_status, submission_date,
                extra_data
         FROM $table
         WHERE artist_telemovel = %s
         ORDER BY submission_date DESC, id DESC
         LIMIT 200",
        $tel
    ) );

    $submissions = [];
    $has_pending = false;
    if ( $rows ) {
        foreach ( $rows as $r ) {
            $submissions[] = rmbz_api_subm_format_for_list( $r );
            if ( in_array( $r->review_status, [ 'pendente', 'correcao' ], true ) ) {
                $has_pending = true;
            }
        }
    }

    return rmbz_api_ok( [
        '_api_version' => RMBZ_API_SUBMISSIONS_VERSION,
        'submissions'  => $submissions,
        'total'        => count( $submissions ),
        'has_pending'  => $has_pending,
    ] );
}


/* ============================================================================
 * GET /submissions/options
 * ============================================================================ */

function rmbz_api_handle_submissions_options( WP_REST_Request $request ) {
    // [1.6.26.2] Géneros e canais EXACTAMENTE iguais aos do
    // template/frontend/artist-upload-form.php (linhas 259 e 370).
    // R&B só está disponível para Publicação Site, não para Redes Sociais.
    $genres_site = [
        'Hip-Hop', 'Trap', 'R&B', 'Pandza', 'Kizomba',
        'Afro House', 'Amapiano', 'Marrabenta', 'Gospel', 'Outro',
    ];
    $genres_social = [
        'Hip-Hop', 'Trap', 'Pandza', 'Kizomba', 'Afro House',
        'Amapiano', 'Marrabenta', 'Gospel', 'Outro',
    ];

    // Apenas Facebook e YouTube — exactamente como no site (linha 347-348).
    $social_channels = [ 'Facebook', 'YouTube' ];

    return rmbz_api_ok( [
        '_api_version'    => RMBZ_API_SUBMISSIONS_VERSION,
        'genres'          => $genres_site,           // backward-compat (lista combinada padrão)
        'genres_site'     => $genres_site,
        'genres_social'   => $genres_social,
        'social_channels' => $social_channels,
    ] );
}


/* ============================================================================
 * GET /submissions/{id}
 * ============================================================================ */

function rmbz_api_handle_submission_detail( WP_REST_Request $request ) {
    $tel = rmbz_api_subm_resolve_tel( $request );
    if ( $tel === '' ) return rmbz_api_error( 'Sessão inválida.', 401, 'auth_required' );

    $sub_id = (int) $request->get_param( 'id' );
    if ( $sub_id <= 0 ) {
        return rmbz_api_error( 'ID inválido.', 400, 'invalid_id' );
    }

    global $wpdb;
    $row = $wpdb->get_row( $wpdb->prepare(
        "SELECT * FROM {$wpdb->prefix}rmbz_artist_submissions
         WHERE id = %d AND artist_telemovel = %s",
        $sub_id, $tel
    ) );

    if ( ! $row ) {
        return rmbz_api_error( 'Submissão não encontrada.', 404, 'not_found' );
    }

    return rmbz_api_ok( [
        '_api_version' => RMBZ_API_SUBMISSIONS_VERSION,
        'submission'   => rmbz_api_subm_format_detail( $row ),
    ] );
}


/* ============================================================================
 * POST /submissions/site
 * ============================================================================ */

function rmbz_api_handle_submission_create_site( WP_REST_Request $request ) {
    // [1.6.47] Bloqueio global de "Gestão de Músicas (Site)"
    if ( function_exists( 'rmbz_is_globally_blocked' ) && rmbz_is_globally_blocked( 'site' ) ) {
        return rmbz_api_error(
            'A publicação de músicas no site está temporariamente suspensa pela administração. Tenta de novo mais tarde.',
            403,
            'site_globally_blocked'
        );
    }

    $tel = rmbz_api_subm_resolve_tel( $request );
    if ( $tel === '' ) return rmbz_api_error( 'Sessão inválida.', 401, 'auth_required' );

    // Permissão
    $perm_err = rmbz_api_subm_require_perm( $tel, 'site' );
    if ( $perm_err ) return $perm_err;

    // Anti-duplicado
    $pending_err = rmbz_api_subm_require_no_pending( $tel );
    if ( $pending_err ) return $pending_err;

    $body = $request->get_json_params();
    if ( ! is_array( $body ) ) $body = [];

    // Campos básicos
    $music_title = trim( (string) ( $body['music_title'] ?? '' ) );
    $music_genre = trim( (string) ( $body['music_genre'] ?? '' ) );
    $artist_name = trim( (string) ( $body['artist_name'] ?? '' ) );
    $is_album    = ! empty( $body['is_album'] );
    $audio_url   = trim( (string) ( $body['audio_file_url'] ?? '' ) );
    $cover_url   = trim( (string) ( $body['cover_image_url'] ?? '' ) );
    $album_tracks = isset( $body['album_tracks'] ) && is_array( $body['album_tracks'] )
        ? $body['album_tracks'] : [];

    // Campos finais (paridade EXACTA com o site — todos OBRIGATÓRIOS)
    $release_date = trim( (string) ( $body['release_date'] ?? '' ) );
    $social_link  = trim( (string) ( $body['social_link']  ?? '' ) );
    $description  = trim( (string) ( $body['description']  ?? '' ) );

    // Toggles condicionais (paridade com radios has_video / on_platforms do site)
    $has_video    = (string) ( $body['has_video']    ?? 'no' );
    $on_platforms = (string) ( $body['on_platforms'] ?? 'no' );

    // Checkboxes obrigatórios (paridade com declaration_check_site / terms_check_site)
    $declaration_check = ! empty( $body['declaration_check'] );
    $terms_check       = ! empty( $body['terms_check'] );

    // Validações campo a campo (mesma ordem do form site)
    if ( $music_title === '' )  return rmbz_api_error( 'Título obrigatório.', 422, 'missing_title' );
    if ( $artist_name === '' )  return rmbz_api_error( 'Nome do artista obrigatório.', 422, 'missing_artist' );
    if ( $music_genre === '' )  return rmbz_api_error( 'Género obrigatório.', 422, 'missing_genre' );
    if ( $release_date === '' ) return rmbz_api_error( 'Data ideal de publicação obrigatória.', 422, 'missing_release_date' );
    if ( $cover_url === '' )    return rmbz_api_error( 'Capa obrigatória.', 422, 'missing_cover' );

    if ( $is_album ) {
        if ( count( $album_tracks ) < 2 ) {
            return rmbz_api_error( 'Álbum precisa de pelo menos 2 faixas.', 422, 'album_too_small' );
        }
        if ( count( $album_tracks ) > 30 ) {
            return rmbz_api_error( 'Álbum não pode ter mais de 30 faixas.', 422, 'album_too_big' );
        }
        $sanitized_tracks = [];
        foreach ( $album_tracks as $t ) {
            if ( ! is_array( $t ) ) continue;
            $title = trim( (string) ( $t['title'] ?? '' ) );
            $url   = trim( (string) ( $t['url']   ?? '' ) );
            if ( $title === '' || $url === '' ) {
                return rmbz_api_error(
                    'Cada faixa do álbum precisa de título e ficheiro.',
                    422, 'album_track_invalid'
                );
            }
            $sanitized_tracks[] = [ 'title' => $title, 'url' => esc_url_raw( $url ) ];
        }
        $album_tracks = $sanitized_tracks;
        $audio_url = 'Álbum/EP (Múltiplas Faixas)';
    } else {
        if ( $audio_url === '' ) {
            return rmbz_api_error( 'Ficheiro de áudio obrigatório.', 422, 'missing_audio' );
        }
        $album_tracks = [];
    }

    // Links opcionais — só validar se vierem
    $youtube_link = trim( (string) ( $body['youtube_link'] ?? '' ) );
    $spotify_link = trim( (string) ( $body['spotify_link'] ?? '' ) );
    $apple_link   = trim( (string) ( $body['apple_link']   ?? '' ) );

    // Se has_video='yes', exige link do YouTube válido
    if ( $has_video === 'yes' ) {
        if ( $youtube_link === '' ) {
            return rmbz_api_error( 'Link do YouTube obrigatório quando indicas que tens videoclipe.', 422, 'missing_youtube' );
        }
        if ( ! rmbz_api_subm_validate_link( $youtube_link, 'youtube' ) ) {
            return rmbz_api_error( 'Link do YouTube inválido (deve ser youtube.com ou youtu.be).', 422, 'invalid_youtube' );
        }
    }
    // Se on_platforms='yes', exige pelo menos um dos dois links
    if ( $on_platforms === 'yes' ) {
        if ( $spotify_link === '' && $apple_link === '' ) {
            return rmbz_api_error( 'Se a música está nas plataformas, indica pelo menos o link do Spotify ou da Apple Music.', 422, 'missing_platform_link' );
        }
        if ( $spotify_link !== '' && ! rmbz_api_subm_validate_link( $spotify_link, 'spotify' ) ) {
            return rmbz_api_error( 'Link do Spotify inválido.', 422, 'invalid_spotify' );
        }
        if ( $apple_link !== '' && ! rmbz_api_subm_validate_link( $apple_link, 'apple' ) ) {
            return rmbz_api_error( 'Link da Apple Music inválido.', 422, 'invalid_apple' );
        }
    } else {
        // Se on_platforms='no', limpamos os links — não devem ir
        $spotify_link = '';
        $apple_link   = '';
    }
    // Se has_video='no', limpamos o youtube_link
    if ( $has_video === 'no' ) {
        $youtube_link = '';
    }

    // Final: redes sociais e descrição obrigatórias (mesmo que o site)
    if ( $social_link === '' ) {
        return rmbz_api_error( 'Link das redes sociais oficiais obrigatório.', 422, 'missing_social_link' );
    }
    if ( $description === '' ) {
        return rmbz_api_error( 'Descrição da música obrigatória.', 422, 'missing_description' );
    }

    // Checkboxes (mesma estratégia do site — bloqueia se não marcou)
    if ( ! $declaration_check ) {
        return rmbz_api_error( 'Tens de aceitar a Declaração de Autoria.', 422, 'missing_declaration' );
    }
    if ( ! $terms_check ) {
        return rmbz_api_error( 'Tens de aceitar os Termos e Condições.', 422, 'missing_terms' );
    }

    $extra_data = [
        'is_album'      => $is_album,
        'album_tracks'  => $album_tracks,
        'artist_name'   => sanitize_text_field( $artist_name ),
        'has_feat'      => sanitize_text_field( (string) ( $body['has_feat'] ?? 'no' ) ),
        'feat_name'     => sanitize_text_field( (string) ( $body['feat_name'] ?? '' ) ),
        'release_date'  => sanitize_text_field( $release_date ),
        'has_video'     => $has_video,
        'youtube_link'  => esc_url_raw( $youtube_link ),
        'on_platforms'  => $on_platforms,
        'spotify_link'  => esc_url_raw( $spotify_link ),
        'apple_link'    => esc_url_raw( $apple_link ),
        'social_link'   => esc_url_raw( $social_link ),
        'description'   => sanitize_textarea_field( $description ),
        'cover_image'   => esc_url_raw( $cover_url ),
    ];

    global $wpdb;
    $inserted = $wpdb->insert(
        $wpdb->prefix . 'rmbz_artist_submissions',
        [
            'artist_telemovel' => $tel,
            'music_title'      => sanitize_text_field( $music_title ),
            'music_file_url'   => esc_url_raw( $audio_url ),
            'music_genre'      => sanitize_text_field( $music_genre ),
            'scenario_type'    => 'Publicação Site',
            'review_status'    => 'pendente',
            'submission_date'  => current_time( 'mysql' ),
            'extra_data'       => wp_json_encode( $extra_data ),
        ]
    );

    if ( $inserted === false ) {
        return rmbz_api_error( 'Falha a guardar submissão na base de dados.', 500, 'db_error' );
    }

    $sub_id = (int) $wpdb->insert_id;
    do_action( 'rmbz_site_submission_created', $sub_id );

    return rmbz_api_ok( [
        '_api_version'  => RMBZ_API_SUBMISSIONS_VERSION,
        'success'       => true,
        'submission_id' => $sub_id,
        'message'       => 'Submissão enviada para análise. A 1MZ vai rever em breve.',
    ], 201 );
}


/* ============================================================================
 * POST /submissions/social
 * ============================================================================ */

function rmbz_api_handle_submission_create_social( WP_REST_Request $request ) {
    // [1.6.47] Bloqueio global de "Promover Vídeos (Redes Sociais)"
    if ( function_exists( 'rmbz_is_globally_blocked' ) && rmbz_is_globally_blocked( 'social' ) ) {
        return rmbz_api_error(
            'A promoção nas redes sociais está temporariamente suspensa pela administração. Tenta de novo mais tarde.',
            403,
            'social_globally_blocked'
        );
    }

    $tel = rmbz_api_subm_resolve_tel( $request );
    if ( $tel === '' ) return rmbz_api_error( 'Sessão inválida.', 401, 'auth_required' );

    $perm_err = rmbz_api_subm_require_perm( $tel, 'social' );
    if ( $perm_err ) return $perm_err;

    $pending_err = rmbz_api_subm_require_no_pending( $tel );
    if ( $pending_err ) return $pending_err;

    $body = $request->get_json_params();
    if ( ! is_array( $body ) ) $body = [];

    $video_title  = trim( (string) ( $body['video_title']    ?? '' ) );
    $video_genre  = trim( (string) ( $body['video_genre']    ?? '' ) );
    $artist_name  = trim( (string) ( $body['artist_name']    ?? '' ) );
    $video_link   = trim( (string) ( $body['video_link_hd']  ?? '' ) );
    $thumbnail    = trim( (string) ( $body['thumbnail_url']  ?? '' ) );
    $release_date = trim( (string) ( $body['release_date']   ?? '' ) );
    $social_caption = trim( (string) ( $body['social_caption'] ?? '' ) );
    $channels     = isset( $body['channels'] ) && is_array( $body['channels'] )
        ? array_map( 'sanitize_text_field', $body['channels'] )
        : [];

    $declaration_check = ! empty( $body['declaration_check'] );
    $terms_check       = ! empty( $body['terms_check'] );

    // Validações (paridade com video_social do site)
    if ( $artist_name === '' )    return rmbz_api_error( 'Nome do artista obrigatório.', 422, 'missing_artist' );
    if ( $video_title === '' )    return rmbz_api_error( 'Título do vídeo obrigatório.', 422, 'missing_title' );
    if ( empty( $channels ) )     return rmbz_api_error( 'Escolhe pelo menos 1 canal de distribuição.', 422, 'no_channels' );
    if ( $video_link === '' )     return rmbz_api_error( 'Link da obra (download HD) obrigatório.', 422, 'missing_video_link' );
    if ( ! rmbz_api_subm_validate_link( $video_link, 'drive' ) ) {
        return rmbz_api_error(
            'Link inválido. Deve ser do Google Drive, Dropbox, WeTransfer ou Mega.',
            422, 'invalid_drive_link'
        );
    }
    if ( $thumbnail === '' )      return rmbz_api_error( 'Capa do vídeo (thumbnail) obrigatória.', 422, 'missing_thumbnail' );
    if ( $video_genre === '' )    return rmbz_api_error( 'Género musical obrigatório.', 422, 'missing_genre' );
    if ( $release_date === '' )   return rmbz_api_error( 'Data ideal de publicação obrigatória.', 422, 'missing_release_date' );
    if ( $social_caption === '' ) return rmbz_api_error( 'Legenda do post obrigatória.', 422, 'missing_caption' );
    if ( ! $declaration_check )   return rmbz_api_error( 'Tens de aceitar a Declaração de Autoria.', 422, 'missing_declaration' );
    if ( ! $terms_check )         return rmbz_api_error( 'Tens de aceitar os Termos e Condições.', 422, 'missing_terms' );

    $extra_data = [
        'artist_name'    => sanitize_text_field( $artist_name ),
        'video_link_hd'  => esc_url_raw( $video_link ),
        'thumbnail_url'  => esc_url_raw( $thumbnail ),
        'channels'       => implode( ', ', $channels ),
        'has_feat'       => sanitize_text_field( (string) ( $body['has_feat'] ?? 'no' ) ),
        'feat_name'      => sanitize_text_field( (string) ( $body['feat_name'] ?? '' ) ),
        'release_date'   => sanitize_text_field( $release_date ),
        'social_caption' => sanitize_textarea_field( $social_caption ),
    ];

    global $wpdb;
    $inserted = $wpdb->insert(
        $wpdb->prefix . 'rmbz_artist_submissions',
        [
            'artist_telemovel' => $tel,
            'music_title'      => sanitize_text_field( $video_title ),
            'music_file_url'   => '', // Redes Sociais não tem ficheiro local
            'music_genre'      => sanitize_text_field( $video_genre ),
            'scenario_type'    => 'Redes Sociais',
            'review_status'    => 'pendente',
            'submission_date'  => current_time( 'mysql' ),
            'extra_data'       => wp_json_encode( $extra_data ),
        ]
    );

    if ( $inserted === false ) {
        return rmbz_api_error( 'Falha a guardar submissão.', 500, 'db_error' );
    }

    return rmbz_api_ok( [
        '_api_version'  => RMBZ_API_SUBMISSIONS_VERSION,
        'success'       => true,
        'submission_id' => (int) $wpdb->insert_id,
        'message'       => 'Vídeo enviado para análise. A equipa vai rever em breve.',
    ], 201 );
}


/* ============================================================================
 * POST /submissions/{id}/correction
 * ============================================================================ */

function rmbz_api_handle_submission_correction( WP_REST_Request $request ) {
    $tel = rmbz_api_subm_resolve_tel( $request );
    if ( $tel === '' ) return rmbz_api_error( 'Sessão inválida.', 401, 'auth_required' );

    $sub_id = (int) $request->get_param( 'id' );
    if ( $sub_id <= 0 ) return rmbz_api_error( 'ID inválido.', 400, 'invalid_id' );

    global $wpdb;
    $table = $wpdb->prefix . 'rmbz_artist_submissions';
    $row = $wpdb->get_row( $wpdb->prepare(
        "SELECT * FROM $table WHERE id = %d AND artist_telemovel = %s",
        $sub_id, $tel
    ) );

    if ( ! $row ) {
        return rmbz_api_error( 'Submissão não encontrada.', 404, 'not_found' );
    }
    if ( $row->review_status !== 'correcao' ) {
        return rmbz_api_error(
            'Esta submissão não está em estado de correcção.',
            409, 'not_in_correction'
        );
    }

    $body = $request->get_json_params();
    if ( ! is_array( $body ) ) $body = [];

    $extra = ! empty( $row->extra_data ) ? json_decode( $row->extra_data, true ) : [];
    if ( ! is_array( $extra ) ) $extra = [];

    $new_audio = trim( (string) ( $body['new_audio_url'] ?? '' ) );
    $new_cover = trim( (string) ( $body['new_cover_url'] ?? '' ) );
    $new_link  = trim( (string) ( $body['new_link']      ?? '' ) );

    // Validar contra as flags activas
    $needs_audio = ! empty( $extra['needs_audio_fix'] );
    $needs_cover = ! empty( $extra['needs_cover_fix'] );
    $needs_link  = ! empty( $extra['needs_link_fix'] );

    if ( $needs_audio && $new_audio === '' ) {
        return rmbz_api_error( 'A correcção exige novo ficheiro de áudio.', 422, 'missing_audio' );
    }
    if ( $needs_cover && $new_cover === '' ) {
        return rmbz_api_error( 'A correcção exige nova capa/imagem.', 422, 'missing_cover' );
    }
    if ( $needs_link && $new_link === '' ) {
        return rmbz_api_error( 'A correcção exige novo link.', 422, 'missing_link' );
    }

    $updated = false;

    // 1. Novo áudio (substitui music_file_url directo)
    if ( $new_audio !== '' ) {
        $wpdb->update(
            $table,
            [ 'music_file_url' => esc_url_raw( $new_audio ) ],
            [ 'id' => $sub_id ]
        );
        $updated = true;
    }

    // 2. Nova capa/thumbnail (vai para extra_data conforme o tipo)
    if ( $new_cover !== '' ) {
        if ( $row->scenario_type === 'Redes Sociais' ) {
            $extra['thumbnail_url'] = esc_url_raw( $new_cover );
        } else {
            $extra['cover_image'] = esc_url_raw( $new_cover );
        }
        $updated = true;
    }

    // 3. Novo link (vai para extra_data — auto-detectar plataforma)
    if ( $new_link !== '' ) {
        $link = esc_url_raw( $new_link );
        if ( $row->scenario_type === 'Redes Sociais' ) {
            if ( strpos( $link, 'facebook.com' ) !== false ) {
                $extra['facebook_url'] = $link;
            } elseif ( strpos( $link, 'youtube.com' ) !== false ||
                       strpos( $link, 'youtu.be' )    !== false ) {
                $extra['youtube_url'] = $link;
            } else {
                $extra['video_link_hd'] = $link;
            }
        } else {
            $extra['youtube_link'] = $link;
        }
        $updated = true;
    }

    if ( ! $updated ) {
        return rmbz_api_error(
            'Nenhum ficheiro ou link novo foi enviado.',
            422, 'no_correction_data'
        );
    }

    // Limpar flags + voltar para pendente
    unset( $extra['needs_audio_fix'], $extra['needs_cover_fix'], $extra['needs_link_fix'] );

    $wpdb->update(
        $table,
        [
            'extra_data'       => wp_json_encode( $extra ),
            'review_status'    => 'pendente',
            'rejection_reason' => '⚠️ Correcção enviada pelo artista (via app). A rever.',
        ],
        [ 'id' => $sub_id ]
    );

    return rmbz_api_ok( [
        '_api_version'  => RMBZ_API_SUBMISSIONS_VERSION,
        'success'       => true,
        'submission_id' => $sub_id,
        'message'       => 'Correcção enviada. A submissão volta para análise.',
    ] );
}


/* ============================================================================
 * POST /uploads/submission-asset
 *
 * Upload genérico para submissões. Aceita imagens (jpg/png) e áudio (mp3/wav),
 * tamanho máx 50MB. Devolve URL directa.
 * ============================================================================ */

function rmbz_api_handle_submission_asset_upload( WP_REST_Request $request ) {
    $tel = rmbz_api_subm_resolve_tel( $request );
    if ( $tel === '' ) return rmbz_api_error( 'Sessão inválida.', 401, 'auth_required' );

    if ( empty( $_FILES['file'] ) ) {
        return rmbz_api_error( 'Nenhum ficheiro enviado.', 422, 'no_file' );
    }

    $file = $_FILES['file'];

    if ( ! empty( $file['error'] ) && $file['error'] !== UPLOAD_ERR_OK ) {
        return rmbz_api_error( 'Erro no upload.', 500, 'upload_error' );
    }
    if ( ! is_uploaded_file( $file['tmp_name'] ) ) {
        return rmbz_api_error( 'Ficheiro inválido.', 422, 'invalid_file' );
    }

    // [1.5.218] Determinar tipo de asset (vindo do app) para aplicar validações.
    // Valores aceites: site_image | social_image | site_audio
    // Se ausente (chamadas de versões antigas), faz check genérico.
    $kind = isset( $_POST['kind'] ) ? sanitize_text_field( (string) $_POST['kind'] ) : '';

    // Tamanho máx genérico 50MB
    $max_size = 50 * 1024 * 1024;
    if ( (int) $file['size'] > $max_size ) {
        return rmbz_api_error( 'Ficheiro demasiado grande (máx 50MB).', 422, 'too_large' );
    }
    if ( (int) $file['size'] < 1024 ) {
        return rmbz_api_error( 'Ficheiro vazio ou corrupto.', 422, 'too_small' );
    }

    $allowed_mimes = [
        'jpg|jpeg' => 'image/jpeg',
        'png'      => 'image/png',
        'mp3'      => 'audio/mpeg',
        'wav'      => 'audio/wav',
    ];

    if ( ! function_exists( 'wp_check_filetype_and_ext' ) ) {
        require_once( ABSPATH . 'wp-admin/includes/file.php' );
    }

    $checked = wp_check_filetype_and_ext( $file['tmp_name'], $file['name'], $allowed_mimes );
    if ( empty( $checked['ext'] ) || empty( $checked['type'] ) ) {
        return rmbz_api_error( 'Tipo de ficheiro não permitido.', 422, 'invalid_type' );
    }

    // MIME real (defesa adicional)
    if ( function_exists( 'finfo_open' ) ) {
        $finfo = finfo_open( FILEINFO_MIME_TYPE );
        $real_mime = finfo_file( $finfo, $file['tmp_name'] );
        finfo_close( $finfo );
        $accepted_real = array_values( $allowed_mimes );
        $accepted_real[] = 'audio/x-wav';
        $accepted_real[] = 'audio/wave';
        if ( $real_mime && ! in_array( $real_mime, $accepted_real, true ) ) {
            return rmbz_api_error( 'Tipo real do ficheiro não corresponde.', 422, 'mime_mismatch' );
        }
    } else {
        $real_mime = null;
    }

    // [1.5.218] Validações específicas por kind
    if ( $kind === 'site_image' || $kind === 'social_image' ) {
        // IMAGEM — quadrada ±5%, 100KB-5MB
        $err = rmbz_api_validate_submission_image( $file, $real_mime );
        if ( $err ) return $err;
    } elseif ( $kind === 'site_audio' ) {
        // ÁUDIO SITE — apenas mp3 com bitrate ≥ 320kbps
        $err = rmbz_api_validate_site_audio( $file, $real_mime );
        if ( $err ) return $err;
    }

    // Renomear para evitar nomes maliciosos
    $ext = strtolower( pathinfo( $file['name'], PATHINFO_EXTENSION ) );
    $file['name'] = 'submission_' . wp_generate_password( 20, false, false ) . '.' . $ext;
    $_FILES['file'] = $file;

    if ( ! function_exists( 'wp_handle_upload' ) ) {
        require_once( ABSPATH . 'wp-admin/includes/file.php' );
    }
    $upload_overrides = [ 'test_form' => false, 'mimes' => $allowed_mimes ];
    $movefile = wp_handle_upload( $_FILES['file'], $upload_overrides );

    if ( ! $movefile || ! empty( $movefile['error'] ) || empty( $movefile['url'] ) ) {
        return rmbz_api_error(
            'Falha ao guardar ficheiro: ' . ( $movefile['error'] ?? 'erro desconhecido' ),
            500, 'save_failed'
        );
    }

    return rmbz_api_ok( [
        '_api_version' => RMBZ_API_SUBMISSIONS_VERSION,
        'url'          => esc_url_raw( $movefile['url'] ),
        'filename'     => basename( $movefile['file'] ),
        'mime'         => $movefile['type'] ?? null,
    ], 201 );
}

/**
 * [1.5.218] Valida imagem para submissão (site ou social).
 * Regras:
 *   - 100KB ≤ tamanho ≤ 5MB
 *   - Quadrada com tolerância ±5%
 *   - JPG ou PNG
 *
 * @return WP_REST_Response|null  null = OK, WP_REST_Response = erro
 */
function rmbz_api_validate_submission_image( $file, $real_mime ) {
    // Verificar que é imagem
    $is_image = false;
    if ( $real_mime && strpos( $real_mime, 'image/' ) === 0 ) $is_image = true;
    elseif ( ! $real_mime ) {
        // Fallback: usar getimagesize
        $info = @getimagesize( $file['tmp_name'] );
        $is_image = $info !== false;
    }
    if ( ! $is_image ) {
        return rmbz_api_error(
            'Apenas imagens (JPG ou PNG) são aceites neste campo.',
            422, 'not_image'
        );
    }

    // Tamanho
    $size = (int) $file['size'];
    $min_kb = 100 * 1024;
    $max_mb = 5 * 1024 * 1024;
    if ( $size < $min_kb ) {
        return rmbz_api_error(
            'A imagem é demasiado pequena (' . round( $size / 1024 ) . 'KB). Mínimo: 100KB.',
            422, 'image_too_small'
        );
    }
    if ( $size > $max_mb ) {
        return rmbz_api_error(
            'A imagem é demasiado grande (' . round( $size / 1024 / 1024, 1 ) . 'MB). Máximo: 5MB.',
            422, 'image_too_large'
        );
    }

    // Dimensões
    $dims = @getimagesize( $file['tmp_name'] );
    if ( $dims === false || empty( $dims[0] ) || empty( $dims[1] ) ) {
        return rmbz_api_error(
            'Não foi possível ler as dimensões da imagem.',
            422, 'image_decode_failed'
        );
    }
    $w = (int) $dims[0];
    $h = (int) $dims[1];
    $max_side = max( $w, $h );
    $min_side = min( $w, $h );
    if ( $max_side <= 0 ) {
        return rmbz_api_error( 'Imagem inválida.', 422, 'image_invalid' );
    }
    $diff = ( $max_side - $min_side ) / $max_side;
    if ( $diff > 0.05 ) {
        return rmbz_api_error(
            'A imagem deve ser quadrada (1:1). A actual é ' . $w . '×' . $h .
            '. Aceitamos pequenas diferenças até 5%.',
            422, 'image_not_square'
        );
    }

    return null;
}

/**
 * [1.5.218] Valida áudio de submissão para Site — apenas mp3 ≥ 320kbps.
 *
 * @return WP_REST_Response|null  null = OK, WP_REST_Response = erro
 */
function rmbz_api_validate_site_audio( $file, $real_mime ) {
    // Tem de ser mp3
    $ext = strtolower( pathinfo( $file['name'], PATHINFO_EXTENSION ) );
    if ( $ext !== 'mp3' ) {
        return rmbz_api_error(
            'Apenas ficheiros .mp3 são aceites para o Site (qualidade máxima exigida).',
            422, 'audio_not_mp3'
        );
    }

    // MIME real
    if ( $real_mime && $real_mime !== 'audio/mpeg' && $real_mime !== 'audio/mp3' ) {
        return rmbz_api_error(
            'O ficheiro não parece ser um mp3 válido (MIME: ' . $real_mime . ').',
            422, 'audio_not_mp3'
        );
    }

    // Bitrate via getID3 (lib do WP) — análise do header mp3.
    // Como o WordPress não inclui getID3 por defeito, vamos usar uma
    // estimativa rápida pelo header VBR/CBR. Se não conseguirmos detectar,
    // confiamos na validação do client (defesa em profundidade).
    $bitrate = rmbz_api_estimate_mp3_bitrate( $file['tmp_name'] );
    if ( $bitrate !== null && $bitrate < 320000 ) {
        return rmbz_api_error(
            'Qualidade insuficiente (' . round( $bitrate / 1000 ) . 'kbps). ' .
            'Para o Site exige-se ≥ 320kbps. Exporta o teu mp3 em qualidade máxima.',
            422, 'audio_low_quality'
        );
    }

    return null;
}

/**
 * [1.5.218] Estima o bitrate de um mp3 lendo o primeiro frame.
 *
 * Lê os primeiros KB do ficheiro, salta tag ID3v2 se presente,
 * encontra o primeiro frame mp3 e descodifica o bitrate do header.
 * Funciona para CBR. Para VBR devolve o bitrate do primeiro frame
 * (que é tipicamente similar à média).
 *
 * Retorna bitrate em bits/segundo ou null se não conseguir detectar.
 */
function rmbz_api_estimate_mp3_bitrate( $path ) {
    $fh = @fopen( $path, 'rb' );
    if ( ! $fh ) return null;

    $buf = fread( $fh, 16384 ); // 16 KB suficiente para skip ID3 + primeiro frame
    fclose( $fh );
    if ( ! $buf || strlen( $buf ) < 10 ) return null;

    $offset = 0;

    // Skip ID3v2 tag se presente
    if ( substr( $buf, 0, 3 ) === 'ID3' ) {
        // Tamanho do header é sync-safe int de 4 bytes a partir do offset 6
        if ( strlen( $buf ) < 10 ) return null;
        $b0 = ord( $buf[6] ) & 0x7F;
        $b1 = ord( $buf[7] ) & 0x7F;
        $b2 = ord( $buf[8] ) & 0x7F;
        $b3 = ord( $buf[9] ) & 0x7F;
        $tag_size = ( $b0 << 21 ) | ( $b1 << 14 ) | ( $b2 << 7 ) | $b3;
        $offset = 10 + $tag_size;
    }

    // Procurar frame sync (0xFFE) a partir do offset
    $len = strlen( $buf );
    for ( $i = $offset; $i < $len - 4; $i++ ) {
        $b0 = ord( $buf[ $i ] );
        $b1 = ord( $buf[ $i + 1 ] );
        if ( $b0 === 0xFF && ( $b1 & 0xE0 ) === 0xE0 ) {
            // Encontrei header de frame
            $version_id = ( $b1 >> 3 ) & 0x03;  // 11=MPEG1, 10=MPEG2, 00=MPEG2.5
            $layer      = ( $b1 >> 1 ) & 0x03;  // 01=Layer3
            $b2 = ord( $buf[ $i + 2 ] );
            $bitrate_idx = ( $b2 >> 4 ) & 0x0F;

            // Tabela bitrate para MPEG1 Layer3 (mais comum em mp3)
            // index 0=free, 1..14 valores, 15=bad
            $mpeg1_l3 = [
                0, 32000, 40000, 48000, 56000, 64000, 80000, 96000,
                112000, 128000, 160000, 192000, 224000, 256000, 320000, 0
            ];
            $mpeg2_l3 = [
                0, 8000, 16000, 24000, 32000, 40000, 48000, 56000,
                64000, 80000, 96000, 112000, 128000, 144000, 160000, 0
            ];

            $is_mpeg1 = ( $version_id === 3 ); // 11
            $is_layer3 = ( $layer === 1 );

            if ( ! $is_layer3 ) return null;
            if ( $bitrate_idx === 0 || $bitrate_idx === 15 ) return null;

            $table = $is_mpeg1 ? $mpeg1_l3 : $mpeg2_l3;
            return $table[ $bitrate_idx ];
        }
    }

    return null;
}
