<?php
/**
 * 1MZ For Artists — Project Chat Engine (admin live chat + anexos)
 *
 * Carregado pelo plugin principal. Inclui:
 *   1. Schema da tabela rmbz_project_chat_messages (dbDelta idempotente)
 *   2. Helpers para gravar/ler mensagens e merge com chat legado (artist_feedback)
 *   3. Controller REST /1mz/v1/admin-chat/* (admin only)
 *   4. Handler de upload de anexos (admin + artista)
 *
 * == 1.5.193 — Live chat + anexos no admin de Projetos ==
 *   - Schema nova mantendo backward-compat com artist_feedback string
 *   - Polling 3s sem WebSocket (funciona em qualquer hosting)
 *   - Toggle por projeto: artist_can_attach (default off)
 *   - Admin: imagem/áudio/PDF/vídeo curto sempre permitido
 *   - Artista: só quando toggle ON
 *
 * @package 1MZ_For_Artists
 */

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

/* ============================================================================
 * SCHEMA — rmbz_project_chat_messages (schema v1)
 * ============================================================================
 *
 * Encadeado com o sistema existente: option 'rmbz_project_chat_schema_version'
 * faz com que dbDelta corra só quando bumpado.
 */
define( 'RMBZ_PROJECT_CHAT_SCHEMA_VERSION', '1.0' );

function rmbz_project_chat_install_schema() {
    $current = get_option( 'rmbz_project_chat_schema_version', '0' );
    if ( version_compare( $current, RMBZ_PROJECT_CHAT_SCHEMA_VERSION, '>=' ) ) {
        return;
    }

    global $wpdb;
    $charset_collate = $wpdb->get_charset_collate();
    $table = $wpdb->prefix . 'rmbz_project_chat_messages';

    require_once ABSPATH . 'wp-admin/includes/upgrade.php';

    $sql = "CREATE TABLE {$table} (
        id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
        project_id BIGINT(20) UNSIGNED NOT NULL,
        author_type ENUM('admin','artist','system') NOT NULL DEFAULT 'admin',
        author_label VARCHAR(80) NULL,
        body TEXT NULL,
        attachment_url VARCHAR(500) NULL,
        attachment_type VARCHAR(40) NULL,
        attachment_name VARCHAR(200) NULL,
        attachment_size INT UNSIGNED NULL,
        is_edited TINYINT(1) NOT NULL DEFAULT 0,
        is_deleted TINYINT(1) NOT NULL DEFAULT 0,
        read_by_admin TINYINT(1) NOT NULL DEFAULT 1,
        read_by_artist TINYINT(1) NOT NULL DEFAULT 0,
        created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
        edited_at DATETIME NULL,
        PRIMARY KEY (id),
        KEY idx_project (project_id, id),
        KEY idx_created (created_at)
    ) {$charset_collate};";
    dbDelta( $sql );

    update_option( 'rmbz_project_chat_schema_version', RMBZ_PROJECT_CHAT_SCHEMA_VERSION );
}
add_action( 'init', 'rmbz_project_chat_install_schema', 5 );

/* ============================================================================
 * HELPERS — read / write / merge
 * ============================================================================ */

/**
 * Lê todas as mensagens do projeto, combinando o histórico legado
 * (`artist_feedback` string) com a tabela nova, em ordem cronológica.
 *
 * Mensagens legadas têm id negativo (-N) para nunca colidirem com ids
 * da tabela nova. Útil para "since=ID" do polling.
 *
 * @param int $project_id
 * @param int $since_id   Devolver apenas mensagens com id > since_id
 * @return array Lista de mensagens normalizadas:
 *   id, author_type, author_label, body, attachment{url,type,name,size}|null,
 *   is_edited, is_deleted, created_at (ISO), created_at_label (humano)
 */
function rmbz_pchat_get_messages( $project_id, $since_id = 0 ) {
    global $wpdb;
    $project_id = (int) $project_id;
    $since_id   = (int) $since_id;
    $out = [];

    // [1.5.202] BUGFIX duplicação: quando o artista envia mensagem via
    // tabela nova, o insert_message_as_artist espelha-a no legado
    // artist_feedback (para back-compat com app antiga 1.6.39). Mas o
    // admin lê AMBOS, então via UI nova o mesmo "olá" aparece 2 vezes:
    // uma como bubble correcta + uma como entrada de texto com link
    // do anexo cru. O dedup faz-se aqui: para cada entrada do legado,
    // se existe uma mensagem na tabela nova com o MESMO body e timestamp
    // do MESMO minuto, descarta a legada (a nova é a fonte de verdade
    // porque tem o attachment como objecto).
    //
    // Pré-fetch dos pares (body, minuto_ts) da tabela nova deste projeto
    // — só precisamos disto na carga inicial, polling não puxa legado.
    $new_signatures = [];
    $deleted_slots  = [];
    if ( $since_id === 0 ) {
        $rows_sig = $wpdb->get_results( $wpdb->prepare(
            "SELECT body, author_type, is_deleted, DATE_FORMAT(created_at, '%%Y-%%m-%%d %%H:%%i') AS minute_ts
             FROM {$wpdb->prefix}rmbz_project_chat_messages
             WHERE project_id = %d",
            $project_id
        ) );
        foreach ( (array) $rows_sig as $rs ) {
            // Chave: body trimmed + minuto
            $key = trim( (string) $rs->body ) . '|' . (string) $rs->minute_ts;
            $new_signatures[ $key ] = true;
            if ( ! empty( $rs->is_deleted ) ) {
                $deleted_slots[ (string) $rs->author_type . '|' . (string) $rs->minute_ts ] = true;
            }
        }
    }

    // 1. LEGADO — só inclui na primeira carga (since_id === 0).
    //    since=-1 é usado pelo cliente live quando ainda não existe cursor:
    //    consulta apenas a tabela nova, sem repetir todo o histórico legado.
    //    Polling subsequente só vê mensagens novas da tabela (ids positivos).
    if ( $since_id === 0 ) {
        $proj = $wpdb->get_row( $wpdb->prepare(
            "SELECT artist_feedback FROM {$wpdb->prefix}rmbz_artist_projects WHERE id = %d",
            $project_id
        ) );
        if ( $proj && ! empty( $proj->artist_feedback ) ) {
            $entries = preg_split( '/\n\n+/', (string) $proj->artist_feedback );
            $i = 0;
            foreach ( $entries as $raw ) {
                $raw = trim( $raw );
                if ( $raw === '' ) continue;
                $i++;

                // Detectar autor
                $is_admin = ( strpos( $raw, '<strong>Equipa' ) !== false
                           || strpos( $raw, '<strong>Admin' ) !== false
                           || strpos( $raw, '<strong>1MZ' ) !== false );

                // Timestamp
                $ts = null; $ts_iso = null; $ts_minute = null;
                if ( preg_match( '/^\[([^\]]+)\]/', $raw, $m ) ) {
                    $ts = $m[1];
                    // Tenta converter "dd/mm/yyyy HH:MM"
                    $d = DateTime::createFromFormat( 'd/m/Y H:i', $ts );
                    if ( $d ) {
                        $ts_iso = $d->format( 'Y-m-d H:i:s' );
                        $ts_minute = $d->format( 'Y-m-d H:i' );
                    }
                }

                // Texto limpo
                $text = preg_replace( '/^\[[^\]]+\]\s*<strong>[^<]+<\/strong>\s*:?\s*/', '', $raw );
                $text = trim( wp_strip_all_tags( $text ) );
                $text = preg_replace( '/\s*\(Editado\)\s*$/', '', $text );

                // [1.5.202] DEDUP: extrair só a parte de texto antes do
                // "📎 ... — URL" e ver se bate com mensagem da tabela nova
                // no mesmo minuto. Casos:
                //   - "Pode ver\n📎 file.jpg — https://..."  → body real = "Pode ver"
                //   - "📎 file.jpg — https://..."             → body real = ""
                //   - "olá"                                   → body real = "olá"
                $body_only = $text;
                if ( strpos( $body_only, '📎' ) !== false ) {
                    // Tirar tudo a partir do primeiro "📎"
                    $body_only = trim( preg_replace( '/\s*📎.*$/su', '', $body_only ) );
                }

                if ( $ts_minute !== null ) {
                    $legacy_author = $is_admin ? 'admin' : 'artist';
                    if ( isset( $deleted_slots[ $legacy_author . '|' . $ts_minute ] ) ) {
                        // Suprime também espelhos antigos cujos bodies já
                        // tinham sido limpos por versões anteriores.
                        continue;
                    }
                    $sig_key = $body_only . '|' . $ts_minute;
                    if ( isset( $new_signatures[ $sig_key ] ) ) {
                        // Esta entrada do legado já está na tabela nova
                        // como mensagem completa (com attachment proper).
                        // Skip.
                        continue;
                    }
                }

                $out[] = [
                    'id'              => -$i, // negativo = legado
                    'author_type'     => $is_admin ? 'admin' : 'artist',
                    'author_label'    => $is_admin ? 'Equipa 1MZ' : 'Artista',
                    'body'            => $text,
                    'attachment'      => null,
                    'is_edited'       => ( strpos( $raw, '(Editado)' ) !== false ),
                    'is_deleted'      => false,
                    'is_legacy'       => true,
                    'created_at'      => $ts_iso,
                    'created_at_label'=> $ts ?: '',
                ];
            }
        }
    }

    // 2. NOVA TABELA
    $where_since = $since_id > 0 ? $wpdb->prepare( 'AND id > %d', $since_id ) : '';
    $rows = $wpdb->get_results( $wpdb->prepare(
        "SELECT * FROM {$wpdb->prefix}rmbz_project_chat_messages
         WHERE project_id = %d {$where_since}
         ORDER BY id ASC",
        $project_id
    ) );
    if ( $rows ) {
        foreach ( $rows as $r ) {
            $att = null;
            if ( ! empty( $r->attachment_url ) ) {
                $att = [
                    'url'  => $r->attachment_url,
                    'type' => $r->attachment_type,
                    'name' => $r->attachment_name,
                    'size' => (int) $r->attachment_size,
                ];
            }
            $created = strtotime( $r->created_at );
            $out[] = [
                'id'              => (int) $r->id,
                'author_type'     => $r->author_type,
                'author_label'    => $r->author_label ?: ( $r->author_type === 'admin' ? 'Equipa 1MZ' : 'Artista' ),
                'body'            => $r->is_deleted ? '' : (string) $r->body,
                'attachment'      => $r->is_deleted ? null : $att,
                'is_edited'       => (bool) $r->is_edited,
                'is_deleted'      => (bool) $r->is_deleted,
                'is_legacy'       => false,
                'created_at'      => $r->created_at,
                'created_at_label'=> $created ? date_i18n( 'd/m/Y H:i', $created ) : '',
            ];
        }
    }

    return $out;
}

