<?php
/**
 * includes/admin/wallet-mt-cleanup.php
 *
 * ============================================================================
 * DETECÇÃO E CORREÇÃO DE CRÉDITOS USD-TRATADOS-COMO-MT (1.5.110)
 * ============================================================================
 *
 * Antes da 1.5.110, três fluxos creditavam a wallet MT (rmbz_wallets) com
 * valores em USD sem conversão:
 *   1. approve_import (royalty_payout, marker [royalty_import:HASH])
 *   2. match_review approve (royalty_payout, marker [match_review:ID])
 *   3. revert_import passo 6.5 (royalty_revert, marker [revert:HASH])
 *
 * Estes créditos/débitos ficaram registados em rmbz_wallet_transactions com
 * markers que permitem detectá-los com SQL LIKE. Este módulo:
 *
 *   - Lista artistas afectados com totais agregados
 *   - Calcula a diferença entre o que ficou na wallet (USD-tratado-como-MT)
 *     e o que devia ter ficado (zero, porque royalties não devem entrar
 *     directamente na wallet MT)
 *   - Permite ao admin reverter caso a caso, criando um débito manual
 *     com explicação clara em rmbz_wallet_transactions
 *
 * Filosofia: NÃO automatiza em massa. Cada artista é um caso. O admin
 * decide se o débito é justo (artista pode já ter feito levantamentos
 * com esse dinheiro, então debitar agora cria saldo negativo).
 */

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


/**
 * Lista artistas com créditos errados na wallet MT.
 * Filtra wallet_transactions por markers conhecidos.
 *
 * Devolve array de objectos com:
 *   - user_telemovel
 *   - total_credited_wrong: soma USD-tratado-como-MT que entrou errado
 *   - total_debited_wrong: soma debitada via revert (negativa)
 *   - net_wrong: o "lixo" actual a corrigir
 *   - tx_count: número de transações afectadas
 *   - already_corrected: se já foi corrigido por este módulo (marker [mtfix:])
 */
function rmbz_wallet_mt_list_affected() {
    global $wpdb;
    $t_trans = $wpdb->prefix . 'rmbz_wallet_transactions';

    if ( $wpdb->get_var("SHOW TABLES LIKE '$t_trans'") !== $t_trans ) {
        return [];
    }

    // Soma todos os créditos errados (royalty_payout com marker import/match)
    $rows = $wpdb->get_results(
        "SELECT
            user_telemovel,
            SUM(CASE
                WHEN type = 'royalty_payout'
                  AND ( details LIKE '%[royalty_import:%' OR details LIKE '%[match_review:%' )
                THEN amount ELSE 0 END
            ) AS total_credited_wrong,
            SUM(CASE
                WHEN type = 'royalty_revert'
                  AND details LIKE '%[revert:%'
                THEN amount ELSE 0 END
            ) AS total_debited_wrong,
            SUM(CASE
                WHEN type = 'mt_fix_correction'
                  AND details LIKE '%[mtfix:%'
                THEN amount ELSE 0 END
            ) AS already_corrected_amount,
            COUNT(CASE
                WHEN ( type = 'royalty_payout' AND ( details LIKE '%[royalty_import:%' OR details LIKE '%[match_review:%' ) )
                  OR ( type = 'royalty_revert' AND details LIKE '%[revert:%' )
                THEN 1 END
            ) AS tx_count
         FROM $t_trans
         GROUP BY user_telemovel
         HAVING tx_count > 0
         ORDER BY ABS(total_credited_wrong) DESC"
    );

    foreach ( $rows as $r ) {
        $r->total_credited_wrong = (float) $r->total_credited_wrong;
        $r->total_debited_wrong  = (float) $r->total_debited_wrong;
        $r->already_corrected_amount = (float) $r->already_corrected_amount;
        // Net "lixo" = créditos + débitos (débitos são negativos) + correções já aplicadas
        $r->net_wrong = $r->total_credited_wrong + $r->total_debited_wrong + $r->already_corrected_amount;
        $r->tx_count = (int) $r->tx_count;
    }

    return $rows;
}


