<?php
/**
 * 1MZ For Artists — Multa por atraso (projeto de vídeo em curso sem subscrição)
 *
 * REGRA: enquanto o artista tem um videoclipe EM PRODUÇÃO, a subscrição tem de
 * estar paga (a equipa não trabalha sem ela). Se a subscrição expirar com o
 * projeto em curso, o sistema conta automaticamente o tempo em atraso e gera
 * uma MULTA acumulada (valor × dias/horas), configurada no admin.
 *
 * [1.6.332] A multa é INFORMATIVA/acumulada — NÃO debita a carteira sozinha.
 * O admin decide quando/se cobra. Calculada dinamicamente (sem cron).
 *
 * @package 1MZ_For_Artists
 */

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

/** Valor da multa por unidade (MT). 0 = desligada. */
function rmbz_project_fine_value() {
    return max( 0.0, (float) get_option( 'rmbz_project_fine_value', 0 ) );
}

/** Unidade da multa: 'day' (por dia) ou 'hour' (por hora). */
function rmbz_project_fine_unit() {
    $u = get_option( 'rmbz_project_fine_unit', 'day' );
    return $u === 'hour' ? 'hour' : 'day';
}

/**
 * Estados em que a equipa já está comprometida com o projeto e a subscrição VIP
 * tem de estar ativa (geram multa por atraso se o VIP expirar).
 * [1.6.382] Conta DESDE APROVADO: Aprovado (pre-production), KYC, Filmagem e
 * Edição. "Em Avaliação" (antes da aprovação) NÃO gera multa.
 */
function rmbz_project_inprogress_states() {
    return [ 'pre-production', 'kyc_review', 'filming', 'editing' ];
}

/**
 * [1.6.386] O artista tem subscrição ATIVA que INCLUI o serviço Ganhar Vídeo
 * (permissão 'video')? Só este VIP pausa a multa — não basta um VIP qualquer.
 */
function rmbz_user_has_active_video_subscription( $telemovel ) {
    if ( ! function_exists( 'rmbz_user_has_active_subscription' )
        || ! rmbz_user_has_active_subscription( $telemovel ) ) {
        return false;
    }
    global $wpdb;
    $raw = $wpdb->get_var( $wpdb->prepare(
        "SELECT extra_data FROM {$wpdb->prefix}rmbz_wallets WHERE user_telemovel = %s",
        $telemovel
    ) );
    $extra = $raw ? json_decode( $raw, true ) : [];
    // Sem vip_permissions definidas (VIP legado) → assume acesso total (vídeo OK).
    // Só falha se houver permissões definidas E 'video' estiver explicitamente a 0.
    if ( ! is_array( $extra ) || ! isset( $extra['vip_permissions'] ) || ! is_array( $extra['vip_permissions'] ) ) {
        return true;
    }
    return ! empty( $extra['vip_permissions']['video'] );
}

/**
 * [1.6.392] O projeto tem as NOTIFICAÇÕES desativadas pelo admin?
 * (extra_data['notifications_muted'] = 1). Quando true, nenhuma mudança do
 * projeto deve notificar o artista (push/email/sino).
 */
function rmbz_project_notifications_muted( $project_id ) {
    global $wpdb;
    $raw = $wpdb->get_var( $wpdb->prepare(
        "SELECT extra_data FROM {$wpdb->prefix}rmbz_artist_projects WHERE id = %d",
        (int) $project_id
    ) );
    $extra = $raw ? json_decode( $raw, true ) : [];
    return is_array( $extra ) && ! empty( $extra['notifications_muted'] );
}

/** Projeto de vídeo EM CURSO do artista (linha) ou null. */
function rmbz_artist_active_video_project( $telemovel ) {
    global $wpdb;
    $t = $wpdb->prefix . 'rmbz_artist_projects';
    $states = rmbz_project_inprogress_states();
    $place  = implode( ',', array_fill( 0, count( $states ), '%s' ) );
    $args   = array_merge( [ $telemovel ], $states );
    $sql = "SELECT id, project_title, production_status FROM {$t}
             WHERE artist_telemovel = %s AND production_status IN ({$place})
             ORDER BY id DESC LIMIT 1";
    $row = $wpdb->get_row( $wpdb->prepare( $sql, $args ) );
    return $row ?: null;
}

/**
 * [1.6.380] Lê a multa PENDENTE (congelada/persistida) de um projeto.
 * Guardada em extra_data['pending_fine'] = {amount, units, unit, rate, since,
 * frozen_at, paid}. Devolve o array ou null.
 */