/**
 * IDs apagados são devolvidos em todos os ciclos de polling para que uma
 * alteração numa mensagem antiga seja refletida sem recarregar o chat.
 */
function rmbz_pchat_get_deleted_message_ids( $project_id ) {
    global $wpdb;
    $ids = $wpdb->get_col( $wpdb->prepare(
        "SELECT id FROM {$wpdb->prefix}rmbz_project_chat_messages
         WHERE project_id = %d AND is_deleted = 1
         ORDER BY id ASC",
        (int) $project_id
    ) );
    return array_map( 'intval', (array) $ids );
}

/**
 * Insere uma mensagem nova na tabela. Devolve o id ou false em falha.
 *
 * Também sincroniza com `artist_feedback` legado quando o autor é admin —
 * assim o artista no site continua a ver via a UI antiga até migrarmos.
 */
function rmbz_pchat_insert_message( $project_id, $author_type, $body, $attachment = null, $author_label = null ) {
    global $wpdb;
    $project_id = (int) $project_id;

    $valid_types = [ 'admin', 'artist', 'system' ];
    if ( ! in_array( $author_type, $valid_types, true ) ) {
        return false;
    }

    $body = (string) $body;
    $att_url  = isset( $attachment['url'] )  ? (string) $attachment['url']  : null;
    $att_type = isset( $attachment['type'] ) ? (string) $attachment['type'] : null;
    $att_name = isset( $attachment['name'] ) ? (string) $attachment['name'] : null;
    $att_size = isset( $attachment['size'] ) ? (int) $attachment['size']    : null;

    // Pelo menos texto OU anexo
    if ( trim( $body ) === '' && empty( $att_url ) ) {
        return false;
    }

    if ( ! $author_label ) {
        $author_label = $author_type === 'admin' ? 'Equipa 1MZ'
                      : ( $author_type === 'artist' ? 'Artista' : 'Sistema' );
    }

    $ok = $wpdb->insert(
        $wpdb->prefix . 'rmbz_project_chat_messages',
        [
            'project_id'      => $project_id,
            'author_type'     => $author_type,
            'author_label'    => $author_label,
            'body'            => $body,
            'attachment_url'  => $att_url,
            'attachment_type' => $att_type,
            'attachment_name' => $att_name,
            'attachment_size' => $att_size,
            'read_by_admin'   => $author_type === 'admin' ? 1 : 0,
            'read_by_artist'  => $author_type === 'artist' ? 1 : 0,
            'created_at'      => current_time( 'mysql' ),
        ],
        [ '%d','%s','%s','%s','%s','%s','%s','%d','%d','%d','%s' ]
    );
    if ( ! $ok ) return false;
    $msg_id = (int) $wpdb->insert_id;

    // [1.5.396] Gatilho único de mensagem inserida — usado pelo push admin
    // (mensagens de artista notificam a equipa no app 1MZ Admin).
    do_action( 'rmbz_pchat_message_inserted', (int) $project_id, (string) $author_type, (string) $body, $msg_id );

    // Sincroniza legado (espelha texto + anexo como link) — só admin, para
    // o artista no site/app continuar a ver via UI antiga.
    if ( $author_type === 'admin' ) {
        $ts = current_time( 'd/m/Y H:i' );
        $synth = "[$ts] <strong>Equipa 1MZ:</strong> ";
        if ( $body !== '' ) $synth .= $body;
        if ( $att_url ) {
            if ( $body !== '' ) $synth .= "\n";
            $synth .= '📎 ' . ( $att_name ?: 'Anexo' ) . ' — ' . esc_url_raw( $att_url );
        }
        $proj = $wpdb->get_row( $wpdb->prepare(
            "SELECT artist_feedback FROM {$wpdb->prefix}rmbz_artist_projects WHERE id = %d",
            $project_id
        ) );
        if ( $proj ) {
            $updated = trim( (string) $proj->artist_feedback );
            $updated = $updated === '' ? $synth : $updated . "\n\n" . $synth;
            $wpdb->update(
                $wpdb->prefix . 'rmbz_artist_projects',
                [ 'artist_feedback' => $updated ],
                [ 'id' => $project_id ],
                [ '%s' ], [ '%d' ]
            );
        }
    }

    // Notificação para o artista quando admin envia (push + sino in-app).
    if ( $author_type === 'admin' && function_exists( 'rmbz_notify_create' ) ) {
        $proj = $wpdb->get_row( $wpdb->prepare(
            "SELECT artist_telemovel, project_title FROM {$wpdb->prefix}rmbz_artist_projects WHERE id = %d",
            $project_id
        ) );
        if ( $proj && ! empty( $proj->artist_telemovel ) ) {
            $preview = $att_url
                ? ( '📎 ' . ( $att_name ?: 'Anexo' ) )
                : mb_substr( wp_strip_all_tags( $body ), 0, 140 );
            rmbz_notify_create([
                'type'             => 'project_chat_message',
                'severity'         => 'info',
                'title'            => 'Mensagem nova: ' . $proj->project_title,
                'body'             => $preview,
                'action_url'       => "1mzapp://projects/{$project_id}",
                'action_label'     => 'Ver',
                'target_telemovel' => $proj->artist_telemovel,
                // dedup curto: 1 push por minuto por projecto, evita spam
                'dedup_key'        => 'pchat_' . $project_id . '_' . floor( time() / 60 ),
                'metadata'         => [ 'project_id' => $project_id, 'message_id' => $msg_id ],
                'created_by'       => 'admin_chat',
            ]);
        }
    }

    return $msg_id;
}