/**
 * Aplica uma correção a um artista específico.
 * Cria uma linha em wallet_transactions com type='mt_fix_correction' e
 * marker [mtfix:TIMESTAMP] que sinaliza ao módulo que já corrigiu.
 *
 * @param string $telemovel
 * @param float  $amount_to_debit  Valor positivo (a função negativa internamente)
 * @param string $note             Nota livre do admin
 * @return array { success: bool, message: string }
 */
function rmbz_wallet_mt_apply_correction( $telemovel, $amount_to_debit, $note = '' ) {
    global $wpdb;

    if ( empty($telemovel) ) {
        return ['success' => false, 'message' => 'Telemovel inválido.'];
    }
    if ( $amount_to_debit <= 0 ) {
        return ['success' => false, 'message' => 'Valor a debitar deve ser positivo.'];
    }

    $t_wallets = $wpdb->prefix . 'rmbz_wallets';
    $t_trans   = $wpdb->prefix . 'rmbz_wallet_transactions';

    // Lock por wallet para evitar race
    $lock_name = 'rmbz_wallet_' . md5((string) $telemovel);
    $got = (int) $wpdb->get_var( $wpdb->prepare("SELECT GET_LOCK(%s, 5)", $lock_name) );
    if ( ! $got ) {
        return ['success' => false, 'message' => 'Sistema ocupado, tenta de novo.'];
    }

    try {
        $current = $wpdb->get_var( $wpdb->prepare(
            "SELECT balance FROM $t_wallets WHERE user_telemovel = %s",
            $telemovel
        ) );
        if ( $current === null ) {
            $wpdb->query( $wpdb->prepare("SELECT RELEASE_LOCK(%s)", $lock_name) );
            return ['success' => false, 'message' => 'Carteira não encontrada para ' . $telemovel];
        }

        $current = (float) $current;
        $new_balance = $current - $amount_to_debit;

        // Update wallet (permite saldo negativo — admin sabe o que faz)
        $now = current_time('mysql');
        $upd_ok = $wpdb->update(
            $t_wallets,
            [ 'balance' => $new_balance ],
            [ 'user_telemovel' => $telemovel ],
            [ '%f' ], [ '%s' ]
        );
        if ( $upd_ok === false ) {
            $wpdb->query( $wpdb->prepare("SELECT RELEASE_LOCK(%s)", $lock_name) );
            return ['success' => false, 'message' => 'Falha ao actualizar wallet: ' . $wpdb->last_error];
        }

        // Registar a correção
        $admin = wp_get_current_user();
        $admin_login = ($admin && $admin->exists()) ? $admin->user_login : 'admin';
        $marker = '[mtfix:' . time() . ']';
        $details = sprintf(
            'Correção 1.5.110 — débito de crédito USD-tratado-como-MT por %s. Saldo anterior: %.2f MT. Saldo novo: %.2f MT. %s %s',
            $admin_login,
            $current,
            $new_balance,
            $note ? 'Nota: ' . $note : '',
            $marker
        );

        $wpdb->insert(
            $t_trans,
            [
                'user_telemovel'   => $telemovel,
                'amount'           => -$amount_to_debit,
                'type'             => 'mt_fix_correction',
                'details'          => $details,
                'transaction_date' => $now,
                'is_unlocked'      => 1,
            ],
            [ '%s', '%f', '%s', '%s', '%s', '%d' ]
        );

        $wpdb->query( $wpdb->prepare("SELECT RELEASE_LOCK(%s)", $lock_name) );

        return [
            'success'     => true,
            'message'     => sprintf( '✓ Debitado %.2f MT de %s. Saldo: %.2f → %.2f MT.', $amount_to_debit, $telemovel, $current, $new_balance ),
            'old_balance' => $current,
            'new_balance' => $new_balance,
        ];
    } catch ( Exception $e ) {
        $wpdb->query( $wpdb->prepare("SELECT RELEASE_LOCK(%s)", $lock_name) );
        return ['success' => false, 'message' => 'Erro: ' . $e->getMessage()];
    }
}