function rmbz_project_pending_fine_get( $project_id ) {
    global $wpdb;
    $raw = $wpdb->get_var( $wpdb->prepare(
        "SELECT extra_data FROM {$wpdb->prefix}rmbz_artist_projects WHERE id = %d",
        (int) $project_id
    ) );
    $extra = $raw ? json_decode( $raw, true ) : [];
    if ( ! is_array( $extra ) ) return null;
    return ( isset( $extra['pending_fine'] ) && is_array( $extra['pending_fine'] ) )
        ? $extra['pending_fine'] : null;
}

/** [1.6.380] Grava (ou remove, se $pf === null) a multa pendente do projeto. */
function rmbz_project_pending_fine_set( $project_id, $pf ) {
    global $wpdb;
    $raw = $wpdb->get_var( $wpdb->prepare(
        "SELECT extra_data FROM {$wpdb->prefix}rmbz_artist_projects WHERE id = %d",
        (int) $project_id
    ) );
    $extra = $raw ? json_decode( $raw, true ) : [];
    if ( ! is_array( $extra ) ) $extra = [];
    if ( $pf === null ) {
        unset( $extra['pending_fine'] );
    } else {
        $extra['pending_fine'] = $pf;
    }
    $wpdb->update(
        $wpdb->prefix . 'rmbz_artist_projects',
        [ 'extra_data' => wp_json_encode( $extra ) ],
        [ 'id' => (int) $project_id ],
        [ '%s' ], [ '%d' ]
    );
}

/** [1.6.383] Timestamp (Unix) até quando a multa já foi paga/limpa. 0 = nunca. */
function rmbz_project_fine_paid_until_ts( $project_id ) {
    global $wpdb;
    $raw = $wpdb->get_var( $wpdb->prepare(
        "SELECT extra_data FROM {$wpdb->prefix}rmbz_artist_projects WHERE id = %d",
        (int) $project_id
    ) );
    $extra = $raw ? json_decode( $raw, true ) : [];
    if ( ! is_array( $extra ) || empty( $extra['fine_paid_until'] ) ) return 0;
    $ts = strtotime( (string) $extra['fine_paid_until'] );
    return $ts ?: 0;
}

/**
 * [1.6.387] Momento (registado) em que o artista perdeu o acesso ao serviço
 * Ganhar Vídeo. Guardado em extra_data['video_access_lost_at']. 0 = não registado.
 */
function rmbz_project_video_lost_ts( $project_id ) {
    global $wpdb;
    $raw = $wpdb->get_var( $wpdb->prepare(
        "SELECT extra_data FROM {$wpdb->prefix}rmbz_artist_projects WHERE id = %d",
        (int) $project_id
    ) );
    $extra = $raw ? json_decode( $raw, true ) : [];
    if ( ! is_array( $extra ) || empty( $extra['video_access_lost_at'] ) ) return 0;
    $ts = strtotime( (string) $extra['video_access_lost_at'] );
    return $ts ?: 0;
}

/** [1.6.387] Grava (string mysql) ou remove (null) o momento de perda de vídeo. */
function rmbz_project_set_video_lost( $project_id, $mysql_or_null ) {
    global $wpdb;
    $raw = $wpdb->get_var( $wpdb->prepare(
        "SELECT extra_data FROM {$wpdb->prefix}rmbz_artist_projects WHERE id = %d",
        (int) $project_id
    ) );
    $extra = $raw ? json_decode( $raw, true ) : [];
    if ( ! is_array( $extra ) ) $extra = [];
    if ( $mysql_or_null === null ) {
        if ( ! isset( $extra['video_access_lost_at'] ) ) return; // nada a fazer
        unset( $extra['video_access_lost_at'] );
    } else {
        $extra['video_access_lost_at'] = (string) $mysql_or_null;
    }
    $wpdb->update(
        $wpdb->prefix . 'rmbz_artist_projects',
        [ 'extra_data' => wp_json_encode( $extra ) ],
        [ 'id' => (int) $project_id ],
        [ '%s' ], [ '%d' ]
    );
}

/**
 * [1.6.387] Calcula o momento EXATO em que o acesso a vídeo se perdeu:
 *   - Se a subscrição está expirada (fim no passado) → esse fim (exato).
 *   - Se há subscrição ativa SEM vídeo → o início dessa subscrição (≈ quando trocou).
 *   - Caso contrário → agora.
 * Devolve uma string mysql.
 */