/**
 * Toggle "artist_can_attach" no extra_data do projecto.
 * Retorna o novo valor.
 */
function rmbz_pchat_set_artist_can_attach( $project_id, $enabled ) {
    global $wpdb;
    $row = $wpdb->get_row( $wpdb->prepare(
        "SELECT extra_data FROM {$wpdb->prefix}rmbz_artist_projects WHERE id = %d",
        $project_id
    ) );
    if ( ! $row ) return false;
    $extra = ! empty( $row->extra_data ) ? json_decode( $row->extra_data, true ) : [];
    if ( ! is_array( $extra ) ) $extra = [];
    $extra['artist_can_attach'] = $enabled ? 1 : 0;
    $extra['artist_can_attach_updated'] = current_time( 'mysql' );
    $wpdb->update(
        $wpdb->prefix . 'rmbz_artist_projects',
        [ 'extra_data' => wp_json_encode( $extra ) ],
        [ 'id' => $project_id ],
        [ '%s' ], [ '%d' ]
    );

    // Mensagem de sistema visível no chat
    rmbz_pchat_insert_message(
        $project_id,
        'system',
        $enabled
            ? '✓ Admin permitiu que o artista anexe ficheiros nesta conversa.'
            : '✕ Admin desactivou anexos do artista.',
        null,
        'Sistema'
    );
    return $enabled ? 1 : 0;
}

function rmbz_pchat_get_artist_can_attach( $project_id ) {
    global $wpdb;
    $row = $wpdb->get_row( $wpdb->prepare(
        "SELECT extra_data FROM {$wpdb->prefix}rmbz_artist_projects WHERE id = %d",
        $project_id
    ) );
    if ( ! $row ) return false;
    $extra = ! empty( $row->extra_data ) ? json_decode( $row->extra_data, true ) : [];
    return ! empty( $extra['artist_can_attach'] );
}

/* ============================================================================
 * UPLOAD HANDLER — anexos do chat
 * ============================================================================
 *
 * Diretório: /wp-content/uploads/1mz-chat/{project_id}/{random-name}.{ext}
 * Tipos permitidos: image/*, audio/*, application/pdf, video/mp4
 * Tamanho máximo: 15 MB
 *
 * Usado tanto pelo admin (autenticação WP) como pelo artista (sessão custom
 * via rmbz_is_user_logged_in). O caller passa $author_type para sabermos.
 */
function rmbz_pchat_handle_upload( $project_id, $file, $author_type = 'admin' ) {
    if ( empty( $file ) || ! isset( $file['tmp_name'] ) ) {
        return new WP_Error( 'no_file', 'Nenhum ficheiro recebido.' );
    }
    if ( ! empty( $file['error'] ) && $file['error'] !== UPLOAD_ERR_OK ) {
        return new WP_Error( 'upload_err', 'Erro no upload (PHP: ' . (int) $file['error'] . ').' );
    }

    $size = (int) ( $file['size'] ?? 0 );
    if ( $size <= 0 || $size > 15 * 1024 * 1024 ) {
        return new WP_Error( 'too_big', 'Ficheiro acima do limite de 15 MB.' );
    }

    // MIME real do conteúdo (finfo) + extensão. Têm de bater certo (AND).
    $real_mime = '';
    if ( function_exists( 'finfo_open' ) ) {
        $fi = finfo_open( FILEINFO_MIME_TYPE );
        if ( $fi ) {
            $real_mime = (string) finfo_file( $fi, $file['tmp_name'] );
            finfo_close( $fi );
        }
    }

    $allowed = [
        'image/jpeg'      => [ 'jpg', 'jpeg' ],
        'image/png'       => [ 'png' ],
        'image/webp'      => [ 'webp' ],
        'image/gif'       => [ 'gif' ],
        'audio/mpeg'      => [ 'mp3' ],
        'audio/mp4'       => [ 'm4a' ],
        'audio/aac'       => [ 'aac' ],
        'audio/ogg'       => [ 'ogg' ],
        'audio/wav'       => [ 'wav' ],
        'audio/x-wav'     => [ 'wav' ],
        'audio/webm'      => [ 'weba', 'webm' ],
        'application/pdf' => [ 'pdf' ],
        'video/mp4'       => [ 'mp4' ],
        'video/webm'      => [ 'webm' ],
        'video/quicktime' => [ 'mov' ],
    ];

    if ( ! isset( $allowed[ $real_mime ] ) ) {
        return new WP_Error( 'bad_mime', 'Tipo de ficheiro não permitido (' . esc_html( $real_mime ) . ').' );
    }

    $ext_input = strtolower( pathinfo( (string) ( $file['name'] ?? '' ), PATHINFO_EXTENSION ) );
    if ( ! in_array( $ext_input, $allowed[ $real_mime ], true ) ) {
        return new WP_Error( 'bad_ext', 'Extensão "' . esc_html( $ext_input ) . '" não combina com o conteúdo.' );
    }

    // Pasta destino
    $uploads = wp_upload_dir();
    if ( ! empty( $uploads['error'] ) ) {
        return new WP_Error( 'no_uploads', 'Pasta de uploads inacessível.' );
    }
    $dir = trailingslashit( $uploads['basedir'] ) . '1mz-chat/' . (int) $project_id;
    if ( ! file_exists( $dir ) ) {
        wp_mkdir_p( $dir );
    }
    // Index.html anti-listagem
    $index = $dir . '/index.html';
    if ( ! file_exists( $index ) ) {
        @file_put_contents( $index, '' );
    }

    // Nome random + extensão limpa
    $random = wp_generate_password( 16, false, false );
    $final_name = $random . '.' . $ext_input;
    $dest = $dir . '/' . $final_name;

    if ( ! @move_uploaded_file( $file['tmp_name'], $dest ) ) {
        return new WP_Error( 'move_fail', 'Falha ao guardar o ficheiro no servidor.' );
    }

    // URL público
    $url = trailingslashit( $uploads['baseurl'] ) . '1mz-chat/' . (int) $project_id . '/' . $final_name;

    // Categorizar para a UI
    $category = 'file';
    if ( strpos( $real_mime, 'image/' ) === 0 ) $category = 'image';
    elseif ( strpos( $real_mime, 'audio/' ) === 0 ) $category = 'audio';
    elseif ( strpos( $real_mime, 'video/' ) === 0 ) $category = 'video';
    elseif ( $real_mime === 'application/pdf' )    $category = 'pdf';

    // Nome amigável para mostrar (sem path)
    $friendly = sanitize_file_name( (string) ( $file['name'] ?? $final_name ) );

    return [
        'url'      => $url,
        'type'     => $category,    // image|audio|video|pdf|file
        'mime'     => $real_mime,
        'name'     => $friendly,
        'size'     => $size,
    ];
}

/* ============================================================================
 * REST API — /1mz/v1/admin-chat/*  (admin only)
 * ============================================================================
 *
 * O admin já está autenticado via cookies WP (current_user_can manage_options).
 * Usa nonce REST padrão (X-WP-Nonce) gerado em wp_create_nonce('wp_rest').
 */

