<?php
/**
 * includes/notifications-center.php
 *
 * ============================================================================
 * CENTRO DE NOTIFICAÇÕES — 1.5.138
 * ============================================================================
 *
 * GATEWAY ÚNICO para todas as notificações ao utilizador. Substitui dezenas
 * de chamadas `wp_mail()` directas espalhadas pelo código.
 *
 * USO TÍPICO:
 *
 *   rmbz_notify( '+258840000000', 'welcome_pending', [
 *       'nome'   => 'João',
 *       'valor'  => '500',
 *       'metodo' => 'M-Pesa',
 *   ]);
 *
 * O que acontece nos bastidores:
 *   1. Lê o template para a key 'welcome_pending' de get_option('rmbz_email_templates')
 *   2. Substitui {nome}, {valor}, {metodo} no subject e no body
 *   3. Envia email para user_email do utilizador
 *   4. Grava em rmbz_email_logs (= aparece no sino 🔔 do utilizador)
 *   5. Loga sucesso/falha para o histórico admin
 *
 * TEMPLATES SUPORTADOS (chaves):
 *
 *   ONBOARDING / DEPÓSITOS
 *     welcome_pending     — Conta criada, depósito a aguardar aprovação
 *     deposit_approved    — Admin aprovou o 1º depósito
 *     deposit_rejected    — Admin rejeitou um depósito
 *
 *   PLANOS VIP / SUBSCRIÇÕES
 *     vip_purchased       — Plano VIP activado
 *     vip_expiring        — Subscrição expira em 3 dias
 *     vip_expired         — Subscrição expirou
 *
 *   CONVITES / REFERRALS
 *     referral_pending    — Alguém usou o teu número como convidador
 *     referral_completed  — Comissão paga (1º depósito + plano confirmados)
 *
 *   DISTRIBUIÇÃO
 *     royalties_credited  — Royalties USD creditados após import
 *     release_returned    — Release devolvida para correcção
 *     release_live        — Release publicada nas lojas
 *
 *   GENÉRICO
 *     custom              — Para quando o admin quer enviar mensagem livre
 *
 * PADRÃO DE NOMES DAS OPÇÕES:
 *   Cada template tem 2 chaves no array `rmbz_email_templates`:
 *     - subj_{key}  : assunto (texto curto)
 *     - body_{key}  : corpo (HTML/texto, pode ter <br> e tags básicas)
 *
 * VARIÁVEIS DISPONÍVEIS (todas opcionais conforme contexto):
 *   {nome_artista}      — User nome
 *   {site_name}         — Nome do site (auto)
 *   {valor}             — Quantia (auto-formatada se numérica)
 *   {moeda}             — MT/USD
 *   {metodo}            — M-Pesa, E-Mola, etc.
 *   {transaction_id}    — ID da transação
 *   {motivo}            — Motivo (rejeição, etc.)
 *   {plano}             — Nome do plano VIP
 *   {dias}              — Número de dias
 *   {data_expiracao}    — Data formatada
 *   {nome_amigo}        — Para referrals
 *   {tel_amigo}         — Telemóvel (parcialmente mascarado)
 *   {comissao}          — Valor da comissão
 *   {titulo_release}    — Para distribuição
 *   {motivo_rejeicao}   — Backward-compat (= {motivo})
 *   {titulo_musica}     — Backward-compat
 *   {genero_musica}     — Backward-compat
 */

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


/**
 * Lista canónica das keys de notificação suportadas pelo Centro 1.5.138.
 * Usada pelo email-manager para gerar dinamicamente os campos do admin.
 *
 * @return array
 */
function rmbz_notifications_keys_list() {
    return array_keys( rmbz_notifications_keys_meta() );
}


/**
 * Metadados (label, descrição, grupo, vars disponíveis) por key.
 * Usada pela UI admin para mostrar bonito.
 *
 * @return array
 */