function rmbz_compute_video_lost_baseline( $telemovel ) {
    $now_ts = current_time( 'timestamp' );
    $expiry = function_exists( 'rmbz_get_subscription_expiry' )
        ? rmbz_get_subscription_expiry( $telemovel ) : null;
    $expiry_ts = $expiry ? strtotime( $expiry ) : 0;
    if ( $expiry_ts && $expiry_ts < $now_ts ) {
        return date( 'Y-m-d H:i:s', $expiry_ts );
    }
    global $wpdb;
    $start = $wpdb->get_var( $wpdb->prepare(
        "SELECT start_date FROM {$wpdb->prefix}rmbz_subscriptions
          WHERE user_telemovel = %s AND status = 'active' ORDER BY end_date DESC LIMIT 1",
        $telemovel
    ) );
    if ( $start ) return (string) $start;
    return current_time( 'mysql' );
}

/**
 * [1.6.387] Timestamp a partir do qual a multa conta — registando o momento de
 * perda de vídeo uma única vez (persistente, nunca falha). Respeita também o
 * último pagamento (fine_paid_until).
 */
function rmbz_project_fine_start_ts( $telemovel, $project_id ) {
    $lost_ts = rmbz_project_video_lost_ts( $project_id );
    if ( ! $lost_ts ) {
        $baseline = rmbz_compute_video_lost_baseline( $telemovel );
        rmbz_project_set_video_lost( $project_id, $baseline );
        $lost_ts = strtotime( $baseline ) ?: current_time( 'timestamp' );
    }
    $paid_until = rmbz_project_fine_paid_until_ts( $project_id );
    return max( (int) $lost_ts, (int) $paid_until );
}

/**
 * [1.6.380] Marca a multa como PAGA/resolvida: limpa a dívida pendente E grava
 * fine_paid_until=agora, para o cálculo ao vivo NÃO voltar a cobrar o período já
 * pago (só conta a partir do momento do pagamento, se o VIP continuar expirado).
 */
function rmbz_project_clear_pending_fine( $project_id, $amount = null ) {
    global $wpdb;
    $raw = $wpdb->get_var( $wpdb->prepare(
        "SELECT extra_data FROM {$wpdb->prefix}rmbz_artist_projects WHERE id = %d",
        (int) $project_id
    ) );
    $extra = $raw ? json_decode( $raw, true ) : [];
    if ( ! is_array( $extra ) ) $extra = [];
    // [1.5.412] Registo das multas PAGAS (alimenta o subsídio da equipa no
    // Portal da Equipa). Sem valor explícito usa a dívida pendente gravada.
    $paid_amt = $amount !== null
        ? (float) $amount
        : (float) ( ( isset( $extra['pending_fine'] ) && is_array( $extra['pending_fine'] ) ) ? ( $extra['pending_fine']['amount'] ?? 0 ) : 0 );
    if ( $paid_amt > 0 ) {
        if ( ! isset( $extra['fine_paid_log'] ) || ! is_array( $extra['fine_paid_log'] ) ) $extra['fine_paid_log'] = [];
        $extra['fine_paid_log'][] = [ 'amount' => round( $paid_amt, 2 ), 'at' => current_time( 'mysql' ) ];
    }
    unset( $extra['pending_fine'] );
    $extra['fine_paid_until'] = current_time( 'mysql' );
    $wpdb->update(
        $wpdb->prefix . 'rmbz_artist_projects',
        [ 'extra_data' => wp_json_encode( $extra ) ],
        [ 'id' => (int) $project_id ],
        [ '%s' ], [ '%d' ]
    );
}

/**
 * [1.6.380] Congela a multa acumulada ATÉ AGORA no projeto em curso do artista.
 * Chamado quando o artista renova o VIP (ANTES de estender a subscrição, ou seja,
 * com o VIP ainda expirado) — para a multa não desaparecer ao ficar VIP ativo.
 * High-water: nunca reduz o valor já devido.
 */