add_action( 'rest_api_init', function () {
    register_rest_route( '1mz/v1', '/admin-chat/(?P<id>\d+)', [
        'methods'             => 'GET',
        'callback'            => 'rmbz_pchat_rest_get',
        'permission_callback' => 'rmbz_pchat_perm_admin',
        'args' => [
            'since' => [ 'type' => 'integer', 'default' => 0 ],
        ],
    ] );

    register_rest_route( '1mz/v1', '/admin-chat/(?P<id>\d+)/info', [
        'methods'             => 'GET',
        'callback'            => 'rmbz_pchat_rest_info',
        'permission_callback' => 'rmbz_pchat_perm_admin',
    ] );

    register_rest_route( '1mz/v1', '/admin-chat/(?P<id>\d+)/send', [
        'methods'             => 'POST',
        'callback'            => 'rmbz_pchat_rest_send',
        'permission_callback' => 'rmbz_pchat_perm_admin',
    ] );

    register_rest_route( '1mz/v1', '/admin-chat/(?P<id>\d+)/toggle-attach', [
        'methods'             => 'POST',
        'callback'            => 'rmbz_pchat_rest_toggle',
        'permission_callback' => 'rmbz_pchat_perm_admin',
    ] );

    register_rest_route( '1mz/v1', '/admin-chat/(?P<id>\d+)/upload', [
        'methods'             => 'POST',
        'callback'            => 'rmbz_pchat_rest_upload',
        'permission_callback' => 'rmbz_pchat_perm_admin',
    ] );

    register_rest_route( '1mz/v1', '/admin-chat/(?P<id>\d+)/edit', [
        'methods'             => 'POST',
        'callback'            => 'rmbz_pchat_rest_edit',
        'permission_callback' => 'rmbz_pchat_perm_admin',
    ] );

    register_rest_route( '1mz/v1', '/admin-chat/(?P<id>\d+)/delete', [
        'methods'             => 'POST',
        'callback'            => 'rmbz_pchat_rest_delete',
        'permission_callback' => 'rmbz_pchat_perm_admin',
    ] );
} );

function rmbz_pchat_perm_admin( WP_REST_Request $req ) {
    return current_user_can( 'manage_options' );
}

function rmbz_pchat_check_project_exists( $project_id ) {
    global $wpdb;
    $exists = (int) $wpdb->get_var( $wpdb->prepare(
        "SELECT id FROM {$wpdb->prefix}rmbz_artist_projects WHERE id = %d",
        (int) $project_id
    ) );
    return $exists > 0;
}

/**
 * Chat bloqueado quando o projeto morreu (rejeitado ou cancelado por prazo).
 * Verificado em TODAS as vias de envio: app do artista, app admin e site.
 */
function rmbz_pchat_is_chat_locked( $project_id ) {
    global $wpdb;
    $status = (string) $wpdb->get_var( $wpdb->prepare(
        "SELECT production_status FROM {$wpdb->prefix}rmbz_artist_projects WHERE id = %d",
        (int) $project_id
    ) );
    return in_array( $status, [ 'rejected', 'cancelled_no_confirmation' ], true );
}

function rmbz_pchat_rest_get( WP_REST_Request $req ) {
    $project_id = (int) $req['id'];
    $since      = (int) $req->get_param( 'since' );

    if ( ! rmbz_pchat_check_project_exists( $project_id ) ) {
        return rmbz_api_error( 'Projeto não encontrado.', 404, 'not_found' );
    }

    $messages = rmbz_pchat_get_messages( $project_id, $since );

    // marcar admin como leitor
    if ( $since === 0 ) {
        global $wpdb;
        $marked = $wpdb->update(
            $wpdb->prefix . 'rmbz_project_chat_messages',
            [ 'read_by_admin' => 1 ],
            [ 'project_id' => $project_id, 'read_by_admin' => 0 ],
            [ '%d' ], [ '%d', '%d' ]
        );
        // [1.5.200] Se marcou alguma como lida, invalidar cache do menu
        // para o badge actualizar (caso contrário ia esperar até 2min).
        if ( $marked && (int) $marked > 0 ) {
            delete_transient( 'rmbz_admin_notification_counts' );
        }
    }

    return rmbz_api_ok( [
        'messages'          => $messages,
        'deleted_message_ids' => rmbz_pchat_get_deleted_message_ids( $project_id ),
        'artist_can_attach' => (bool) rmbz_pchat_get_artist_can_attach( $project_id ),
        'server_time'       => current_time( 'mysql' ),
    ] );
}

/* ============================================================================
 * INFO endpoint — perfil do artista + change log + recomendações
 * ============================================================================
 *
 * Devolve dados não-realtime para a sidebar do detalhe: estatísticas do
 * artista (saldo, projetos anteriores), log de alterações recentes
 * deste projeto, e recomendações geradas server-side.
 */
function rmbz_pchat_rest_info( WP_REST_Request $req ) {
    $project_id = (int) $req['id'];
    global $wpdb;

    // Garantir que projeto tem pelo menos a entrada inicial no log
    rmbz_pchat_ensure_initial_entry( $project_id );

    $proj = $wpdb->get_row( $wpdb->prepare(
        "SELECT p.*, w.user_nome, w.user_email, w.balance, w.is_banned, w.is_creator, w.user_provincia
         FROM {$wpdb->prefix}rmbz_artist_projects p
         LEFT JOIN {$wpdb->prefix}rmbz_wallets w ON p.artist_telemovel = w.user_telemovel
         WHERE p.id = %d",
        $project_id
    ) );
    if ( ! $proj ) {
        return rmbz_api_error( 'Projeto não encontrado.', 404, 'not_found' );
    }

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

    // [1.5.373] ?full=1 → histórico completo (para o modal); senão, 20 recentes.
    $full  = (bool) $req->get_param( 'full' );
    $limit = $full ? 0 : 20;

    return rmbz_api_ok( [
        'artist'         => rmbz_pchat_get_artist_info( $proj ),
        'change_log'     => rmbz_pchat_get_change_log( $extra, $limit ),
        'change_log_total' => isset( $extra['change_log'] ) && is_array( $extra['change_log'] ) ? count( $extra['change_log'] ) : 0,
        'recommendations'=> rmbz_pchat_get_recommendations( $proj, $extra ),
    ] );
}

/**
 * Calcula informações do artista para o sidebar.
 *
 * Inclui: saldo da carteira, total de projetos (todos), publicados,
 * em curso, rejeitados, e quantas submissões de música/vídeo já fez.
 * Tudo em duas queries agregadas.
 */
function rmbz_pchat_get_artist_info( $project ) {
    global $wpdb;
    $tel = $project->artist_telemovel;

    // Projetos agregados
    $proj_rows = $wpdb->get_results( $wpdb->prepare(
        "SELECT production_status AS s, COUNT(*) AS c
         FROM {$wpdb->prefix}rmbz_artist_projects
         WHERE artist_telemovel = %s
         GROUP BY production_status",
        $tel
    ) );
    $proj_counts = [
        'total' => 0, 'pending' => 0, 'in_progress' => 0,
        'done' => 0, 'rejected' => 0, 'correction' => 0,
    ];
    foreach ( $proj_rows as $r ) {
        $n = (int) $r->c;
        $proj_counts['total'] += $n;
        if ( $r->s === 'pending_review' )                                   $proj_counts['pending']    += $n;
        elseif ( in_array( $r->s, [ 'pre-production', 'kyc_review', 'filming', 'editing' ], true ) )
                                                                            $proj_counts['in_progress']+= $n;
        elseif ( $r->s === 'done' )                                          $proj_counts['done']       += $n;
        elseif ( $r->s === 'rejected' )                                      $proj_counts['rejected']   += $n;
        elseif ( $r->s === 'correction' )                                    $proj_counts['correction'] += $n;
    }

    // Submissões agregadas (Música + Vídeos)
    $sub_rows = $wpdb->get_results( $wpdb->prepare(
        "SELECT scenario_type AS s, review_status AS r, COUNT(*) AS c
         FROM {$wpdb->prefix}rmbz_artist_submissions
         WHERE artist_telemovel = %s
         GROUP BY scenario_type, review_status",
        $tel
    ) );
    $sub_counts = [ 'music_total' => 0, 'music_published' => 0, 'video_total' => 0, 'video_published' => 0 ];
    foreach ( $sub_rows as $r ) {
        $n = (int) $r->c;
        if ( $r->s === 'Publicação Site' ) {
            $sub_counts['music_total'] += $n;
            if ( $r->r === 'finalizado' ) $sub_counts['music_published'] += $n;
        } elseif ( $r->s === 'Redes Sociais' ) {
            $sub_counts['video_total'] += $n;
            if ( $r->r === 'finalizado' ) $sub_counts['video_published'] += $n;
        }
    }

    // Data primeiro contacto
    $first_contact = $wpdb->get_var( $wpdb->prepare(
        "SELECT MIN(submission_date) FROM {$wpdb->prefix}rmbz_artist_submissions WHERE artist_telemovel = %s",
        $tel
    ) );
    if ( ! $first_contact ) {
        $first_contact = $wpdb->get_var( $wpdb->prepare(
            "SELECT MIN(contract_date) FROM {$wpdb->prefix}rmbz_artist_projects WHERE artist_telemovel = %s",
            $tel
        ) );
    }

    return [
        'name'              => $project->user_nome ?: '—',
        'telemovel'         => $project->artist_telemovel,
        'email'             => $project->user_email ?: null,
        'provincia'         => $project->user_provincia ?: null,
        'is_banned'         => ! empty( $project->is_banned ),
        'is_creator'        => ! empty( $project->is_creator ),
        'balance'           => (float) ( $project->balance ?? 0 ),
        'projects'          => $proj_counts,
        'submissions'       => $sub_counts,
        'first_contact_at'  => $first_contact ?: null,
    ];
}