/**
 * Devolve detalhes das transações afectadas de um artista — usado para
 * mostrar ao admin antes de aplicar a correção.
 *
 * [1.5.147] Adicionada paginação opcional. Default mantém retro-compat
 * (LIMIT 100, oferta=0 → primeiras 100). Para a UI nova, passar $page
 * com $per_page para navegar pelo histórico completo.
 *
 * @param string $telemovel
 * @param int    $page      Página 1-indexed (default 1)
 * @param int    $per_page  Por página (default 100)
 * @return array            Lista de objectos transaction
 */
function rmbz_wallet_mt_get_artist_details( $telemovel, $page = 1, $per_page = 100 ) {
    global $wpdb;
    $t_trans = $wpdb->prefix . 'rmbz_wallet_transactions';

    $page     = max( 1, (int) $page );
    $per_page = max( 10, min( 1000, (int) $per_page ) );
    $offset   = ($page - 1) * $per_page;

    return $wpdb->get_results( $wpdb->prepare(
        "SELECT transaction_id, amount, type, details, transaction_date
         FROM $t_trans
         WHERE user_telemovel = %s
           AND (
                ( type = 'royalty_payout' AND ( details LIKE '%%[royalty_import:%%' OR details LIKE '%%[match_review:%%' ) )
             OR ( type = 'royalty_revert' AND details LIKE '%%[revert:%%' )
             OR ( type = 'mt_fix_correction' AND details LIKE '%%[mtfix:%%' )
           )
         ORDER BY transaction_date DESC
         LIMIT %d OFFSET %d",
        $telemovel, $per_page, $offset
    ) );
}


/**
 * [1.5.147] Conta total de transações afectadas para paginação.
 *
 * @param string $telemovel
 * @return int
 */
function rmbz_wallet_mt_count_artist_details( $telemovel ) {
    global $wpdb;
    $t_trans = $wpdb->prefix . 'rmbz_wallet_transactions';

    return (int) $wpdb->get_var( $wpdb->prepare(
        "SELECT COUNT(*)
         FROM $t_trans
         WHERE user_telemovel = %s
           AND (
                ( type = 'royalty_payout' AND ( details LIKE '%%[royalty_import:%%' OR details LIKE '%%[match_review:%%' ) )
             OR ( type = 'royalty_revert' AND details LIKE '%%[revert:%%' )
             OR ( type = 'mt_fix_correction' AND details LIKE '%%[mtfix:%%' )
           )",
        $telemovel
    ) );
}


/**
 * [1.5.147] PREVIEW do impacto de uma correção, sem executar.
 *
 * Devolve TODOS os dados que o admin precisa para decidir antes de
 * carregar Debitar. Cobre cenários edge case críticos:
 *   - Saldo actual baixo → débito vai criar negativo
 *   - Artista pode não ter saldo suficiente → quanto fica
 *   - Sugere "débito seguro" = min(net_wrong, saldo_actual)
 *
 * @param string $telemovel
 * @param float  $proposed_debit  Quanto o admin pretende debitar
 * @return array {
 *     current_balance, proposed_debit, new_balance, will_go_negative,
 *     safe_debit_max, severity, recommendation
 * }
 */