function rmbz_notifications_keys_meta() {
    return [
        // ────────────────── ONBOARDING / DEPÓSITOS ──────────────────
        'welcome_pending' => [
            'group'  => 'Onboarding',
            'icon'   => '👋',
            'label'  => 'Boas-vindas (depósito a aguardar)',
            'desc'   => 'Enviado quando o utilizador acaba de criar conta e o depósito inicial está pendente.',
            'vars'   => ['nome_artista', 'valor', 'metodo', 'site_name'],
        ],
        'deposit_approved' => [
            'group'  => 'Onboarding',
            'icon'   => '✅',
            'label'  => 'Depósito aprovado',
            'desc'   => 'Enviado quando o admin aprova um depósito.',
            'vars'   => ['nome_artista', 'valor', 'site_name'],
        ],
        'deposit_rejected' => [
            'group'  => 'Onboarding',
            'icon'   => '❌',
            'label'  => 'Depósito rejeitado',
            'desc'   => 'Enviado quando o admin rejeita um depósito.',
            'vars'   => ['nome_artista', 'valor', 'motivo', 'site_name'],
        ],

        // ────────────────── PLANOS VIP ──────────────────
        'vip_purchased' => [
            'group'  => 'Planos VIP',
            'icon'   => '🎉',
            'label'  => 'Plano VIP comprado',
            'desc'   => 'Enviado quando o utilizador activa um plano VIP.',
            'vars'   => ['nome_artista', 'plano', 'dias', 'data_expiracao', 'site_name'],
        ],
        'vip_expiring' => [
            'group'  => 'Planos VIP',
            'icon'   => '⏰',
            'label'  => 'Subscrição expira em breve',
            'desc'   => 'Enviado 3 dias antes da subscrição expirar (via cron).',
            'vars'   => ['nome_artista', 'dias', 'data_expiracao', 'site_name'],
        ],
        'vip_expired' => [
            'group'  => 'Planos VIP',
            'icon'   => '🔒',
            'label'  => 'Subscrição expirou',
            'desc'   => 'Enviado quando a subscrição passa do prazo.',
            'vars'   => ['nome_artista', 'site_name'],
        ],
        'vip_expiring_project_fine' => [
            'group'  => 'Planos VIP',
            'icon'   => '🎬',
            'label'  => 'Subscrição expira c/ videoclipe em produção (multa)',
            'desc'   => 'Enviado (via cron) 3 dias antes da subscrição expirar quando o artista TEM um videoclipe em produção. Avisa a regra da multa por atraso.',
            'vars'   => ['nome_artista', 'dias', 'data_expiracao', 'musica', 'regra_multa', 'site_name'],
        ],
        'fine_reminder' => [
            'group'  => 'Planos VIP',
            'icon'   => '💸',
            'label'  => 'Lembrete diário: multa a acumular',
            'desc'   => 'Enviado (via cron, ~1×/dia) enquanto a subscrição com Ganhar Vídeo está expirada e a multa do videoclipe continua a acumular. Diz a dívida atual e pede para renovar.',
            'vars'   => ['nome_artista', 'musica', 'divida', 'taxa', 'site_name'],
        ],

        // ────────────────── TRANSFERÊNCIA DE PROJETO [1.5.398] ──────────────────
        'project_transfer_request' => [
            'group'  => 'Produção',
            'icon'   => '🔁',
            'label'  => 'Pedido de transferência de projeto',
            'desc'   => 'Enviado ao dono ATUAL quando a equipa pede a transferência do projeto para outra conta (modo "com aceitação"). O artista aceita/recusa no site.',
            'vars'   => ['nome_artista', 'musica', 'motivo', 'site_name'],
        ],
        'project_transfer_done_new' => [
            'group'  => 'Produção',
            'icon'   => '🎬',
            'label'  => 'Projeto transferido — novo dono',
            'desc'   => 'Enviado ao NOVO dono quando a transferência é concluída. Pede para assinar o contrato.',
            'vars'   => ['nome_artista', 'musica', 'site_name'],
        ],
        'project_transfer_done_old' => [
            'group'  => 'Produção',
            'icon'   => '🔁',
            'label'  => 'Projeto transferido — dono antigo (aceite)',
            'desc'   => 'Enviado ao dono ANTIGO quando a transferência que ele aceitou é concluída. (Na transferência forçada não é enviado nada ao antigo.)',
            'vars'   => ['nome_artista', 'musica', 'site_name'],
        ],
        'project_transfer_failed' => [
            'group'  => 'Produção',
            'icon'   => '🔁',
            'label'  => 'Transferência não foi possível / cancelada',
            'desc'   => 'Enviado ao dono atual quando um pedido de transferência (pendente ou já aceite) é cancelado pela equipa — o projeto continua na conta dele.',
            'vars'   => ['nome_artista', 'musica', 'site_name'],
        ],

        // ────────────────── DESISTÊNCIA DO PROJETO [1.5.442] ──────────────────
        'project_giveup_ask' => [
            'group'  => 'Produção',
            'icon'   => '🛑',
            'label'  => 'Desistência — pedido de decisão ao artista',
            'desc'   => 'Enviado ao artista quando a equipa propõe a desistência do projeto de vídeo e a decisão fica com ele (modal no Início com o conselho da IA).',
            'vars'   => ['nome_artista', 'musica', 'site_name'],
        ],
        'project_giveup_done' => [
            'group'  => 'Produção',
            'icon'   => '🛑',
            'label'  => 'Desistência — projeto cancelado',
            'desc'   => 'Enviado ao artista quando a desistência acontece (forçada pela equipa ou aceite por ele). {perda} descreve o valor perdido, se houver.',
            'vars'   => ['nome_artista', 'musica', 'motivo', 'perda', 'site_name'],
        ],

        // ────────────────── PARTICIPAÇÃO (FEAT) [1.5.401] ──────────────────
        'feat_invite' => [
            'group'  => 'Produção',
            'icon'   => '🎤',
            'label'  => 'Convite de participação (feat)',
            'desc'   => 'Enviado ao PARTICIPANTE quando o dono submete um Projecto de Vídeo com a participação dele. Ele aceita (com termos) ou recusa no site/app.',
            'vars'   => ['nome_artista', 'dono', 'musica', 'site_name'],
        ],
        'feat_accepted' => [
            'group'  => 'Produção',
            'icon'   => '🎤',
            'label'  => 'Participação aceite (para o dono)',
            'desc'   => 'Enviado ao DONO do projeto quando o participante aceita o convite — a produção segue normalmente.',
            'vars'   => ['nome_artista', 'participante', 'musica', 'site_name'],
        ],
        'feat_declined' => [
            'group'  => 'Produção',
            'icon'   => '❌',
            'label'  => 'Participação recusada — projeto cancelado (para o dono)',
            'desc'   => 'Enviado ao DONO quando o participante recusa: sem o participante não há como fazer o vídeo, por isso o projeto é cancelado automaticamente.',
            'vars'   => ['nome_artista', 'participante', 'musica', 'site_name'],
        ],

        // ────────────────── GRAVAÇÃO AO VIVO [1.5.404] ──────────────────
        'filming_live_started' => [
            'group'  => 'Produção',
            'icon'   => '🎬',
            'label'  => 'Filmagem começou (ao vivo)',
            'desc'   => 'Enviado ao artista quando a equipa carrega em INICIAR GRAVAÇÃO no dia da filmagem. O participante (feat) recebe push/sino do app.',
            'vars'   => ['nome_artista', 'musica', 'dia', 'site_name'],
        ],
        'filming_live_continues' => [
            'group'  => 'Produção',
            'icon'   => '🎬',
            'label'  => 'Filmagem do dia terminada — continua',
            'desc'   => 'Enviado ao artista quando a equipa finaliza o dia mas ainda falta filmar: diz quantos % faltam e o próximo dia.',
            'vars'   => ['nome_artista', 'musica', 'dia', 'falta', 'data', 'site_name'],
        ],
        'filming_live_wrapped' => [
            'group'  => 'Produção',
            'icon'   => '🎉',
            'label'  => 'Filmagem concluída (100%)',
            'desc'   => 'Enviado ao artista quando a equipa marca TUDO FILMADO — o projeto passa para edição.',
            'vars'   => ['nome_artista', 'musica', 'dias', 'site_name'],
        ],
        'vistoria_due' => [
            'group'  => 'Produção',
            'icon'   => '🔒',
            'label'  => 'Taxa de vistoria por pagar — projeto bloqueado',
            'desc'   => 'Enviado ao artista quando a filmagem termina e a taxa de vistoria (pessoas externas pedidas por ele) ainda não foi paga: o projeto fica bloqueado em edição até pagar. Com editor externo, avisa que os ficheiros não serão entregues ({editor_nota}).',
            'vars'   => ['nome_artista', 'musica', 'valor', 'editor_nota', 'site_name'],
        ],
        // [1.5.411] Fluxo de entrega ao EDITOR EXTERNO (depois da taxa paga)
        'editor_files_ready' => [
            'group'  => 'Produção',
            'icon'   => '📦',
            'label'  => 'Ficheiros prontos para o editor externo',
            'desc'   => 'Enviado ao artista quando o admin regista os ficheiros preparados para o editor externo (quantos são, GB e qualidade), depois de a taxa do editor ser paga.',
            'vars'   => ['nome_artista', 'musica', 'ficheiros', 'tamanho', 'qualidade', 'site_name'],
        ],
        'editor_files_delivered' => [
            'group'  => 'Produção',
            'icon'   => '🚚',
            'label'  => 'Ficheiros entregues ao editor externo',
            'desc'   => 'Enviado ao artista quando o admin marca a entrega: o editor já levou todos os ficheiros e a 1MZ só aguarda a edição para aprovar o vídeo final.',
            'vars'   => ['nome_artista', 'musica', 'ficheiros', 'tamanho', 'qualidade', 'site_name'],
        ],

        // ────────────────── PRODUÇÃO ──────────────────
        'filming_dates_set' => [
            'group'  => 'Produção',
            'icon'   => '📅',
            'label'  => 'Datas de gravação definidas',
            'desc'   => 'Enviado quando o admin define ou altera as datas de gravação de um projecto.',
            'vars'   => ['nome_artista', 'musica', 'data_inicio', 'data_fim', 'site_name'],
        ],
        'filming_ending_soon' => [
            'group'  => 'Produção',
            'icon'   => '⏳',
            'label'  => 'Filmagens terminam amanhã',
            'desc'   => 'Enviado (via cron) quando falta 1 dia para terminar a gravação.',
            'vars'   => ['nome_artista', 'musica', 'data_fim', 'site_name'],
        ],
        // [1.6.118.111] Aviso 1 dia antes do início sem confirmação
        'filming_confirm_warning' => [
            'group'  => 'Produção',
            'icon'   => '⚠️',
            'label'  => 'Falta confirmar — 1 dia para cancelar',
            'desc'   => 'Enviado (via cron) 1 dia antes do início das filmagens se o artista ainda não confirmou. Avisa que o projecto será cancelado.',
            'vars'   => ['nome_artista', 'musica', 'data_inicio', 'site_name'],
        ],
        // Aviso: escolher localização (2 dias após o admin enviar opções válidas)
        'location_choose_warning' => [
            'group'  => 'Produção',
            'icon'   => '📍',
            'label'  => 'Falta escolher localização — 1 dia para cancelar',
            'desc'   => 'Enviado quando resta 1 dia do prazo iniciado após o admin guardar opções válidas de localização.',
            'vars'   => ['nome_artista', 'musica', 'site_name'],
        ],
        'filming_cancelled' => [
            'group'  => 'Produção',
            'icon'   => '❌',
            'label'  => 'Projecto cancelado (sem confirmação)',
            'desc'   => 'Enviado quando o projecto é cancelado por falta de escolha da localização ou confirmação das datas. Inclui devolução do valor pago.',
            'vars'   => ['nome_artista', 'musica', 'cancel_reason', 'refund_line', 'site_name'],
        ],

        // ────────────────── CONVITES ──────────────────
        'referral_pending' => [
            'group'  => 'Convites',
            'icon'   => '👥',
            'label'  => 'Alguém usou-te como convidador',
            'desc'   => 'Enviado quando alguém se regista indicando este utilizador como convidador.',
            'vars'   => ['nome_artista', 'nome_amigo', 'tel_amigo', 'site_name'],
        ],
        'referral_completed' => [
            'group'  => 'Convites',
            'icon'   => '🎉',
            'label'  => 'Comissão de convite paga',
            'desc'   => 'Enviado quando o convidado cumpre 1º depósito + plano VIP, e a comissão é creditada.',
            'vars'   => ['nome_artista', 'nome_amigo', 'tel_amigo', 'comissao', 'site_name'],
        ],

        // ────────────────── DISTRIBUIÇÃO ──────────────────
        'royalties_credited' => [
            'group'  => 'Distribuição',
            'icon'   => '💵',
            'label'  => 'Royalties creditados',
            'desc'   => 'Enviado após import de relatórios das lojas, para utilizadores que receberam royalties.',
            'vars'   => ['nome_artista', 'valor', 'moeda', 'site_name'],
        ],
        'release_returned' => [
            'group'  => 'Distribuição',
            'icon'   => '⚠',
            'label'  => 'Release devolvido para correcção',
            'desc'   => 'Enviado quando admin marca release como returned com motivo.',
            'vars'   => ['nome_artista', 'titulo_release', 'motivo', 'site_name'],
        ],
        'release_live' => [
            'group'  => 'Distribuição',
            'icon'   => '🚀',
            'label'  => 'Release publicado nas lojas',
            'desc'   => 'Enviado quando release passa para status live/distribuído.',
            'vars'   => ['nome_artista', 'titulo_release', 'site_name'],
        ],

        // [1.5.144] Pedidos de alteração de lojas (DSPs)
        'store_change_approved' => [
            'group'  => 'Distribuição',
            'icon'   => '✅',
            'label'  => 'Pedido de lojas aprovado',
            'desc'   => 'Enviado quando o admin aprova um pedido de adição/remoção de lojas.',
            'vars'   => ['nome_artista', 'release_id', 'diff', 'fee', 'site_name'],
        ],
        'store_change_rejected' => [
            'group'  => 'Distribuição',
            'icon'   => '❌',
            'label'  => 'Pedido de lojas rejeitado',
            'desc'   => 'Enviado quando o admin rejeita um pedido de alteração de lojas.',
            'vars'   => ['nome_artista', 'release_id', 'motivo', 'site_name'],
        ],

        // [1.5.156] Cleanup automático de releases abandonadas
        'release_abandoned_warning' => [
            'group'  => 'Distribuição',
            'icon'   => '⏳',
            'label'  => 'Aviso: release vai ser apagada',
            'desc'   => 'Enviado 7 dias antes da release abandonada ser apagada (estado incomplete há muito tempo OU returned com prazo expirado).',
            'vars'   => ['nome_artista', 'titulo_release', 'release_id', 'motivo_estado', 'dias_restantes', 'site_name'],
        ],
        'release_abandoned_deleted' => [
            'group'  => 'Distribuição',
            'icon'   => '🗑',
            'label'  => 'Release apagada por inactividade',
            'desc'   => 'Confirmação enviada quando a release é efectivamente apagada após o período de aviso.',
            'vars'   => ['nome_artista', 'titulo_release', 'motivo_estado', 'site_name'],
        ],

        // [1.5.290] Primeiro relatório → desbloqueia o Assistente de IA
        'ai_unlocked' => [
            'group'  => 'Distribuição',
            'icon'   => '🤖',
            'label'  => 'Assistente de IA desbloqueado',
            'desc'   => 'Enviado uma vez, quando o artista recebe o seu primeiro relatório de royalties — desbloqueia o Assistente de IA.',
            'vars'   => ['nome_artista', 'site_name'],
        ],
    ];
}