/**
 * Lê o change log gravado em extra_data['change_log'] (array).
 * Devolve do mais recente para o mais antigo. $limit <= 0 devolve tudo.
 *
 * Cada entrada: { ts: 'Y-m-d H:i:s', who: 'admin'|'system', what: string, from?, to? }
 */
function rmbz_pchat_get_change_log( $extra, $limit = 20 ) {
    $log = isset( $extra['change_log'] ) && is_array( $extra['change_log'] )
         ? $extra['change_log'] : [];
    $log = array_reverse( $log ); // mais recente primeiro
    // [1.5.373] $limit <= 0 → devolve tudo (para o modal de histórico completo).
    if ( $limit > 0 ) {
        $log = array_slice( $log, 0, $limit );
    }

    // Formatar para o cliente
    $out = [];
    foreach ( $log as $e ) {
        if ( ! is_array( $e ) ) continue;
        $ts  = isset( $e['ts'] )   ? (string) $e['ts'] : '';
        $epoch = $ts ? strtotime( $ts ) : 0;
        $out[] = [
            'ts'    => $ts,
            'ts_label' => $epoch ? date_i18n( 'd/m/Y H:i', $epoch ) : '',
            'who'   => $e['who'] ?? 'admin',
            'what'  => (string) ( $e['what'] ?? '' ),
            'from'  => isset( $e['from'] ) ? (string) $e['from'] : null,
            'to'    => isset( $e['to'] ) ? (string) $e['to'] : null,
        ];
    }
    return $out;
}

/**
 * Regista uma entrada no change log do projeto.
 *
 * Não é REST endpoint — é chamado server-side quando admin grava o form
 * principal (hook em onemz_admin_update_project) ou quando o sistema
 * altera algo (pagamento aprovado, KYC, etc.).
 *
 * Limite: 50 entradas (rotação FIFO para não inchar extra_data).
 */
function rmbz_pchat_append_change_log( $project_id, $entry ) {
    global $wpdb;
    $row = $wpdb->get_row( $wpdb->prepare(
        "SELECT extra_data FROM {$wpdb->prefix}rmbz_artist_projects WHERE id = %d",
        (int) $project_id
    ) );
    if ( ! $row ) return false;
    $extra = ! empty( $row->extra_data ) ? json_decode( $row->extra_data, true ) : [];
    if ( ! is_array( $extra ) ) $extra = [];
    if ( ! isset( $extra['change_log'] ) || ! is_array( $extra['change_log'] ) ) {
        $extra['change_log'] = [];
    }

    $extra['change_log'][] = array_merge(
        [ 'ts' => current_time( 'mysql' ), 'who' => 'admin' ],
        is_array( $entry ) ? $entry : []
    );

    // [1.5.373] Manter máx 200 (histórico completo no modal sem inchar demais).
    if ( count( $extra['change_log'] ) > 200 ) {
        $extra['change_log'] = array_slice( $extra['change_log'], -200 );
    }

    $wpdb->update(
        $wpdb->prefix . 'rmbz_artist_projects',
        [ 'extra_data' => wp_json_encode( $extra ) ],
        [ 'id' => (int) $project_id ],
        [ '%s' ], [ '%d' ]
    );
    return true;
}

/**
 * Gera recomendações baseadas em regras simples.
 *
 * Cada recomendação tem `severity` (info|warn|alert) e `text`. O cliente
 * mostra ícone + cor conforme. Tudo determinístico — sem ML.
 */
function rmbz_pchat_get_recommendations( $project, $extra ) {
    global $wpdb;
    $recs = [];
    $tel = $project->artist_telemovel;
    $status = $project->production_status ?: 'pending_review';
    $now = time();

    // 1. Histórico de rejeições do artista nos projetos
    $rej_count = (int) $wpdb->get_var( $wpdb->prepare(
        "SELECT COUNT(*) FROM {$wpdb->prefix}rmbz_artist_projects
         WHERE artist_telemovel = %s AND production_status = 'rejected' AND id != %d",
        $tel, (int) $project->id
    ) );
    if ( $rej_count >= 3 ) {
        $recs[] = [
            'severity' => 'alert',
            'icon'     => '⚠',
            'text'     => "Artista já tem {$rej_count} projetos rejeitados anteriores — atenção redobrada.",
        ];
    } elseif ( $rej_count >= 1 ) {
        $recs[] = [
            'severity' => 'warn',
            'icon'     => 'ℹ',
            'text'     => "Artista tem {$rej_count} rejeição/ões anterior(es).",
        ];
    }

    // 2. Carteira banida
    if ( ! empty( $project->is_banned ) ) {
        $recs[] = [
            'severity' => 'alert',
            'icon'     => '🚫',
            'text'     => 'Carteira do artista está BANIDA. Não publicar nem libertar pagamento.',
        ];
    }

    // 3. Saldo na carteira
    if ( isset( $project->balance ) && (float) $project->balance > 1000 ) {
        $recs[] = [
            'severity' => 'info',
            'icon'     => '💰',
            'text'     => 'Carteira tem saldo ' . number_format( (float) $project->balance, 2, ',', '.' ) . ' MT — pode usar para pagar este projeto.',
        ];
    }

    // 4. Contrato aceite há tempo mas projeto ainda em pre-production
    $contract_accepted_at = $extra['contract_accepted_at'] ?? null;
    if ( $contract_accepted_at && in_array( $status, [ 'pre-production', 'pending_review' ], true ) ) {
        $days = floor( ( $now - strtotime( $contract_accepted_at ) ) / 86400 );
        if ( $days >= 7 ) {
            $recs[] = [
                'severity' => 'warn',
                'icon'     => '⏰',
                'text'     => "Contrato aceite há {$days} dias mas a produção ainda não avançou — avançar?",
            ];
        }
    }

    // 5. Pagamento pendente após pre-production
    if ( in_array( $status, [ 'filming', 'editing' ], true ) && empty( $project->is_paid ) ) {
        $recs[] = [
            'severity' => 'warn',
            'icon'     => '💸',
            'text'     => 'Projeto em produção mas pagamento ainda não está completo.',
        ];
    }

    // 6. Sem actividade no chat há X dias
    $last_msg = $wpdb->get_var( $wpdb->prepare(
        "SELECT MAX(created_at) FROM {$wpdb->prefix}rmbz_project_chat_messages
         WHERE project_id = %d AND author_type IN ('admin','artist')",
        (int) $project->id
    ) );
    if ( $last_msg ) {
        $days_silent = floor( ( $now - strtotime( $last_msg ) ) / 86400 );
        if ( $days_silent >= 5 && ! in_array( $status, [ 'done', 'rejected' ], true ) ) {
            $recs[] = [
                'severity' => 'info',
                'icon'     => '💤',
                'text'     => "Sem mensagens há {$days_silent} dias — vale a pena dar um toque.",
            ];
        }
    }

    // 7. Novo artista
    $total_projects = (int) $wpdb->get_var( $wpdb->prepare(
        "SELECT COUNT(*) FROM {$wpdb->prefix}rmbz_artist_projects WHERE artist_telemovel = %s",
        $tel
    ) );
    if ( $total_projects <= 1 ) {
        $recs[] = [
            'severity' => 'info',
            'icon'     => '⭐',
            'text'     => 'Primeiro projeto deste artista. Bem-vindo!',
        ];
    }

    // 8. Artista com muitos sucessos
    $done_count = (int) $wpdb->get_var( $wpdb->prepare(
        "SELECT COUNT(*) FROM {$wpdb->prefix}rmbz_artist_projects
         WHERE artist_telemovel = %s AND production_status = 'done'",
        $tel
    ) );
    if ( $done_count >= 3 ) {
        $recs[] = [
            'severity' => 'info',
            'icon'     => '🏆',
            'text'     => "Artista veterano: {$done_count} videoclipes já lançados consigo.",
        ];
    }

    return $recs;
}