function rmbz_wallet_mt_preview_impact( $telemovel, $proposed_debit ) {
    global $wpdb;
    $t_wallets = $wpdb->prefix . 'rmbz_wallets';

    $current_balance = (float) $wpdb->get_var( $wpdb->prepare(
        "SELECT balance FROM $t_wallets WHERE user_telemovel = %s",
        $telemovel
    ) );

    $proposed_debit = max( 0, (float) $proposed_debit );
    $new_balance    = $current_balance - $proposed_debit;
    $will_negative  = ( $new_balance < 0 );
    $safe_max       = max( 0, $current_balance ); // Nunca debitar mais que o saldo

    // Severidade visual:
    //   safe       — saldo positivo após débito, não há problema
    //   warning    — saldo > 0 mas vai ficar < 100 MT (margem fina)
    //   critical   — saldo vai ficar negativo
    if ( $will_negative ) {
        $severity = 'critical';
        $rec = sprintf(
            'Esta correção vai criar saldo NEGATIVO de %.2f MT. O artista terá de cobrir antes de poder levantar. '
            . 'Considera o "Débito Seguro" (%.2f MT) que zera a wallet sem ficar negativa.',
            abs($new_balance), $safe_max
        );
    } elseif ( $new_balance < 100 ) {
        $severity = 'warning';
        $rec = 'Saldo após débito fica baixo (' . number_format($new_balance, 2) . ' MT). Verifica se faz sentido.';
    } else {
        $severity = 'safe';
        $rec = 'Débito seguro — saldo permanece positivo.';
    }

    return [
        'current_balance'  => $current_balance,
        'proposed_debit'   => $proposed_debit,
        'new_balance'      => $new_balance,
        'will_go_negative' => $will_negative,
        'safe_debit_max'   => $safe_max,
        'severity'         => $severity,
        'recommendation'   => $rec,
    ];
}


/**
 * [1.5.147] Devolve histórico de correções já aplicadas a um artista.
 * Cada linha é uma transação com type='mt_fix_correction' (gravada por
 * rmbz_wallet_mt_apply_correction). Para auditoria + possibilidade de
 * anular individualmente.
 *
 * @param string $telemovel
 * @return array
 */
function rmbz_wallet_mt_get_corrections_history( $telemovel ) {
    global $wpdb;
    $t_trans = $wpdb->prefix . 'rmbz_wallet_transactions';

    return $wpdb->get_results( $wpdb->prepare(
        "SELECT transaction_id, amount, details, transaction_date
         FROM $t_trans
         WHERE user_telemovel = %s
           AND type = 'mt_fix_correction'
           AND details LIKE '%%[mtfix:%%'
         ORDER BY transaction_date DESC",
        $telemovel
    ) );
}


/**
 * [1.5.147] Anula uma correção previamente aplicada. Equivale a fazer
 * um crédito do mesmo valor que foi debitado, com type 'mt_fix_undo'
 * para distinguir.
 *
 * Útil quando admin executou correção errada (ex: debitou demais).
 *
 * @param int $transaction_id  ID da correção a anular
 * @return array { success, message }
 */