function rmbz_project_freeze_pending_fine( $telemovel ) {
    $project = rmbz_artist_active_video_project( $telemovel );
    if ( ! $project ) return;

    $rate = rmbz_project_fine_value();
    if ( $rate <= 0 ) return; // multa desligada — nada a congelar

    // [1.6.387] Mesmo baseline do cálculo ao vivo (momento de perda de vídeo +
    // último pagamento) — coerência entre o valor mostrado e o congelado.
    $now_ts   = current_time( 'timestamp' );
    $start_ts = rmbz_project_fine_start_ts( $telemovel, $project->id );
    if ( $start_ts >= $now_ts ) return; // não há período por cobrar

    $unit  = rmbz_project_fine_unit();
    $secs  = max( 0, $now_ts - $start_ts );
    $units = ( $unit === 'hour' ) ? (int) floor( $secs / 3600 ) : (int) floor( $secs / 86400 );
    $live  = round( $rate * $units, 2 );
    if ( $live <= 0 ) return;

    $existing = rmbz_project_pending_fine_get( $project->id );
    $existing_amt = ( $existing && empty( $existing['paid'] ) ) ? (float) ( $existing['amount'] ?? 0 ) : 0.0;
    // [1.5.410] Mesma regra do cálculo ao vivo: dívida de um lapso ANTERIOR
    // (since diferente) é CARRY e soma-se; no mesmo lapso vale o high-water.
    $existing_since_ts = ( $existing && ! empty( $existing['since'] ) )
        ? ( strtotime( (string) $existing['since'] ) ?: 0 ) : 0;
    $same_lapse = $existing_since_ts > 0 && abs( $existing_since_ts - $start_ts ) <= 120;
    $carry = 0.0;
    if ( $existing_amt > 0 ) {
        $carry = $same_lapse ? (float) ( $existing['carry'] ?? 0 ) : $existing_amt;
    }
    $amount = max( $existing_amt, round( $carry + $live, 2 ) );

    rmbz_project_pending_fine_set( $project->id, [
        'amount'    => $amount,
        'carry'     => $carry,
        'units'     => $units,
        'unit'      => $unit,
        'rate'      => $rate,
        'since'     => date( 'Y-m-d H:i:s', $start_ts ),
        'frozen_at' => current_time( 'mysql' ),
        'paid'      => 0,
    ] );
}

/**
 * Informação da multa por atraso. Devolve sempre um array.
 *
 * [1.6.380] A multa passa a PERSISTIR: enquanto o VIP está expirado acumula ao
 * vivo (e grava o high-water); quando o artista renova o VIP, a multa NÃO
 * desaparece — fica como dívida PENDENTE ('frozen'=true) até o admin a marcar
 * como paga. Campos extra: 'frozen' (não está a crescer), 'pending' (há dívida).
 */