/**
 * Hook no save do projeto para registar mudanças automaticamente.
 *
 * O handler onemz_admin_update_project já existe (não tocamos nele) e
 * grava o form completo. Antes desse hook correr, capturamos o estado
 * actual; depois comparamos e registamos diff.
 *
 * Usa duas globals temporárias por simplicidade — só vive durante uma
 * request, não persistido.
 */
$GLOBALS['_rmbz_pchat_pre_save'] = null;
add_action( 'admin_post_onemz_admin_update_project', function () {
    $pid = isset( $_POST['project_id'] ) ? (int) $_POST['project_id'] : 0;
    if ( $pid <= 0 ) return;
    global $wpdb;
    $before = $wpdb->get_row( $wpdb->prepare(
        "SELECT production_status, is_paid, final_video_url, youtube_url, script_content, extra_data
         FROM {$wpdb->prefix}rmbz_artist_projects WHERE id = %d", $pid
    ) );
    $GLOBALS['_rmbz_pchat_pre_save'] = [ 'project_id' => $pid, 'before' => $before ];
}, 1 );

add_action( 'admin_post_onemz_admin_update_project', function () {
    $snap = $GLOBALS['_rmbz_pchat_pre_save'] ?? null;
    if ( ! $snap || empty( $snap['before'] ) ) return;
    global $wpdb;
    $pid = (int) $snap['project_id'];
    $before = $snap['before'];
    $after = $wpdb->get_row( $wpdb->prepare(
        "SELECT production_status, is_paid, final_video_url, youtube_url, script_content, extra_data
         FROM {$wpdb->prefix}rmbz_artist_projects WHERE id = %d", $pid
    ) );
    if ( ! $after ) return;

    $who = rmbz_pchat_current_admin_who();

    // Comparar campos principais
    $checks = [
        'production_status' => 'Estado',
        'is_paid'           => 'Pagamento',
        'final_video_url'   => 'Link HD',
        'youtube_url'       => 'YouTube',
        'script_content'    => 'Anotações',
    ];
    foreach ( $checks as $field => $label ) {
        if ( (string) $before->{$field} !== (string) $after->{$field} ) {
            $b = (string) $before->{$field};
            $a = (string) $after->{$field};
            // Trim valores longos para o log não inchar
            if ( strlen( $b ) > 80 ) $b = substr( $b, 0, 80 ) . '…';
            if ( strlen( $a ) > 80 ) $a = substr( $a, 0, 80 ) . '…';
            rmbz_pchat_append_change_log( $pid, [
                'who'  => $who,
                'what' => 'Alterou ' . $label,
                'from' => $b,
                'to'   => $a,
            ] );
        }
    }

    // Comparar campos relevantes do extra_data
    $bx = ! empty( $before->extra_data ) ? json_decode( $before->extra_data, true ) : [];
    $ax = ! empty( $after->extra_data )  ? json_decode( $after->extra_data, true )  : [];
    if ( ! is_array( $bx ) ) $bx = [];
    if ( ! is_array( $ax ) ) $ax = [];
    $extra_checks = [
        'script_url'           => 'Link do guião',
        'allow_self_fund'      => 'Permitir auto-financiamento',
        'contract_deadline'    => 'Prazo do contrato',
    ];
    foreach ( $extra_checks as $field => $label ) {
        $b = isset( $bx[$field] ) ? (string) $bx[$field] : '';
        $a = isset( $ax[$field] ) ? (string) $ax[$field] : '';
        if ( $b !== $a ) {
            if ( strlen( $b ) > 80 ) $b = substr( $b, 0, 80 ) . '…';
            if ( strlen( $a ) > 80 ) $a = substr( $a, 0, 80 ) . '…';
            rmbz_pchat_append_change_log( $pid, [
                'who'  => $who,
                'what' => 'Alterou ' . $label,
                'from' => $b,
                'to'   => $a,
            ] );
        }
    }
}, 999 );

/* ============================================================================
 * LOG: EVENTOS DO CICLO DE VIDA DO PROJETO
 * ============================================================================
 *
 * Tudo o que acontece a um projeto é registado: candidatura criada, contrato
 * enviado/aceite/rejeitado/revogado, KYC aprovado/rejeitado, pagamento do
 * cenário pelo artista, escolha de localização, depósitos aprovados, etc.
 *
 * Estratégia:
 *   - Hooks com priority 99 (depois do handler ter feito o trabalho)
 *   - Cada um faz append no change_log com texto humano
 *   - Falhas silenciosas (try/catch) — log nunca quebra a operação principal
 */

function rmbz_pchat_current_admin_who() {
    if ( ! function_exists( 'wp_get_current_user' ) ) return 'admin';
    $u = wp_get_current_user();
    if ( $u && ! empty( $u->user_login ) ) return $u->user_login;
    return 'admin';
}

/**
 * Log: admin faz upload de um contrato.
 */
add_action( 'admin_post_rmbz_admin_upload_contract', function () {
    $pid = isset( $_POST['project_id'] ) ? (int) $_POST['project_id'] : 0;
    if ( $pid <= 0 ) return;
    rmbz_pchat_append_change_log( $pid, [
        'who'  => rmbz_pchat_current_admin_who(),
        'what' => '📄 Enviou contrato para o artista assinar',
    ] );
}, 99 );

/**
 * Log: admin revoga / apaga contrato.
 */
add_action( 'admin_post_onemz_admin_revoke_contract', function () {
    $pid = isset( $_POST['project_id'] ) ? (int) $_POST['project_id'] : 0;
    if ( $pid <= 0 ) return;
    rmbz_pchat_append_change_log( $pid, [
        'who'  => rmbz_pchat_current_admin_who(),
        'what' => '🗑 Revogou contrato — projeto cancelado',
    ] );
}, 99 );

/**
 * Log: admin força rejeição do projeto.
 */
add_action( 'admin_post_rmbz_admin_force_reject_project', function () {
    $pid = isset( $_POST['project_id'] ) ? (int) $_POST['project_id'] : 0;
    if ( $pid <= 0 ) return;
    $reason = isset( $_POST['admin_rejection_reason'] ) ? sanitize_text_field( $_POST['admin_rejection_reason'] ) : '';
    rmbz_pchat_append_change_log( $pid, [
        'who'  => rmbz_pchat_current_admin_who(),
        'what' => '❌ Rejeitou projeto definitivamente',
        'from' => '',
        'to'   => $reason,
    ] );
}, 99 );

/**
 * Log: admin aprova KYC.
 */