/**
 * Devolve os defaults dos templates novos (1.5.138).
 * Os 4 originais (admin_new, artist_received, artist_approved, artist_rejected)
 * continuam a ser geridos pelo email-manager.php — não tocamos.
 */
function rmbz_notifications_default_templates() {
    return [
        // ────────────────── ONBOARDING / DEPÓSITOS ──────────────────
        'subj_welcome_pending' => 'Bem-vindo à 1MZ! Pagamento em análise',
        'body_welcome_pending' => "Olá {nome_artista},\n\n"
            . "A sua conta foi criada com sucesso na 1MZ For Artists. 🎉\n\n"
            . "📥 Depósito a aguardar confirmação:\n"
            . "• Valor: {valor} MT\n"
            . "• Método: {metodo}\n\n"
            . "A nossa equipa vai validar o pagamento dentro de 24 horas. "
            . "Assim que estiver aprovado, recebe outro email e pode aceder à sua área de artista.\n\n"
            . "Se já não fez a transferência, faça-a o mais rápido possível.\n\n"
            . "Equipa {site_name}",

        'subj_deposit_approved' => '✅ Depósito aprovado — saldo creditado',
        'body_deposit_approved' => "Olá {nome_artista},\n\n"
            . "O seu depósito de {valor} MT foi aprovado e creditado na sua carteira.\n\n"
            . "Já pode comprar um Plano VIP ou usar o saldo nos serviços disponíveis.\n\n"
            . "Equipa {site_name}",

        'subj_deposit_rejected' => '❌ Depósito não validado',
        'body_deposit_rejected' => "Olá {nome_artista},\n\n"
            . "Não conseguimos validar o seu depósito de {valor} MT.\n\n"
            . "Motivo: {motivo}\n\n"
            . "Por favor, contacte o nosso suporte se acreditar que houve um erro.\n\n"
            . "Equipa {site_name}",

        // ────────────────── VIP / SUBSCRIÇÕES ──────────────────
        'subj_vip_purchased' => '🎉 Plano {plano} activado',
        'body_vip_purchased' => "Olá {nome_artista},\n\n"
            . "O seu Plano {plano} foi activado com sucesso!\n\n"
            . "• Validade: {dias} dias\n"
            . "• Expira em: {data_expiracao}\n\n"
            . "Já pode aceder a todos os serviços incluídos no plano: distribuição nas lojas, "
            . "promoção, lançamentos e candidaturas a videoclipe (consoante o pacote).\n\n"
            . "Bom trabalho!\n\n"
            . "Equipa {site_name}",

        'subj_vip_expiring' => '⏰ A sua subscrição expira em {dias} dias',
        'body_vip_expiring' => "Olá {nome_artista},\n\n"
            . "A sua subscrição na 1MZ expira em {dias} dia(s) — a {data_expiracao}.\n\n"
            . "Para evitar interrupção dos serviços (distribuição, candidaturas, promoção), "
            . "renove o seu plano VIP a partir do painel.\n\n"
            . "Equipa {site_name}",

        'subj_vip_expiring_project_fine' => '🎬 Renova já — videoclipe "{musica}" em produção (expira em {dias} dias)',
        'body_vip_expiring_project_fine' => "Olá {nome_artista},\n\n"
            . "A tua subscrição na 1MZ expira em {dias} dia(s) — a {data_expiracao} — e tens "
            . "o videoclipe \"{musica}\" em produção.\n\n"
            . "⚠️ REGRA IMPORTANTE:\n"
            . "{regra_multa}\n\n"
            . "Para não parares a produção (nem acumular multa), renova o teu plano VIP a "
            . "partir do painel ou da app antes de {data_expiracao}.\n\n"
            . "Equipa {site_name}",

        'subj_fine_reminder' => '💸 Multa a acumular em "{musica}" — já {divida}',
        'body_fine_reminder' => "Olá {nome_artista},\n\n"
            . "A tua subscrição com o serviço Ganhar Vídeo expirou e o videoclipe \"{musica}\" "
            . "está em produção. A multa por atraso já vai em {divida} (a {taxa}) e continua a "
            . "crescer todos os dias.\n\n"
            . "👉 Renova já o teu VIP que inclui o Ganhar Vídeo para PARAR a multa e a equipa "
            . "retomar o trabalho. Depois podes pagar a dívida no teu projeto.\n\n"
            . "Equipa {site_name}",

        'subj_project_transfer_request' => '🔁 Pedido de transferência do projeto "{musica}"',
        'body_project_transfer_request' => "Olá {nome_artista},\n\n"
            . "A equipa {site_name} pediu para transferir o teu projeto de vídeo \"{musica}\" "
            . "para outra conta.{motivo}\n\n"
            . "👉 Entra na tua conta no site (secção dos projetos) para ACEITAR ou RECUSAR o pedido. "
            . "Enquanto não responderes, o projeto continua na tua conta.\n\n"
            . "Equipa {site_name}",

        'subj_project_transfer_done_new' => '🎬 O projeto "{musica}" é agora teu',
        'body_project_transfer_done_new' => "Olá {nome_artista},\n\n"
            . "O projeto de vídeo \"{musica}\" foi transferido para a tua conta pela equipa {site_name}.\n\n"
            . "👉 Entra na tua conta e revê o CONTRATO do projeto — precisas de o assinar (e validar o "
            . "teu documento) para a produção continuar.\n\n"
            . "Equipa {site_name}",

        'subj_project_transfer_done_old' => '🔁 Transferência do projeto "{musica}" concluída',
        'body_project_transfer_done_old' => "Olá {nome_artista},\n\n"
            . "Conforme aceitaste, o projeto \"{musica}\" foi transferido para outra conta. "
            . "Obrigado pela colaboração!\n\n"
            . "Equipa {site_name}",

        'subj_project_transfer_failed' => '🔁 Transferência do projeto "{musica}" não foi possível',
        'body_project_transfer_failed' => "Olá {nome_artista},\n\n"
            . "A transferência do teu projeto de vídeo \"{musica}\" NÃO foi concluída. "
            . "O projeto continua na tua conta e tudo fica como estava — não precisas de fazer nada.\n\n"
            . "Se tiveres dúvidas, fala com a equipa pelo chat do projeto.\n\n"
            . "Equipa {site_name}",

        'subj_project_giveup_ask' => 'Decisão pendente no projeto "{musica}"',
        'body_project_giveup_ask' => "Olá {nome_artista},\n\n"
            . "A equipa {site_name} propôs o encerramento do teu projeto de videoclipe \"{musica}\". "
            . "A decisão final é tua.\n\n"
            . "Entra na tua conta para veres o resumo completo do projeto (pagamentos, multas e valores de produção) "
            . "e um conselho do nosso assistente, antes de decidires continuar ou desistir.\n\n"
            . "Nota: de acordo com as regras do projeto, em caso de desistência o valor já pago não é devolvido.\n\n"
            . "Com os melhores cumprimentos,\nEquipa {site_name}",

        'subj_project_giveup_done' => 'O teu projeto "{musica}" foi encerrado',
        'body_project_giveup_done' => "Olá {nome_artista},\n\n"
            . "{motivo}{perda}\n\n"
            . "Se tiveres alguma dúvida, contacta a equipa através do suporte.\n\n"
            . "Com os melhores cumprimentos,\nEquipa {site_name}",

        'subj_feat_invite' => '🎤 Foste adicionado como participação em "{musica}"',
        'body_feat_invite' => "Olá {nome_artista},\n\n"
            . "{dono} adicionou-te como PARTICIPAÇÃO (feat) no Projecto de Vídeo \"{musica}\" "
            . "na plataforma {site_name}.\n\n"
            . "👉 Entra na tua conta (site ou app) para ACEITAR ou RECUSAR o convite. "
            . "Ao aceitar vais ver os Termos da Participação (imagem, gravações, pagamentos e regras). "
            . "⚠️ Se recusares, o projeto é automaticamente cancelado — sem o participante "
            . "não há como produzir o vídeo.\n\n"
            . "Equipa {site_name}",

        'subj_feat_accepted' => '🎤 {participante} aceitou a participação em "{musica}"',
        'body_feat_accepted' => "Olá {nome_artista},\n\n"
            . "Boas notícias: {participante} ACEITOU a participação no teu Projecto de Vídeo "
            . "\"{musica}\" e concordou com os Termos da Participação.\n\n"
            . "A produção segue normalmente — vais recebendo as novidades no projeto.\n\n"
            . "Equipa {site_name}",

        'subj_feat_declined' => '❌ Projeto "{musica}" cancelado — participação recusada',
        'body_feat_declined' => "Olá {nome_artista},\n\n"
            . "Infelizmente {participante} RECUSOU a participação no teu Projecto de Vídeo "
            . "\"{musica}\". Sem o participante não é possível produzir o vídeo, por isso o "
            . "projeto foi CANCELADO automaticamente.\n\n"
            . "👉 Podes submeter uma nova candidatura sem essa participação (ou com outro "
            . "participante) quando quiseres.\n\n"
            . "Equipa {site_name}",

        'subj_filming_live_started' => '🎬 A filmagem de "{musica}" COMEÇOU!',
        'body_filming_live_started' => "Olá {nome_artista},\n\n"
            . "Grandes notícias: a equipa {site_name} iniciou AGORA a gravação do teu "
            . "videoclipe \"{musica}\" (dia {dia} de filmagem). 🎬\n\n"
            . "Vais recebendo novidades por aqui à medida que o dia avança. Boa sorte!\n\n"
            . "Equipa {site_name}",

        'subj_filming_live_continues' => '🎬 Dia {dia} de filmagem terminado — "{musica}" continua a {data}',
        'body_filming_live_continues' => "Olá {nome_artista},\n\n"
            . "O dia {dia} de gravação do teu videoclipe \"{musica}\" terminou. "
            . "Ainda falta {falta}% para concluir as filmagens.\n\n"
            . "📅 A filmagem CONTINUA no dia {data} — mantém-te disponível e atento às "
            . "notificações.\n\n"
            . "Equipa {site_name}",

        'subj_vistoria_due' => '🔒 Projeto "{musica}" bloqueado — taxa de vistoria por pagar',
        'body_vistoria_due' => "Olá {nome_artista},\n\n"
            . "A filmagem do teu videoclipe \"{musica}\" TERMINOU — parabéns! Mas a taxa de "
            . "vistoria das pessoas externas que pediste ({valor}) ainda não foi paga.\n"
            . "{editor_nota}\n"
            . "🔒 Enquanto não for paga, o projeto fica BLOQUEADO: a equipa não pode editar o "
            . "vídeo nem entregar ficheiros, e não consegues acompanhar o estado da produção.\n\n"
            . "👉 Entra na tua conta, abre o projeto e faz o pagamento em falta para desbloquear "
            . "e a edição começar.\n\n"
            . "Equipa {site_name}",

        'subj_editor_files_ready' => '📦 Ficheiros de "{musica}" prontos para o editor',
        'body_editor_files_ready' => "Olá {nome_artista},\n\n"
            . "Boas notícias! A taxa do editor externo foi regularizada e a equipa já preparou "
            . "os ficheiros do teu videoclipe \"{musica}\" para entregar ao EDITOR EXTERNO:\n\n"
            . "🎞️ Ficheiros: {ficheiros}\n"
            . "💾 Tamanho: {tamanho}\n"
            . "✨ Qualidade: {qualidade}\n\n"
            . "Falta só a entrega ao editor — avisamos-te assim que ele levar os ficheiros.\n\n"
            . "Equipa {site_name}",

        'subj_editor_files_delivered' => '🚚 O editor já levou os ficheiros de "{musica}"',
        'body_editor_files_delivered' => "Olá {nome_artista},\n\n"
            . "O EDITOR EXTERNO já levou TODOS os ficheiros do teu videoclipe \"{musica}\":\n\n"
            . "🎞️ Ficheiros: {ficheiros}\n"
            . "💾 Tamanho: {tamanho}\n"
            . "✨ Qualidade: {qualidade}\n\n"
            . "A 1MZ agora só AGUARDA A EDIÇÃO para aprovar o vídeo final. Assim que o editor "
            . "entregar a versão editada, fazemos a revisão e recebes novidades.\n\n"
            . "Equipa {site_name}",

        'subj_filming_live_wrapped' => '🎉 Filmagem de "{musica}" 100% CONCLUÍDA!',
        'body_filming_live_wrapped' => "Olá {nome_artista},\n\n"
            . "A gravação do teu videoclipe \"{musica}\" está 100% concluída, em {dias} dia(s) "
            . "de filmagem! 🎉\n\n"
            . "O vídeo entra agora em EDIÇÃO — a equipa vai trabalhar na montagem e em breve "
            . "recebes novidades (e depois o vídeo final para veres em primeira mão).\n\n"
            . "Equipa {site_name}",

        'subj_vip_expired' => '🔒 Subscrição expirada',
        'body_vip_expired' => "Olá {nome_artista},\n\n"
            . "A sua subscrição expirou hoje. Os serviços VIP foram suspensos até renovar.\n\n"
            . "As suas músicas e dados continuam intactos — basta comprar um novo plano para "
            . "voltar a ter acesso completo.\n\n"
            . "Equipa {site_name}",

        // ────────────────── ASSISTENTE DE IA ──────────────────
        'subj_ai_unlocked' => '🤖 Desbloqueaste o Assistente de IA da 1MZ!',
        'body_ai_unlocked' => "Parabéns, {nome_artista}! 🎉\n\n"
            . "Acabaste de receber o teu primeiro relatório de royalties — e com ele "
            . "desbloqueaste o ASSISTENTE DE IA da 1MZ For Artists.\n\n"
            . "Ele analisa os teus números reais e ajuda-te a alavancar a tua carreira: "
            . "insights, dicas diárias, plano de lançamento, legendas para redes, kit de "
            . "divulgação e respostas às tuas dúvidas.\n\n"
            . "👉 Para usares o Assistente, adiciona o ADD-ON DO ASSISTENTE DE IA quando "
            . "ativares ou renovares o teu plano. Abre a app ou o teu painel e marca a "
            . "opção do Assistente de IA para começares a usar a IA hoje.\n\n"
            . "Vamos fazer a tua música crescer. 🚀\n\n"
            . "Equipa {site_name}",

        // ────────────────── PRODUÇÃO ──────────────────
        'subj_filming_dates_set' => '📅 Datas de gravação definidas para "{musica}"',
        'body_filming_dates_set' => "Olá {nome_artista},\n\n"
            . "A equipa definiu as datas de gravação do teu videoclipe \"{musica}\":\n\n"
            . "• Início: {data_inicio}\n"
            . "• Fim: {data_fim}\n\n"
            . "Abre a app ou o teu painel para CONFIRMAR estas datas ou PROPOR outras, "
            . "caso precises.\n\n"
            . "Prepara tudo o que ficaste de assumir na tua checklist e conta connosco "
            . "para o resto. Vai ser épico!\n\n"
            . "Equipa {site_name}",

        'subj_filming_ending_soon' => '⏳ A gravação de "{musica}" termina amanhã',
        'body_filming_ending_soon' => "Olá {nome_artista},\n\n"
            . "Lembrete: a gravação do teu videoclipe \"{musica}\" termina amanhã, "
            . "{data_fim}.\n\n"
            . "Garante que tens tudo pronto para o último dia de filmagens — material, "
            . "locais, e qualquer item da tua checklist.\n\n"
            . "Qualquer questão, fala connosco pelo chat do projecto.\n\n"
            . "Equipa {site_name}",

        // [1.6.118.111] Aviso urgente: confirmar ou perder o projecto
        'subj_filming_confirm_warning' => '⚠️ URGENTE: confirma "{musica}" ou o projecto será cancelado',
        'body_filming_confirm_warning' => "Olá {nome_artista},\n\n"
            . "As filmagens do teu videoclipe \"{musica}\" estão marcadas para começar a "
            . "{data_inicio}, mas ainda NÃO confirmaste as datas.\n\n"
            . "⚠️ Tens 1 DIA para confirmar. Se não confirmares até ao dia do início, "
            . "o projecto será CANCELADO e APAGADO de forma permanente — sem possibilidade "
            . "de recuperação.\n\n"
            . "Abre já a app ou o teu painel e confirma (ou propõe outras datas) para não "
            . "perderes o teu videoclipe.\n\n"
            . "Equipa {site_name}",

        // Aviso: escolher localização (2 dias após o admin enviar opções válidas)
        'subj_location_choose_warning' => '📍 Escolhe a localização de "{musica}" — resta 1 dia',
        'body_location_choose_warning' => "Olá {nome_artista},\n\n"
            . "O teu videoclipe \"{musica}\" foi aprovado, mas ainda NÃO escolheste a "
            . "localização de gravação.\n\n"
            . "⚠️ Tens 1 DIA para escolher. Se não escolheres a localização dentro do "
            . "prazo de 2 dias iniciado quando a equipa enviou as opções, o projecto será "
            . "CANCELADO e esta mesma música não poderá ser reenviada.\n\n"
            . "Abre a app ou o teu painel e escolhe já a localização para não perderes "
            . "o teu videoclipe.\n\n"
            . "Equipa {site_name}",

        'subj_filming_cancelled' => '❌ O teu projecto "{musica}" foi cancelado',
        'body_filming_cancelled' => "Olá {nome_artista},\n\n"
            . "O teu projecto de videoclipe \"{musica}\" foi cancelado porque {cancel_reason}.\n\n"
            . "{refund_line}\n\n"
            . "Esta mesma música não poderá ser reenviada; podes candidatar outras músicas.\n\n"
            . "Equipa {site_name}",


        // ────────────────── CONVITES / REFERRALS ──────────────────
        'subj_referral_pending' => '👥 Alguém indicou-o como convidador',
        'body_referral_pending' => "Olá {nome_artista},\n\n"
            . "Boas notícias: {nome_amigo} ({tel_amigo}) acabou de criar conta na 1MZ "
            . "indicando-o como convidador!\n\n"
            . "Quando esta pessoa fizer o 1º depósito aprovado E comprar um plano VIP, "
            . "recebe a sua recompensa automaticamente.\n\n"
            . "Pode acompanhar os seus convites no card 'Ganhe Saldo Extra' do seu painel.\n\n"
            . "Equipa {site_name}",

        'subj_referral_completed' => '🎉 Comissão de convite recebida!',
        'body_referral_completed' => "Olá {nome_artista},\n\n"
            . "Parabéns! O seu convidado {nome_amigo} ({tel_amigo}) acabou de cumprir todos "
            . "os requisitos:\n"
            . "✓ 1º depósito aprovado\n"
            . "✓ Plano VIP comprado\n\n"
            . "💰 Recompensa: {comissao}\n\n"
            . "Já está disponível na sua carteira (ou subscrição, se for o caso).\n\n"
            . "Continue a partilhar a 1MZ com mais amigos!\n\n"
            . "Equipa {site_name}",

        // ────────────────── DISTRIBUIÇÃO ──────────────────
        'subj_royalties_credited' => '💵 Royalties creditados — {valor} {moeda}',
        'body_royalties_credited' => "Olá {nome_artista},\n\n"
            . "Foram creditados {valor} {moeda} de royalties na sua conta de distribuição "
            . "após o último import de relatórios das lojas.\n\n"
            . "Pode ver o detalhe em 'Distribuição Digital → Analytics' no seu painel "
            . "ou solicitar levantamento.\n\n"
            . "Equipa {site_name}",

        'subj_release_returned' => '⚠ Release devolvido para correcção: {titulo_release}',
        'body_release_returned' => "Olá {nome_artista},\n\n"
            . "O seu release '{titulo_release}' foi devolvido pela equipa de validação "
            . "para correcção.\n\n"
            . "Motivo: {motivo}\n\n"
            . "Aceda ao painel de Distribuição Digital, abra o release e corrija os pontos "
            . "indicados. Depois pode submeter novamente para revisão.\n\n"
            . "Equipa {site_name}",

        'subj_release_live' => '🚀 Release publicado nas lojas: {titulo_release}',
        'body_release_live' => "Olá {nome_artista},\n\n"
            . "O seu release '{titulo_release}' acabou de ficar disponível nas lojas digitais "
            . "(Spotify, Apple Music, TikTok, etc.).\n\n"
            . "Pode demorar algumas horas a aparecer publicamente em todas as plataformas. "
            . "Os royalties começam a ser contabilizados a partir das primeiras streams.\n\n"
            . "Boa sorte com o lançamento! 🎶\n\n"
            . "Equipa {site_name}",

        // [1.5.144] Templates dos pedidos de alteração de lojas
        'subj_store_change_approved' => '✅ Alteração de lojas aprovada — Release #{release_id}',
        'body_store_change_approved' => "Olá {nome_artista},\n\n"
            . "O seu pedido de alteração de lojas para o Release #{release_id} foi APROVADO.\n\n"
            . "{diff}\n"
            . "Custo aplicado: {fee}\n\n"
            . "As alterações estão registadas no sistema. Pode levar algumas horas até ficarem reflectidas "
            . "nas lojas digitais.\n\n"
            . "Equipa {site_name}",

        'subj_store_change_rejected' => '❌ Pedido de alteração de lojas rejeitado',
        'body_store_change_rejected' => "Olá {nome_artista},\n\n"
            . "Infelizmente o seu pedido de alteração de lojas para o Release #{release_id} foi rejeitado.\n\n"
            . "Motivo: {motivo}\n\n"
            . "Pode submeter um novo pedido a qualquer momento ou contactar o suporte.\n\n"
            . "Equipa {site_name}",

        // [1.5.156] Templates de cleanup de releases abandonadas
        'subj_release_abandoned_warning' => '⏳ Release "{titulo_release}" será apagada em {dias_restantes} dias',
        'body_release_abandoned_warning' => "Olá {nome_artista},\n\n"
            . "O seu release '{titulo_release}' (ID #{release_id}) está {motivo_estado}.\n\n"
            . "Para evitar acumulação de dados antigos, este release será APAGADO automaticamente "
            . "em {dias_restantes} dias se não houver actividade.\n\n"
            . "Para manter o release:\n"
            . "  • Aceda ao painel de Distribuição Digital\n"
            . "  • Abra o release\n"
            . "  • Continue a edição (incompleto) OU envie correcções (devolvido)\n\n"
            . "Se já não tem interesse neste release, pode ignorar esta mensagem.\n\n"
            . "Equipa {site_name}",

        'subj_release_abandoned_deleted' => '🗑 Release "{titulo_release}" foi apagada por inactividade',
        'body_release_abandoned_deleted' => "Olá {nome_artista},\n\n"
            . "O seu release '{titulo_release}' foi apagado automaticamente do sistema "
            . "por inactividade ({motivo_estado}).\n\n"
            . "Caso ainda queira distribuir esta música, pode criar um novo release a partir "
            . "do painel de Distribuição Digital.\n\n"
            . "Equipa {site_name}",
    ];
}