function rmbz_project_overdue_info( $telemovel ) {
    $unit = rmbz_project_fine_unit();
    $rate = rmbz_project_fine_value();
    $none = [
        'overdue'  => false,
        'frozen'   => false,
        'pending'  => false,
        'amount'   => 0.0,
        'units'    => 0,
        'unit'     => $unit,
        'rate'     => $rate,
        'currency' => 'MT',
        'enabled'  => $rate > 0,
    ];

    $project = rmbz_artist_active_video_project( $telemovel );
    if ( ! $project ) return $none;

    $pending = rmbz_project_pending_fine_get( $project->id );
    $pending_unpaid = ( $pending && empty( $pending['paid'] ) && (float) ( $pending['amount'] ?? 0 ) > 0 )
        ? (float) $pending['amount'] : 0.0;

    $frozen_payload = function ( $project, $pending, $pending_unpaid, $unit, $rate ) {
        return [
            'overdue'       => true,
            'frozen'        => true,
            'pending'       => true,
            'project_id'    => (int) $project->id,
            'project_title' => (string) ( $project->project_title ?: '' ),
            'since'         => (string) ( $pending['since'] ?? '' ),
            'unit'          => (string) ( $pending['unit'] ?? $unit ),
            'units'         => (int) ( $pending['units'] ?? 0 ),
            'rate'          => (float) ( $pending['rate'] ?? $rate ),
            'amount'        => $pending_unpaid,
            'currency'      => 'MT',
            'enabled'       => true,
        ];
    };

    // [1.6.386] Só pausa a multa um VIP que INCLUI o serviço Ganhar Vídeo —
    // não basta um VIP qualquer (ex.: um plano só de Site/Redes não conta).
    $vip_active = rmbz_user_has_active_video_subscription( $telemovel );

    if ( $vip_active ) {
        // VIP (com vídeo) ativo: o acesso a vídeo está OK → limpa o marcador de
        // perda (para um novo lapso futuro recomeçar do momento certo). A multa
        // não cresce; se há dívida por pagar, devolve-a congelada.
        rmbz_project_set_video_lost( $project->id, null );
        if ( $pending_unpaid > 0 ) {
            return $frozen_payload( $project, $pending, $pending_unpaid, $unit, $rate );
        }
        return $none;
    }

    // [1.6.387] Sem VIP com vídeo → a multa acumula desde o momento (registado)
    // em que o acesso a vídeo se perdeu. Funciona tanto quando a subscrição
    // expirou de todo como quando há um VIP ativo SEM o serviço Ganhar Vídeo.
    $now_ts   = current_time( 'timestamp' );
    $start_ts = rmbz_project_fine_start_ts( $telemovel, $project->id );
    if ( $start_ts >= $now_ts ) {
        if ( $pending_unpaid > 0 ) {
            return $frozen_payload( $project, $pending, $pending_unpaid, $unit, $rate );
        }
        return $none;
    }

    $secs  = max( 0, $now_ts - $start_ts );
    $units = ( $unit === 'hour' ) ? (int) floor( $secs / 3600 ) : (int) floor( $secs / 86400 );
    $live  = round( $rate * $units, 2 );

    // [1.5.410] BUGFIX "a multa parou de contar": quando a dívida persistida
    // pertence a um LAPSO ANTERIOR (o artista renovou e voltou a expirar → o
    // `since` mudou), ela é CARRY (dívida antiga) e SOMA-SE à contagem nova.
    // Antes fazia-se max(dívida antiga, contagem nova) — o total ficava preso
    // no valor antigo (ex.: 1.740 MT) até a contagem nova o ultrapassar
    // (dias!), parecendo parado. No MESMO lapso o high-water continua certo.
    $pending_since_ts = ( $pending && ! empty( $pending['since'] ) )
        ? ( strtotime( (string) $pending['since'] ) ?: 0 ) : 0;
    $same_lapse = $pending_since_ts > 0 && abs( $pending_since_ts - $start_ts ) <= 120;
    $carry = 0.0;
    if ( $pending_unpaid > 0 ) {
        $carry = $same_lapse ? (float) ( $pending['carry'] ?? 0 ) : $pending_unpaid;
    }
    $amount = max( $pending_unpaid, round( $carry + $live, 2 ) );

    // Persistir high-water (só escreve quando aumenta — máx 1×/unidade).
    if ( $rate > 0 && $amount > $pending_unpaid ) {
        rmbz_project_pending_fine_set( $project->id, [
            'amount'    => $amount,
            'carry'     => $carry,
            'units'     => $units,
            'unit'      => $unit,
            'rate'      => $rate,
            'since'     => date( 'Y-m-d H:i:s', $start_ts ),
            'frozen_at' => current_time( 'mysql' ),
            'paid'      => 0,
        ] );
    }

    return [
        'overdue'       => true,
        'frozen'        => false,
        'pending'       => $pending_unpaid > 0,
        'project_id'    => (int) $project->id,
        'project_title' => (string) ( $project->project_title ?: '' ),
        'since'         => date( 'Y-m-d H:i:s', $start_ts ),
        'unit'          => $unit,
        'units'         => $units,
        'rate'          => $rate,
        'amount'        => $amount,
        // Dívida herdada de lapsos anteriores (já incluída em amount).
        'carry'         => $carry,
        'currency'      => 'MT',
        'enabled'       => $rate > 0,
    ];
}

/**
 * [1.5.410] DIAGNÓSTICO da multa — explica, porta a porta, porque é que a
 * multa está (ou não) a contar para um artista/projeto. Para o admin ver
 * "porque parou de contar" sem adivinhar.
 *
 * @return array lista de linhas [ 'ok'=>bool|null, 'label'=>string ]
 */