add_action( 'admin_post_onemz_admin_approve_kyc', function () {
    $pid = isset( $_GET['project_id'] ) ? (int) $_GET['project_id'] : 0;
    if ( $pid <= 0 ) return;
    rmbz_pchat_append_change_log( $pid, [
        'who'  => rmbz_pchat_current_admin_who(),
        'what' => '✓ Aprovou identidade (KYC) do artista',
    ] );
}, 99 );

/**
 * Log: admin rejeita KYC.
 */
add_action( 'admin_post_onemz_admin_reject_kyc', function () {
    $pid = isset( $_POST['project_id'] ) ? (int) $_POST['project_id']
         : ( isset( $_GET['project_id'] ) ? (int) $_GET['project_id'] : 0 );
    if ( $pid <= 0 ) return;
    $reason = isset( $_POST['kyc_rejection_reason'] ) ? sanitize_text_field( $_POST['kyc_rejection_reason'] ) : '';
    rmbz_pchat_append_change_log( $pid, [
        'who'  => rmbz_pchat_current_admin_who(),
        'what' => '✕ Rejeitou identidade (KYC)',
        'from' => '',
        'to'   => $reason,
    ] );
}, 99 );

/**
 * Log: artista aceita contrato.
 */
add_action( 'admin_post_onemz_accept_contract', function () {
    $pid = isset( $_POST['project_id'] ) ? (int) $_POST['project_id'] : 0;
    if ( $pid <= 0 ) return;
    rmbz_pchat_append_change_log( $pid, [
        'who'  => 'artista',
        'what' => '✓ Artista aceitou o contrato',
    ] );
}, 99 );
add_action( 'admin_post_nopriv_onemz_accept_contract', function () {
    $pid = isset( $_POST['project_id'] ) ? (int) $_POST['project_id'] : 0;
    if ( $pid <= 0 ) return;
    rmbz_pchat_append_change_log( $pid, [
        'who'  => 'artista',
        'what' => '✓ Artista aceitou o contrato',
    ] );
}, 99 );

/**
 * Log: artista rejeita contrato.
 */
add_action( 'admin_post_onemz_reject_contract', function () {
    $pid = isset( $_POST['project_id'] ) ? (int) $_POST['project_id'] : 0;
    if ( $pid <= 0 ) return;
    $reason = isset( $_POST['rejection_reason'] ) ? sanitize_text_field( $_POST['rejection_reason'] ) : '';
    rmbz_pchat_append_change_log( $pid, [
        'who'  => 'artista',
        'what' => '✕ Artista rejeitou o contrato',
        'from' => '',
        'to'   => $reason,
    ] );
}, 99 );
add_action( 'admin_post_nopriv_onemz_reject_contract', function () {
    $pid = isset( $_POST['project_id'] ) ? (int) $_POST['project_id'] : 0;
    if ( $pid <= 0 ) return;
    $reason = isset( $_POST['rejection_reason'] ) ? sanitize_text_field( $_POST['rejection_reason'] ) : '';
    rmbz_pchat_append_change_log( $pid, [
        'who'  => 'artista',
        'what' => '✕ Artista rejeitou o contrato',
        'from' => '',
        'to'   => $reason,
    ] );
}, 99 );

/**
 * Log: artista escolheu localização de orçamento.
 */
add_action( 'admin_post_onemz_artist_choose_budget', function () {
    $pid = isset( $_POST['project_id'] ) ? (int) $_POST['project_id'] : 0;
    if ( $pid <= 0 ) return;
    $loc = isset( $_POST['location'] ) ? sanitize_text_field( $_POST['location'] ) : '';
    rmbz_pchat_append_change_log( $pid, [
        'who'  => 'artista',
        'what' => '📍 Artista escolheu localização',
        'from' => '',
        'to'   => $loc,
    ] );
}, 99 );
add_action( 'admin_post_nopriv_onemz_artist_choose_budget', function () {
    $pid = isset( $_POST['project_id'] ) ? (int) $_POST['project_id'] : 0;
    if ( $pid <= 0 ) return;
    $loc = isset( $_POST['location'] ) ? sanitize_text_field( $_POST['location'] ) : '';
    rmbz_pchat_append_change_log( $pid, [
        'who'  => 'artista',
        'what' => '📍 Artista escolheu localização',
        'from' => '',
        'to'   => $loc,
    ] );
}, 99 );

/**
 * Log: artista paga o cenário (parcial ou total).
 *
 * Hookamos depois do pagamento ter sido processado. Lemos o estado
 * actualizado e registamos o valor pago.
 */
add_action( 'admin_post_onemz_pay_project_cost', function () {
    rmbz_pchat_log_payment_after();
}, 99 );
add_action( 'admin_post_nopriv_onemz_pay_project_cost', function () {
    rmbz_pchat_log_payment_after();
}, 99 );

function rmbz_pchat_log_payment_after() {
    $pid = isset( $_POST['project_id'] ) ? (int) $_POST['project_id'] : 0;
    if ( $pid <= 0 ) return;
    global $wpdb;
    $proj = $wpdb->get_row( $wpdb->prepare(
        "SELECT amount_paid, scenario_cost, is_paid FROM {$wpdb->prefix}rmbz_artist_projects WHERE id = %d", $pid
    ) );
    if ( ! $proj ) return;
    $paid_label = number_format( (float) $proj->amount_paid, 2, ',', '.' ) . ' / ' .
                  number_format( (float) $proj->scenario_cost, 2, ',', '.' ) . ' MT';
    rmbz_pchat_append_change_log( $pid, [
        'who'  => 'artista',
        'what' => $proj->is_paid ? '💰 Artista completou pagamento do cenário' : '💵 Artista efectuou pagamento parcial',
        'from' => '',
        'to'   => $paid_label,
    ] );
}

/**
 * Log: admin apaga um projeto.
 *
 * Aqui o projeto desaparece — registamos no log antes do delete? Não,
 * porque o extra_data é apagado junto. Em vez disso, podemos optar por
 * NÃO logar (porque o projeto deixa de existir, ninguém vai ver).
 * Skip.
 */

/**
 * Log: candidatura inicial (quando o projeto é criado).
 *
 * O plugin cria projetos via diferentes paths (admin aprovar submissão,
 * artista candidatar via site/app). Hookamos no momento mais comum:
 * filtro em wp_insert no wpdb? Não, é mais fiável adicionar quando o
 * artista se candidata.
 *
 * Por ora detectamos: se um projeto chega à página de detalhe e o
 * change_log está vazio MAS o projeto existe, criamos uma entrada
 * "📋 Candidatura aberta" baseada no contract_date. Isso garante que
 * todo projeto tem pelo menos uma entrada inicial sem mexer em mais
 * handlers do plugin.
 */
function rmbz_pchat_ensure_initial_entry( $project_id ) {
    global $wpdb;
    $row = $wpdb->get_row( $wpdb->prepare(
        "SELECT contract_date, extra_data FROM {$wpdb->prefix}rmbz_artist_projects WHERE id = %d",
        (int) $project_id
    ) );
    if ( ! $row ) return;
    $extra = ! empty( $row->extra_data ) ? json_decode( $row->extra_data, true ) : [];
    if ( ! is_array( $extra ) ) $extra = [];
    if ( isset( $extra['change_log'] ) && is_array( $extra['change_log'] ) && count( $extra['change_log'] ) > 0 ) {
        return; // Já tem entries
    }
    $extra['change_log'] = [ [
        'ts'   => $row->contract_date ?: current_time( 'mysql' ),
        'who'  => 'sistema',
        'what' => '📋 Candidatura aberta',
    ] ];
    $wpdb->update(
        $wpdb->prefix . 'rmbz_artist_projects',
        [ 'extra_data' => wp_json_encode( $extra ) ],
        [ 'id' => (int) $project_id ],
        [ '%s' ], [ '%d' ]
    );
}