/**
 * Devolve um template (subject ou body) para uma key.
 * Tenta primeiro o que está na option, depois cai no default.
 */
function rmbz_notifications_get_template( $key, $field = 'body' ) {
    $option_key = $field . '_' . $key; // ex: body_welcome_pending
    $tpl = get_option( 'rmbz_email_templates', [] );
    $defaults = rmbz_notifications_default_templates();

    if ( isset($tpl[$option_key]) && trim((string) $tpl[$option_key]) !== '' ) {
        $saved = (string) $tpl[$option_key];

        // Modelos antigos revelavam detalhes internos de retenção/limpeza e
        // assumiam sempre que o motivo eram as datas. Migrar automaticamente.
        if ( $option_key === 'body_filming_cancelled'
             && ( strpos( $saved, '{cancel_reason}' ) === false
                  || stripos( $saved, '24 horas' ) !== false
                  || stripos( $saved, '30 dias' ) !== false ) ) {
            return (string) ( $defaults[$option_key] ?? $saved );
        }
        if ( $option_key === 'body_location_choose_warning'
             && ( stripos( $saved, 'após a aprovação' ) !== false
                  || stripos( $saved, 'durante 24 horas' ) !== false ) ) {
            return (string) ( $defaults[$option_key] ?? $saved );
        }

        return $saved;
    }
    if ( isset($defaults[$option_key]) ) {
        return (string) $defaults[$option_key];
    }
    return '';
}