function rmbz_project_fine_diagnose( $telemovel, $viewing_project_id = 0 ) {
    $out = [];
    $rate = rmbz_project_fine_value();
    $unit = rmbz_project_fine_unit();
    $out[] = [ 'ok' => $rate > 0, 'label' => sprintf( 'Taxa configurada: %s MT/%s %s', number_format( $rate, 2, ',', '.' ), $unit === 'hour' ? 'hora' : 'dia', $rate > 0 ? '' : '— MULTA DESLIGADA (define a taxa nas opções)' ) ];

    $project = rmbz_artist_active_video_project( $telemovel );
    if ( ! $project ) {
        $out[] = [ 'ok' => false, 'label' => 'Nenhum projeto EM CURSO (pre-production/kyc_review/filming/editing) — sem projeto ativo não há multa.' ];
        return $out;
    }
    $out[] = [ 'ok' => true, 'label' => sprintf( 'Projeto em curso escolhido pelo motor: #%d "%s" (%s)', (int) $project->id, (string) $project->project_title, (string) $project->production_status ) ];
    if ( $viewing_project_id && (int) $project->id !== (int) $viewing_project_id ) {
        $out[] = [ 'ok' => false, 'label' => sprintf( '⚠️ A multa está LIGADA ao projeto #%d (o mais recente em curso do artista) — NÃO a este (#%d). Se criaste um projeto de teste novo, a multa "mudou" para lá.', (int) $project->id, (int) $viewing_project_id ) ];
    }

    $vip = rmbz_user_has_active_video_subscription( $telemovel );
    $expiry = function_exists( 'rmbz_get_subscription_expiry' ) ? rmbz_get_subscription_expiry( $telemovel ) : null;
    $out[] = [ 'ok' => ! $vip, 'label' => $vip
        ? 'VIP com Ganhar Vídeo ATIVO (expira: ' . ( $expiry ?: '—' ) . ') — a multa está PAUSADA (correto).'
        : 'Sem VIP com Ganhar Vídeo (expirou: ' . ( $expiry ?: 'nunca teve' ) . ') — a multa deve contar.' ];

    $lost_ts = rmbz_project_video_lost_ts( $project->id );
    $out[] = [ 'ok' => null, 'label' => 'Perda de acesso registada (video_access_lost_at): ' . ( $lost_ts ? date( 'Y-m-d H:i:s', $lost_ts ) : '— (será registada no próximo cálculo)' ) ];

    $paid_until = rmbz_project_fine_paid_until_ts( $project->id );
    if ( $paid_until ) {
        $out[] = [ 'ok' => null, 'label' => 'Multa marcada PAGA até: ' . date( 'Y-m-d H:i:s', $paid_until ) . ' — só volta a contar a partir daqui (se pagaste/limpaste há pouco, o contador recomeça do ZERO e demora até 1 ' . ( $unit === 'hour' ? 'hora' : 'dia' ) . ' a mostrar valor).' ];
    }

    $info = rmbz_project_overdue_info( $telemovel );
    $out[] = [ 'ok' => ! empty( $info['overdue'] ), 'label' => sprintf(
        'Resultado do motor agora: %s · %d %s · %s MT%s',
        ! empty( $info['overdue'] ) ? ( ! empty( $info['frozen'] ) ? 'DÍVIDA CONGELADA (VIP renovado, falta pagar)' : 'A CONTAR' ) : 'SEM MULTA',
        (int) ( $info['units'] ?? 0 ),
        ( ( $info['unit'] ?? 'day' ) === 'hour' ? 'hora(s)' : 'dia(s)' ),
        number_format( (float) ( $info['amount'] ?? 0 ), 2, ',', '.' ),
        ! empty( $info['since'] ) ? ' · desde ' . $info['since'] : ''
    ) ];
    return $out;
}

/**
 * [1.6.390] Modal de pagamento da multa (site) — pede o PIN e debita o saldo.
 * Renderizado UMA vez por página (guard estático). Abre via JS: o botão "Pagar
 * multa" faz document.getElementById('rmbz-fine-pay').style.display='flex'.
 */
function rmbz_render_fine_pay_modal( $amount ) {
    static $done = false;
    if ( $done ) return;
    $done = true;
    $amt = number_format( (float) $amount, 2, ',', '.' );
    ?>
    <div id="rmbz-fine-pay" style="display:none;position:fixed;inset:0;z-index:100061;align-items:center;justify-content:center;background:rgba(15,18,25,.6);padding:16px;" onclick="if(event.target===this)this.style.display='none';">
        <div style="background:#fff;border-radius:20px;max-width:400px;width:100%;padding:24px;box-shadow:0 24px 70px rgba(0,0,0,.35);">
            <h3 style="margin:0 0 8px;font-size:18px;font-weight:800;color:#1a1a1f;">💳 Pagar multa</h3>
            <p style="margin:0 0 16px;font-size:14px;line-height:1.5;color:#3a3a44;">Vão ser debitados <strong><?php echo esc_html( $amt ); ?> MT</strong> do teu saldo para pagar a multa por atraso. Confirma com o teu PIN.</p>
            <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
                <input type="hidden" name="action" value="rmbz_pay_project_fine">
                <?php wp_nonce_field( 'rmbz_pay_fine_nonce' ); ?>
                <input type="password" name="pin" inputmode="numeric" maxlength="6" placeholder="PIN" required style="width:100%;box-sizing:border-box;padding:11px 14px;border-radius:10px;border:1px solid #d8dae0;font-size:16px;margin-bottom:14px;">
                <div style="display:flex;gap:8px;justify-content:flex-end;">
                    <button type="button" onclick="document.getElementById('rmbz-fine-pay').style.display='none';" style="background:#f1f3f5;color:#5f6368;border:0;border-radius:10px;padding:10px 16px;font-size:13px;font-weight:700;cursor:pointer;">Cancelar</button>
                    <button type="submit" style="background:#1a8a4a;color:#fff;border:0;border-radius:10px;padding:10px 18px;font-size:13px;font-weight:700;cursor:pointer;">Pagar <?php echo esc_html( $amt ); ?> MT</button>
                </div>
            </form>
        </div>
    </div>
    <?php
}