function rmbz_pchat_rest_send( WP_REST_Request $req ) {
    $project_id = (int) $req['id'];
    if ( ! rmbz_pchat_check_project_exists( $project_id ) ) {
        return rmbz_api_error( 'Projeto não encontrado.', 404, 'not_found' );
    }
    if ( rmbz_pchat_is_chat_locked( $project_id ) ) {
        return rmbz_api_error( 'Chat desativado — o projeto foi cancelado ou rejeitado.', 403, 'project_locked' );
    }

    $body_json = $req->get_json_params();
    $text = isset( $body_json['body'] ) ? (string) $body_json['body'] : '';
    $text = sanitize_textarea_field( $text );

    $attachment = null;
    if ( ! empty( $body_json['attachment'] ) && is_array( $body_json['attachment'] ) ) {
        $a = $body_json['attachment'];
        if ( ! empty( $a['url'] ) ) {
            $attachment = [
                'url'  => esc_url_raw( (string) $a['url'] ),
                'type' => sanitize_key( (string) ( $a['type'] ?? 'file' ) ),
                'name' => sanitize_text_field( (string) ( $a['name'] ?? '' ) ),
                'size' => (int) ( $a['size'] ?? 0 ),
            ];
        }
    }

    if ( $text === '' && empty( $attachment ) ) {
        return rmbz_api_error( 'Mensagem vazia.', 400, 'empty' );
    }

    $msg_id = rmbz_pchat_insert_message( $project_id, 'admin', $text, $attachment );
    if ( ! $msg_id ) {
        return rmbz_api_error( 'Falha ao gravar mensagem.', 500, 'insert_fail' );
    }

    // Devolver mensagens desde just before (cliente faz merge optimista)
    return rmbz_api_ok( [
        'id'       => $msg_id,
        'messages' => rmbz_pchat_get_messages( $project_id, $msg_id - 1 ),
    ] );
}

function rmbz_pchat_rest_toggle( WP_REST_Request $req ) {
    $project_id = (int) $req['id'];
    if ( ! rmbz_pchat_check_project_exists( $project_id ) ) {
        return rmbz_api_error( 'Projeto não encontrado.', 404, 'not_found' );
    }
    $body_json = $req->get_json_params();
    $enabled = ! empty( $body_json['enabled'] );

    $new = rmbz_pchat_set_artist_can_attach( $project_id, $enabled );
    return rmbz_api_ok( [ 'artist_can_attach' => (bool) $new ] );
}

function rmbz_pchat_rest_upload( WP_REST_Request $req ) {
    $project_id = (int) $req['id'];
    if ( ! rmbz_pchat_check_project_exists( $project_id ) ) {
        return rmbz_api_error( 'Projeto não encontrado.', 404, 'not_found' );
    }
    if ( empty( $_FILES['file'] ) ) {
        return rmbz_api_error( 'Sem ficheiro recebido.', 400, 'no_file' );
    }
    $res = rmbz_pchat_handle_upload( $project_id, $_FILES['file'], 'admin' );
    if ( is_wp_error( $res ) ) {
        return rmbz_api_error( $res->get_error_message(), 400, $res->get_error_code() );
    }
    return rmbz_api_ok( $res );
}

function rmbz_pchat_rest_edit( WP_REST_Request $req ) {
    $project_id = (int) $req['id'];
    if ( ! rmbz_pchat_check_project_exists( $project_id ) ) {
        return rmbz_api_error( 'Projeto não encontrado.', 404, 'not_found' );
    }
    $body_json = $req->get_json_params();
    $msg_id  = (int) ( $body_json['message_id'] ?? 0 );
    $new_body = sanitize_textarea_field( (string) ( $body_json['body'] ?? '' ) );
    if ( $msg_id <= 0 ) {
        return rmbz_api_error( 'ID inválido. Mensagens legadas não podem ser editadas aqui.', 400, 'bad_id' );
    }
    if ( $new_body === '' ) {
        return rmbz_api_error( 'Mensagem vazia.', 400, 'empty' );
    }

    global $wpdb;
    $row = $wpdb->get_row( $wpdb->prepare(
        "SELECT * FROM {$wpdb->prefix}rmbz_project_chat_messages WHERE id = %d AND project_id = %d",
        $msg_id, $project_id
    ) );
    if ( ! $row ) return rmbz_api_error( 'Mensagem não encontrada.', 404, 'not_found' );
    if ( $row->author_type !== 'admin' ) {
        return rmbz_api_error( 'Apenas mensagens do admin podem ser editadas.', 403, 'forbidden' );
    }

    $wpdb->update(
        $wpdb->prefix . 'rmbz_project_chat_messages',
        [
            'body'      => $new_body,
            'is_edited' => 1,
            'edited_at' => current_time( 'mysql' ),
        ],
        [ 'id' => $msg_id ],
        [ '%s', '%d', '%s' ], [ '%d' ]
    );

    return rmbz_api_ok( [ 'id' => $msg_id, 'body' => $new_body ] );
}

function rmbz_pchat_rest_delete( WP_REST_Request $req ) {
    $project_id = (int) $req['id'];
    $body_json = $req->get_json_params();
    $msg_id  = (int) ( $body_json['message_id'] ?? 0 );
    if ( $msg_id <= 0 ) {
        return rmbz_api_error( 'ID inválido.', 400, 'bad_id' );
    }

    global $wpdb;
    $row = $wpdb->get_row( $wpdb->prepare(
        "SELECT * FROM {$wpdb->prefix}rmbz_project_chat_messages WHERE id = %d AND project_id = %d",
        $msg_id, $project_id
    ) );
    if ( ! $row ) return rmbz_api_error( 'Mensagem não encontrada.', 404, 'not_found' );

    if ( ! in_array( $row->author_type, [ 'admin', 'artist' ], true ) ) {
        return rmbz_api_error( 'Esta mensagem de sistema não pode ser apagada.', 403, 'forbidden' );
    }

    // Soft delete: a API mascara o conteúdo, mas o body é mantido internamente
    // para suprimir com segurança a cópia legada em artist_feedback.
    $wpdb->update(
        $wpdb->prefix . 'rmbz_project_chat_messages',
        [ 'is_deleted' => 1, 'attachment_url' => null, 'edited_at' => current_time( 'mysql' ) ],
        [ 'id' => $msg_id ],
        [ '%d', '%s', '%s' ], [ '%d' ]
    );

    return rmbz_api_ok( [ 'id' => $msg_id, 'deleted' => true ] );
}

/* ============================================================================
 * INLINE CONFIG — emitir window.rmbzChatConfig directamente quando o detalhe
 * é renderizado. Não dependemos de admin_print_footer_scripts (frágil porque
 * o admin_enqueue_scripts pode correr depois desse hook já ter sido despachado
 * em alguns ambientes). Esta função é chamada por projects-admin-page.php
 * no início de rmbz_pjv2_render_detail() — emite o <script> ANTES de tudo
 * o resto, garantindo que está disponível quando o chat script corre.
 */
function rmbz_pchat_emit_inline_config( $project_id ) {
    $project_id = (int) $project_id;
    if ( $project_id <= 0 ) return;

    $base = '1mz/v1/admin-chat/' . $project_id;
    $urls = [
        'get'           => esc_url_raw( add_query_arg( 'since', '__SINCE__', rest_url( $base ) ) ),
        'info'          => esc_url_raw( rest_url( $base . '/info' ) ),
        'send'          => esc_url_raw( rest_url( $base . '/send' ) ),
        'toggle_attach' => esc_url_raw( rest_url( $base . '/toggle-attach' ) ),
        'upload'        => esc_url_raw( rest_url( $base . '/upload' ) ),
        'edit'          => esc_url_raw( rest_url( $base . '/edit' ) ),
        'delete'        => esc_url_raw( rest_url( $base . '/delete' ) ),
    ];
    ?>
    <script>
    window.rmbzChatConfig = {
        urls: <?php echo wp_json_encode( $urls ); ?>,
        nonce: <?php echo wp_json_encode( wp_create_nonce( 'wp_rest' ) ); ?>,
        projectId: <?php echo (int) $project_id; ?>,
        pollIntervalMs: 3000
    };
    </script>
    <?php
}