function rmbz_wallet_mt_undo_correction( $transaction_id ) {
    global $wpdb;
    $transaction_id = (int) $transaction_id;
    if ( $transaction_id <= 0 ) {
        return [ 'success' => false, 'message' => 'ID de transação inválido.' ];
    }

    $t_trans   = $wpdb->prefix . 'rmbz_wallet_transactions';
    $t_wallets = $wpdb->prefix . 'rmbz_wallets';

    // Buscar a correção original
    $orig = $wpdb->get_row( $wpdb->prepare(
        "SELECT * FROM $t_trans
         WHERE transaction_id = %d AND type = 'mt_fix_correction'",
        $transaction_id
    ) );
    if ( ! $orig ) {
        return [ 'success' => false, 'message' => 'Correção não encontrada ou já anulada.' ];
    }

    // Verificar se já foi anulada (procura uma mt_fix_undo subsequente
    // que aponte para esta via marker [undo:transaction_id])
    $already_undone = (int) $wpdb->get_var( $wpdb->prepare(
        "SELECT COUNT(*) FROM $t_trans
         WHERE type = 'mt_fix_undo'
           AND details LIKE %s",
        '%[undo:' . $transaction_id . ']%'
    ) );
    if ( $already_undone > 0 ) {
        return [ 'success' => false, 'message' => 'Esta correção já foi anulada.' ];
    }

    $tel = $orig->user_telemovel;
    $debited_amount = (float) $orig->amount; // negativo (foi débito)
    $credit_back    = -$debited_amount;       // positivo (devolver)

    // Lock anti-race
    $lock_name = 'rmbz_wallet_' . md5((string) $tel);
    $got = (int) $wpdb->get_var( $wpdb->prepare("SELECT GET_LOCK(%s, 5)", $lock_name) );
    if ( ! $got ) {
        return [ 'success' => false, 'message' => 'Sistema ocupado, tenta de novo.' ];
    }

    try {
        $current = (float) $wpdb->get_var( $wpdb->prepare(
            "SELECT balance FROM $t_wallets WHERE user_telemovel = %s",
            $tel
        ) );
        $new_balance = $current + $credit_back;

        $wpdb->update(
            $t_wallets,
            [ 'balance' => $new_balance ],
            [ 'user_telemovel' => $tel ],
            [ '%f' ], [ '%s' ]
        );

        $admin = wp_get_current_user();
        $admin_login = ($admin && $admin->exists()) ? $admin->user_login : 'admin';
        $details = sprintf(
            'Anulação da correção #%d por %s. Devolveu %.2f MT à wallet. Saldo: %.2f → %.2f MT. [undo:%d]',
            $transaction_id, $admin_login, $credit_back, $current, $new_balance, $transaction_id
        );

        $wpdb->insert(
            $t_trans,
            [
                'user_telemovel'   => $tel,
                'amount'           => $credit_back,
                'type'             => 'mt_fix_undo',
                'details'          => $details,
                'transaction_date' => current_time('mysql'),
                'is_unlocked'      => 1,
            ],
            [ '%s', '%f', '%s', '%s', '%s', '%d' ]
        );

        $wpdb->query( $wpdb->prepare("SELECT RELEASE_LOCK(%s)", $lock_name) );

        return [
            'success'     => true,
            'message'     => sprintf( '✓ Anulada correção #%d. Devolveu %.2f MT. Saldo: %.2f → %.2f MT.', $transaction_id, $credit_back, $current, $new_balance ),
            'old_balance' => $current,
            'new_balance' => $new_balance,
        ];
    } catch ( Exception $e ) {
        $wpdb->query( $wpdb->prepare("SELECT RELEASE_LOCK(%s)", $lock_name) );
        return [ 'success' => false, 'message' => 'Erro: ' . $e->getMessage() ];
    }
}


/**
 * [1.5.147] AJAX endpoint para preview live do impacto.
 * Usado pelo input de "valor a debitar" — quando admin escreve, esta
 * função devolve o novo saldo + severidade sem fazer nada na BD.
 */
function rmbz_wallet_mt_ajax_preview() {
    if ( ! current_user_can('manage_options') ) {
        wp_send_json_error( [ 'message' => 'Sem permissão.' ] );
    }
    if ( ! check_ajax_referer( 'rmbz_wallet_mt_preview_nonce', 'nonce', false ) ) {
        wp_send_json_error( [ 'message' => 'Erro de segurança.' ] );
    }

    $tel = sanitize_text_field( $_POST['telemovel'] ?? '' );
    $amount = (float) ( $_POST['amount'] ?? 0 );

    if ( empty($tel) ) {
        wp_send_json_error( [ 'message' => 'Telemovel inválido.' ] );
    }

    $preview = rmbz_wallet_mt_preview_impact( $tel, $amount );
    wp_send_json_success( $preview );
}
add_action( 'wp_ajax_rmbz_wallet_mt_preview', 'rmbz_wallet_mt_ajax_preview' );