/**
 * [1.6.385] Modal "Saiba mais" da multa/VIP (site). Renderizado UMA vez por
 * página (guard estático). Explica que a multa não desaparece ao renovar, que é
 * preciso o VIP com o serviço Ganhar Vídeo, e para nunca o deixar expirar com um
 * projeto ativo.
 */
function rmbz_render_fine_info_modal() {
    static $done = false;
    if ( $done ) return;
    $done = true;
    ?>
    <div id="rmbz-fine-info" style="display:none;position:fixed;inset:0;z-index:100060;align-items:center;justify-content:center;background:rgba(15,18,25,.6);padding:16px;" onclick="if(event.target===this)this.style.display='none';">
        <div style="background:#fff;border-radius:20px;max-width:480px;width:100%;padding:24px 24px 18px;box-shadow:0 24px 70px rgba(0,0,0,.35);">
            <h3 style="margin:0 0 14px;font-size:18px;font-weight:800;color:#1a1a1f;">🎬 Sobre a multa e o teu VIP</h3>
            <div style="display:flex;flex-direction:column;gap:12px;font-size:14px;line-height:1.55;color:#3a3a44;">
                <div>💸 <strong>A multa não desaparece ao renovar.</strong> Mesmo que reactives o VIP, a multa acumulada fica como dívida e terás de a pagar.</div>
                <div>⭐ <strong>Precisas do VIP que inclui o serviço Ganhar Vídeo.</strong> Só esse plano deixa a equipa continuar a produção do teu videoclipe.</div>
                <div>⏰ <strong>Nunca deixes esse VIP expirar</strong> enquanto tens um projeto de vídeo ativo — é assim que evitas multas e a paragem da equipa.</div>
            </div>
            <div style="text-align:right;margin-top:18px;">
                <button type="button" onclick="document.getElementById('rmbz-fine-info').style.display='none';" style="background:#1a1a1f;color:#fff;border:0;border-radius:10px;padding:9px 20px;font-size:13px;font-weight:700;cursor:pointer;">Entendi</button>
            </div>
        </div>
    </div>
    <?php
}

/**
 * [1.6.370] Texto da REGRA da multa (independente do utilizador) — para avisos
 * antes da subscrição expirar. Usa a config atual do admin.
 */
function rmbz_project_fine_rule_text() {
    $rate = rmbz_project_fine_value();
    $unit = rmbz_project_fine_unit();
    if ( $rate > 0 ) {
        return sprintf(
            'Enquanto tens um videoclipe em produção, a subscrição tem de estar paga — a equipa não trabalha sem ela. Se a subscrição expirar com o projeto em curso, começa a contar uma multa de %s MT por %s até renovares.',
            number_format( $rate, 2, ',', '.' ),
            $unit === 'hour' ? 'hora' : 'dia'
        );
    }
    return 'Enquanto tens um videoclipe em produção, a subscrição tem de estar paga — a equipa não trabalha sem ela. Se expirar com o projeto em curso, a produção fica suspensa até renovares.';
}

/**
 * [1.6.380] Handler admin — marca a multa pendente de um projeto como PAGA
 * (limpa a dívida, libertando a equipa para continuar).
 */
add_action( 'admin_post_rmbz_clear_project_fine', 'rmbz_admin_handle_clear_project_fine' );
function rmbz_admin_handle_clear_project_fine() {
    if ( ! current_user_can( 'manage_options' ) ) wp_die( 'Sem permissão.' );
    $pid = isset( $_GET['project_id'] ) ? absint( $_GET['project_id'] ) : 0;
    check_admin_referer( 'rmbz_clear_fine_' . $pid );
    if ( $pid <= 0 ) wp_die( 'Projeto inválido.' );

    rmbz_project_clear_pending_fine( $pid );

    if ( function_exists( 'rmbz_pchat_append_change_log' ) ) {
        rmbz_pchat_append_change_log( $pid, [
            'who'  => function_exists( 'rmbz_pchat_current_admin_who' ) ? rmbz_pchat_current_admin_who() : 'admin',
            'what' => '💸 Marcou a multa por atraso como PAGA (dívida limpa)',
        ] );
    }

    wp_safe_redirect( admin_url( 'admin.php?page=rmbz-projetos&action=edit&id=' . $pid . '&fine_cleared=1' ) );
    exit;
}