/**
 * Substitui {variáveis} no texto. Aceita arrays nestados — devolve string final.
 */
function rmbz_notifications_render( $template, $vars ) {
    if ( ! is_array($vars) ) $vars = [];

    // Variáveis automáticas que não precisam de ser passadas
    $vars['site_name'] = $vars['site_name'] ?? get_bloginfo('name');

    // Backward-compat aliases (templates antigos usavam estes nomes)
    if ( isset($vars['motivo']) && ! isset($vars['motivo_rejeicao']) ) {
        $vars['motivo_rejeicao'] = $vars['motivo'];
    }

    foreach ( $vars as $k => $v ) {
        $template = str_replace( '{' . $k . '}', (string) $v, $template );
    }
    return $template;
}


/**
 * Função PRINCIPAL. Envia notificação a um utilizador.
 *
 * @param string $user_tel  Telemóvel do utilizador (chave em rmbz_wallets)
 * @param string $key       Chave do template (ex: 'welcome_pending')
 * @param array  $vars      Variáveis a substituir nos placeholders {x}
 * @param array  $opts      Opções: ['skip_email'=>bool, 'skip_bell'=>bool, 'override_email'=>string]
 * @return bool             true se pelo menos uma das vias entregou
 */
function rmbz_notify( $user_tel, $key, $vars = [], $opts = [] ) {
    if ( empty($user_tel) || empty($key) ) return false;

    global $wpdb;

    // Resolver email do utilizador
    $email = '';
    $is_admin_target = ($user_tel === '__admin__');

    if ( ! empty($opts['override_email']) ) {
        $email = sanitize_email( $opts['override_email'] );
    } elseif ( ! $is_admin_target ) {
        $email = (string) $wpdb->get_var( $wpdb->prepare(
            "SELECT user_email FROM {$wpdb->prefix}rmbz_wallets WHERE user_telemovel = %s",
            $user_tel
        ) );
    }

    // [1.6.365] Email PLACEHOLDER (@1mz.local) = conta sem email real definido
    // pelo utilizador → NÃO enviar email (só mostra no sino). Antes "enviava"
    // para endereços falsos e o log marcava SUCESSO indevidamente.
    if ( $email !== '' && ( stripos( $email, '@1mz.local' ) !== false || ! is_email( $email ) ) ) {
        $email = '';
    }

    // Auto-popular nome se não veio
    if ( empty($vars['nome_artista']) && ! $is_admin_target ) {
        $vars['nome_artista'] = (string) $wpdb->get_var( $wpdb->prepare(
            "SELECT user_nome FROM {$wpdb->prefix}rmbz_wallets WHERE user_telemovel = %s",
            $user_tel
        ) ) ?: 'Artista';
    } elseif ( $is_admin_target && empty($vars['nome_artista']) ) {
        $vars['nome_artista'] = 'Equipa';
    }

    // Carregar templates
    $subj_raw = rmbz_notifications_get_template( $key, 'subj' );
    $body_raw = rmbz_notifications_get_template( $key, 'body' );

    if ( empty($subj_raw) && empty($body_raw) ) {
        // Template não existe — log silencioso, não falha
        if ( function_exists('error_log') ) {
            error_log( "[rmbz_notify] Template '$key' não encontrado." );
        }
        return false;
    }

    $subject = rmbz_notifications_render( $subj_raw, $vars );
    $body    = rmbz_notifications_render( $body_raw, $vars );

    // Body em HTML básico — converter \n para <br> para email HTML
    $body_html = nl2br( $body );

    // Headers do remetente vêm da config existente do email-manager
    $tpl_settings = (array) get_option( 'rmbz_email_templates', [] );
    $sender_name  = ! empty($tpl_settings['sender_name'])  ? $tpl_settings['sender_name']  : get_bloginfo('name');
    $sender_email = ! empty($tpl_settings['sender_email']) ? $tpl_settings['sender_email'] : get_option('admin_email');
    $headers = [
        'Content-Type: text/html; charset=UTF-8',
        'From: ' . $sender_name . ' <' . $sender_email . '>',
    ];

    $skip_email = ! empty($opts['skip_email']);
    $skip_bell  = ! empty($opts['skip_bell']);

    $email_ok = false;
    $bell_ok  = false;

    // 1) EMAIL — embrulha no template Material 3 Expressive (1.6.370).
    if ( ! $skip_email && ! empty($email) ) {
        $email_html = $body_html;
        if ( function_exists( 'rmbz_build_html_email' ) ) {
            $meta    = function_exists('rmbz_notifications_keys_meta') ? rmbz_notifications_keys_meta() : [];
            $icon    = isset($meta[$key]['icon'])  ? $meta[$key]['icon']  : '';
            $eyebrow = isset($meta[$key]['group']) ? $meta[$key]['group'] : 'Notificação';
            $email_html = rmbz_build_html_email( $body_html, $sender_name, [
                'emoji'   => $icon,
                'eyebrow' => $eyebrow,
            ] );
        }
        $email_ok = wp_mail( $email, $subject, $email_html, $headers );
    }

    // 2) SINO 🔔 — gravar em rmbz_email_logs (mesma tabela que o sino lê)
    if ( ! $skip_bell ) {
        $log_email = $email ?: ($user_tel . '@1mz.local');
        $insert_ok = $wpdb->insert(
            $wpdb->prefix . 'rmbz_email_logs',
            [
                'recipient_email' => $log_email,
                'subject'         => $subject,
                'body'            => $body_html,
                'sent_date'       => current_time('mysql'),
                'status'          => $email_ok ? 'sucesso' : 'so_sino',
                'is_read'         => 0,
                'is_cleared'      => 0,
            ],
            ['%s','%s','%s','%s','%s','%d','%d']
        );
        $bell_ok = ($insert_ok !== false);
    }

    return $email_ok || $bell_ok;
}