/**
 * [DS 1.5.113] Devolve análise estatística agregada das transações
 * afectadas, para validar antes de carregar Debitar.
 *
 * Detecta:
 *   - Composição (imports vs matches vs reverts)
 *   - Range de datas
 *   - Duplicados exactos (mesmo amount + mesmo details)
 *   - Markers únicos vs total (se 1 import gerou 50 transações iguais é red flag)
 */
function rmbz_wallet_mt_artist_breakdown( $telemovel ) {
    global $wpdb;
    $t_trans = $wpdb->prefix . 'rmbz_wallet_transactions';

    // 1) Composição por tipo
    $by_type = $wpdb->get_results( $wpdb->prepare(
        "SELECT
            CASE
                WHEN type = 'royalty_payout' AND details LIKE '%%[royalty_import:%%' THEN 'import'
                WHEN type = 'royalty_payout' AND details LIKE '%%[match_review:%%' THEN 'match'
                WHEN type = 'royalty_revert' AND details LIKE '%%[revert:%%' THEN 'revert'
                WHEN type = 'mt_fix_correction' AND details LIKE '%%[mtfix:%%' THEN 'fix'
                ELSE 'other'
            END AS bucket,
            COUNT(*) AS cnt,
            SUM(amount) AS total,
            MIN(transaction_date) AS min_date,
            MAX(transaction_date) AS max_date,
            AVG(amount) AS avg_amount
         FROM $t_trans
         WHERE user_telemovel = %s
           AND (
                ( type = 'royalty_payout' AND ( details LIKE '%%[royalty_import:%%' OR details LIKE '%%[match_review:%%' ) )
             OR ( type = 'royalty_revert' AND details LIKE '%%[revert:%%' )
             OR ( type = 'mt_fix_correction' AND details LIKE '%%[mtfix:%%' )
           )
         GROUP BY bucket",
        $telemovel
    ) );

    // 2) Markers únicos (cada import/match único deveria gerar UMA linha)
    // Se há 1.720 transações mas só 10 markers, há duplicação massiva.
    $unique_markers = (int) $wpdb->get_var( $wpdb->prepare(
        "SELECT COUNT(DISTINCT
            SUBSTRING_INDEX(SUBSTRING_INDEX(details, ']', 1), '[', -1)
         )
         FROM $t_trans
         WHERE user_telemovel = %s
           AND ( details LIKE '%%[royalty_import:%%'
              OR details LIKE '%%[match_review:%%' )",
        $telemovel
    ) );

    // 3) Top 10 markers que mais aparecem (se algum tem 100+ ocorrências, há bug)
    $repeating = $wpdb->get_results( $wpdb->prepare(
        "SELECT
            SUBSTRING_INDEX(SUBSTRING_INDEX(details, ']', 1), '[', -1) AS marker,
            COUNT(*) AS occurrences,
            SUM(amount) AS total_credited
         FROM $t_trans
         WHERE user_telemovel = %s
           AND ( details LIKE '%%[royalty_import:%%'
              OR details LIKE '%%[match_review:%%' )
         GROUP BY marker
         HAVING COUNT(*) > 1
         ORDER BY occurrences DESC
         LIMIT 10",
        $telemovel
    ) );

    // 4) Distribuição por mês (para ver se foi acumulado ao longo do tempo
    //    ou se foi tudo num burst)
    $by_month = $wpdb->get_results( $wpdb->prepare(
        "SELECT
            DATE_FORMAT(transaction_date, '%%Y-%%m') AS month,
            COUNT(*) AS cnt,
            SUM(amount) AS total
         FROM $t_trans
         WHERE user_telemovel = %s
           AND ( details LIKE '%%[royalty_import:%%'
              OR details LIKE '%%[match_review:%%' )
         GROUP BY month
         ORDER BY month DESC
         LIMIT 12",
        $telemovel
    ) );

    return [
        'by_type'        => $by_type,
        'unique_markers' => $unique_markers,
        'repeating'      => $repeating,
        'by_month'       => $by_month,
    ];
}