/**
 * [1.6.383] O ARTISTA paga a multa por atraso com o saldo da carteira.
 * Valida o PIN, debita o valor da multa e limpa a dívida. Devolve um array
 * { success, message, paid, balance } ou { success:false, code, message }.
 */
function rmbz_project_pay_fine_from_wallet( $telemovel, $pin ) {
    $telemovel = (string) $telemovel;
    if ( $telemovel === '' ) {
        return [ 'success' => false, 'code' => 'auth_required', 'message' => 'Sessão inválida.' ];
    }

    $info = rmbz_project_overdue_info( $telemovel );
    if ( empty( $info['overdue'] ) || (float) ( $info['amount'] ?? 0 ) <= 0 ) {
        return [ 'success' => false, 'code' => 'no_fine', 'message' => 'Não há nenhuma multa por pagar neste momento.' ];
    }
    $amount     = round( (float) $info['amount'], 2 );
    $project_id = (int) ( $info['project_id'] ?? 0 );
    if ( $project_id <= 0 ) {
        return [ 'success' => false, 'code' => 'no_project', 'message' => 'Projeto da multa não encontrado.' ];
    }

    // Validar PIN (mesma estratégia das outras operações da carteira).
    if ( ! function_exists( 'rmbz_verify_user_pin' ) || ! rmbz_verify_user_pin( $telemovel, (string) $pin ) ) {
        return [ 'success' => false, 'code' => 'wrong_pin', 'message' => 'PIN incorreto.' ];
    }

    if ( ! function_exists( 'rmbz_update_wallet_balance' ) ) {
        return [ 'success' => false, 'code' => 'wallet_fn_missing', 'message' => 'Sistema indisponível.' ];
    }

    // Saldo suficiente?
    $balance = function_exists( 'rmbz_get_wallet_balance_by_phone' )
        ? (float) rmbz_get_wallet_balance_by_phone( $telemovel ) : 0.0;
    if ( $balance < $amount ) {
        return [
            'success' => false, 'code' => 'insufficient_balance',
            'message' => sprintf(
                'Saldo insuficiente. A multa é %s MT e tens %s MT. Faz um depósito primeiro.',
                number_format( $amount, 2, ',', '.' ),
                number_format( $balance, 2, ',', '.' )
            ),
        ];
    }

    // Debitar o valor da multa.
    $details = 'Pagamento de multa por atraso (videoclipe)';
    $debit = rmbz_update_wallet_balance( $telemovel, -$amount, 'project_fine', $project_id, $details );
    if ( empty( $debit['success'] ) ) {
        return [
            'success' => false, 'code' => 'debit_failed',
            'message' => 'Erro ao debitar: ' . ( $debit['error'] ?? 'desconhecido' ),
        ];
    }

    // Limpar a dívida (marca fine_paid_until para não recobrar) e registar
    // o valor pago (subsídio da equipa).
    rmbz_project_clear_pending_fine( $project_id, $amount );

    // Registar no histórico do projeto.
    if ( function_exists( 'rmbz_pchat_append_change_log' ) ) {
        rmbz_pchat_append_change_log( $project_id, [
            'who'  => 'artista',
            'what' => '💸 Artista pagou a multa por atraso (' . number_format( $amount, 2, ',', '.' ) . ' MT)',
        ] );
    }

    $new_balance = function_exists( 'rmbz_get_wallet_balance_by_phone' )
        ? (float) rmbz_get_wallet_balance_by_phone( $telemovel ) : max( 0.0, $balance - $amount );

    return [
        'success' => true,
        'message' => 'Multa paga com sucesso. A equipa pode continuar.',
        'paid'    => $amount,
        'balance' => $new_balance,
    ];
}

/** Texto curto pt-PT da multa (para banners no site/admin). */
function rmbz_project_fine_text( array $info ) {
    if ( empty( $info['overdue'] ) ) return '';
    $unit_lbl = $info['unit'] === 'hour' ? 'hora(s)' : 'dia(s)';
    if ( empty( $info['enabled'] ) || $info['rate'] <= 0 ) {
        return sprintf(
            'Subscrição expirada há %d %s com um videoclipe em produção. Renova para a equipa continuar.',
            (int) $info['units'], $unit_lbl
        );
    }
    return sprintf(
        'Subscrição expirada há %d %s com um videoclipe em produção. Multa acumulada: %s MT (%s MT/%s). Renova para parar a multa e a equipa continuar.',
        (int) $info['units'], $unit_lbl,
        number_format( (float) $info['amount'], 2, ',', '.' ),
        number_format( (float) $info['rate'], 2, ',', '.' ),
        $info['unit'] === 'hour' ? 'hora' : 'dia'
    );
}