/**
 * Helper: notificação a admin (para alertas internos da equipa).
 */
function rmbz_notify_admin( $key, $vars = [], $opts = [] ) {
    $tpl_settings = (array) get_option( 'rmbz_email_templates', [] );
    $admin_email = ! empty($tpl_settings['admin_notify_email'])
        ? $tpl_settings['admin_notify_email']
        : get_option('admin_email');

    if ( empty($admin_email) ) return false;

    $opts['override_email'] = $admin_email;
    $opts['skip_bell']      = true; // sino é só do utilizador, não do admin

    // Para admin não há "user_tel" — usamos uma string-marker
    return rmbz_notify( '__admin__', $key, $vars, $opts );
}

/**
 * [1.6.365] REDE DE SEGURANÇA CENTRAL: nunca enviar email para endereços
 * placeholder (@1mz.local — contas sem email real). Cobre TODOS os `wp_mail()`
 * do plugin/site, mesmo os que não tenham guard próprio. Remove os destinatários
 * falsos; se não sobrar nenhum real, aborta o envio em silêncio.
 */
add_filter( 'pre_wp_mail', 'rmbz_block_placeholder_mail', 10, 2 );
function rmbz_block_placeholder_mail( $short_circuit, $atts ) {
    if ( $short_circuit !== null ) return $short_circuit; // outro filtro já decidiu
    $to = $atts['to'] ?? '';
    $recipients = is_array( $to ) ? $to : preg_split( '/[,;]\s*/', (string) $to );
    $real = array_filter( (array) $recipients, function ( $addr ) {
        return $addr !== '' && stripos( (string) $addr, '@1mz.local' ) === false;
    } );
    if ( empty( $real ) ) {
        // Todos os destinatários são placeholders → não enviar (sem erro).
        return true;
    }
    return $short_circuit; // null → segue o envio normal
}
