<?php
/**
 * 1MZ For Artists — Notifications Controller (Fase 4a)
 *
 * Endpoints para o app ler e dispensar notificações guardadas na tabela
 * rmbz_notifications do plugin.
 *
 * Reusa funções existentes:
 *   - rmbz_notify_get_for_user()
 *   - rmbz_notify_dismiss()
 *
 * Particularidade: o app só mostra notificações criadas DEPOIS da primeira
 * vez que o utilizador entrou na tab de notificações. Isso evita "lavar"
 * a app com histórico antigo do site.
 *
 * O timestamp "vi até aqui" é guardado por device, na user_meta
 * `1mz_app_notifs_since_<device_id>`. Cada device tem o seu próprio
 * watermark — assim se o utilizador instalar em vários telemóveis, cada
 * um arranca limpo.
 *
 * @package 1MZ_For_Artists
 * @since 1.6.5
 */

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

add_action( 'rest_api_init', 'rmbz_api_register_notifications_routes' );

function rmbz_api_register_notifications_routes() {

    // Lista de notificações
    register_rest_route( RMBZ_API_NAMESPACE, '/notifications', [
        'methods'             => 'GET',
        'callback'            => 'rmbz_api_handle_get_notifications',
        'permission_callback' => 'rmbz_api_require_auth',
        'args' => [
            'page'     => [ 'type' => 'integer', 'default' => 1 ],
            'per_page' => [ 'type' => 'integer', 'default' => 20 ],
        ],
    ] );

    // Contagem de não lidas (para o badge)
    register_rest_route( RMBZ_API_NAMESPACE, '/notifications/unread-count', [
        'methods'             => 'GET',
        'callback'            => 'rmbz_api_handle_unread_count',
        'permission_callback' => 'rmbz_api_require_auth',
    ] );

    // Dispensar notificação (marcar como lida)
    register_rest_route( RMBZ_API_NAMESPACE, '/notifications/(?P<id>\d+)/dismiss', [
        'methods'             => 'POST',
        'callback'            => 'rmbz_api_handle_dismiss_notification',
        'permission_callback' => 'rmbz_api_require_auth',
        'args' => [
            'id' => [ 'type' => 'integer', 'required' => true ],
        ],
    ] );

    // Marcar TODAS como lidas (botão "Marcar todas como lidas")
    register_rest_route( RMBZ_API_NAMESPACE, '/notifications/dismiss-all', [
        'methods'             => 'POST',
        'callback'            => 'rmbz_api_handle_dismiss_all',
        'permission_callback' => 'rmbz_api_require_auth',
    ] );

    // [1.6.32] Endpoint de debug — diagnostica porque notificações não aparecem
    // GET /notifications/debug
    // Devolve:
    //   - since (watermark actual deste device)
    //   - total_no_filtro (todas as notif na BD para este telemovel, sem since)
    //   - total_com_filtro (com since aplicado)
    //   - amostra_recentes (últimas 5 da BD, mesmo que filtradas)
    //   - device_id, user_id, telemovel
    // Útil para perceber se: (a) há notif na BD, (b) since está mal,
    //                       (c) target_telemovel não bate.
    register_rest_route( RMBZ_API_NAMESPACE, '/notifications/debug', [
        'methods'             => 'GET',
        'callback'            => 'rmbz_api_handle_notifications_debug',
        'permission_callback' => 'rmbz_api_require_auth',
    ] );

    // [1.6.32] Reset do watermark — força ver tudo dos últimos 30 dias
    // POST /notifications/reset-watermark
    // Útil quando o user reporta "não vejo notificações" — limpa o since
    // do device e devolve a nova data, que vai retroceder 30 dias.
    register_rest_route( RMBZ_API_NAMESPACE, '/notifications/reset-watermark', [
        'methods'             => 'POST',
        'callback'            => 'rmbz_api_handle_reset_watermark',
        'permission_callback' => 'rmbz_api_require_auth',
    ] );
}

/* ============================================================================
 * Helpers internos
 * ============================================================================ */

/**
 * Devolve o telemovel + device_id do request actual.
 */
function rmbz_api_notif_telemovel_and_device( WP_REST_Request $request ) {
    $telemovel = (string) $request->get_param( 'rmbz_telemovel' );

    // Device ID vem do header X-1MZ-Device-Id (Guarda-Costas, Fase 1.6.1)
    $device_id = $request->get_header( 'X-1MZ-Device-Id' );
    if ( ! $device_id ) {
        $device_id = $request->get_header( 'x_1mz_device_id' );
    }
    $device_id = preg_replace( '/[^a-zA-Z0-9-]/', '', (string) $device_id );

    return [ $telemovel, $device_id ];
}

/**
 * Devolve o timestamp "ver desde" para este device.
 *
 * Se o device nunca consultou notificações, regista AGORA e devolve agora.
 * Isso garante que o utilizador só vê notificações criadas DEPOIS de ter
 * instalado o app — não é inundado com histórico antigo do site.
 */
function rmbz_api_notif_get_or_init_since( $telemovel, $device_id ) {
    if ( empty( $device_id ) ) {
        // Sem device_id, vê tudo dos últimos 7 dias
        return gmdate( 'Y-m-d H:i:s', strtotime( '-7 days' ) );
    }

    // [1.6.32] Bug-fix: a coluna `user_id` NÃO existe em `rmbz_wallets`
    // (a tabela só tem `id`). O código antigo fazia `SELECT user_id` que
    // devolvia sempre 0, e `get_user_meta(0, ...)` falhava silenciosamente
    // — o watermark nunca era guardado, e a próxima chamada inicializava
    // de novo. Resultado: filtragem por `since = current_time` funcionava
    // mas como nunca persistia, cada chamada era "primeira vez".
    //
    // Fix: guardar em wp_options com chave única por (telemovel, device).
    // wp_options está sempre disponível, não precisa de WP user, e
    // sobrevive a quaisquer mudanças no schema.
    $tel_clean = preg_replace( '/[^0-9+]/', '', $telemovel );
    $dev_clean = substr( $device_id, 0, 32 );
    $option_key = '1mz_app_notifs_since_' . md5( $tel_clean . '_' . $dev_clean );

    $since = get_option( $option_key, '' );

    if ( empty( $since ) ) {
        // Primeira vez deste device — retrocede 7 dias para apanhar
        // notificações recentes em vez de ficar com a tela vazia.
        $since = gmdate( 'Y-m-d H:i:s', strtotime( '-7 days' ) );
        // autoload = no — não queremos isto em memória em todos os pageviews
        update_option( $option_key, $since, false );
    }

    return $since;
}

/**
 * Reusa rmbz_notify_get_for_user mas filtra por "since" (created_at >= since).
 */
function rmbz_api_notif_get_filtered( $telemovel, $since, $limit, $offset ) {
    global $wpdb;

    $telemovel_clean = preg_replace( '/[^0-9+]/', '', $telemovel );
    if ( empty( $telemovel_clean ) ) return [ 'rows' => [], 'total' => 0 ];

    // [1.6.32] Aceitar 3 formatos de target_telemovel:
    //   +258849392636 (formato canónico do app, com +)
    //   258849392636  (sem +, mas com 258)
    //   849392636     (só local — formato antigo do site)
    // O artista pode ter sido registado no site web sem +258, ou ter
    // notificações antigas guardadas em formato inconsistente.
    $variants = rmbz_api_notif_telemovel_variants( $telemovel_clean );
    $placeholders = implode( ',', array_fill( 0, count( $variants ), '%s' ) );

    $now = current_time( 'mysql' );

    // Query base — filtra por (since + visibilidade + match em qualquer variante)
    $sql_count = $wpdb->prepare(
        "SELECT COUNT(*)
         FROM {$wpdb->prefix}rmbz_notifications n
         LEFT JOIN {$wpdb->prefix}rmbz_notifications_dismissed d
                ON d.notification_id = n.id AND d.telemovel = %s
         WHERE n.is_archived = 0
           AND (n.expires_at IS NULL OR n.expires_at > %s)
           AND n.created_at >= %s
           AND d.id IS NULL
           AND (
                n.target_telemovel IN ($placeholders)
             OR (n.target_telemovel IS NULL AND (n.target_role IS NULL OR n.target_role = 'artist'))
           )",
        array_merge( [ $telemovel_clean, $now, $since ], $variants )
    );
    $total = (int) $wpdb->get_var( $sql_count );

    $sql = $wpdb->prepare(
        "SELECT n.*,
                (CASE WHEN d.id IS NULL THEN 0 ELSE 1 END) AS is_dismissed
         FROM {$wpdb->prefix}rmbz_notifications n
         LEFT JOIN {$wpdb->prefix}rmbz_notifications_dismissed d
                ON d.notification_id = n.id AND d.telemovel = %s
         WHERE n.is_archived = 0
           AND (n.expires_at IS NULL OR n.expires_at > %s)
           AND n.created_at >= %s
           AND d.id IS NULL
           AND (
                n.target_telemovel IN ($placeholders)
             OR (n.target_telemovel IS NULL AND (n.target_role IS NULL OR n.target_role = 'artist'))
           )
         ORDER BY n.created_at DESC
         LIMIT %d OFFSET %d",
        array_merge( [ $telemovel_clean, $now, $since ], $variants, [ $limit, $offset ] )
    );

    $rows = $wpdb->get_results( $sql );

    // Filtrar órfãs (release_id que já não existe)
    if ( function_exists( 'rmbz_notify_filter_orphans' ) ) {
        $rows = rmbz_notify_filter_orphans( $rows );
    }

    return [ 'rows' => $rows, 'total' => $total ];
}

/**
 * [1.6.32] Devolve variantes do telemovel para o match flexível.
 * Input: "+258849392636"
 * Output: ["+258849392636", "258849392636", "849392636"]
 */
function rmbz_api_notif_telemovel_variants( $telemovel_clean ) {
    $variants = [ $telemovel_clean ];
    // Sem '+'
    $no_plus = ltrim( $telemovel_clean, '+' );
    if ( $no_plus !== $telemovel_clean ) {
        $variants[] = $no_plus;
    }
    // Sem '258' (assume Moçambique). Aceita também o formato sem +258.
    if ( strpos( $no_plus, '258' ) === 0 ) {
        $local = substr( $no_plus, 3 );
        if ( ! empty( $local ) ) $variants[] = $local;
    }
    return array_values( array_unique( $variants ) );
}

/**
 * Formata uma row da BD para JSON.
 */
function rmbz_api_format_notification( $n ) {
    $metadata = [];
    if ( ! empty( $n->metadata ) ) {
        $dec = json_decode( $n->metadata, true );
        if ( is_array( $dec ) ) $metadata = $dec;
    }

    // Tentar extrair release_id se a notificação for sobre um release
    $release_id = null;
    if ( ! empty( $metadata['release_id'] ) ) {
        $release_id = (int) $metadata['release_id'];
    } elseif ( function_exists( 'rmbz_notify_extract_release_id' ) ) {
        $extracted = rmbz_notify_extract_release_id( $n );
        if ( $extracted ) $release_id = (int) $extracted;
    }

    return [
        'id'           => (int) $n->id,
        'type'         => $n->type,
        'severity'     => $n->severity, // info | success | warning | danger
        'title'        => $n->title,
        'body'         => $n->body,
        'action_url'   => $n->action_url,
        'action_label' => $n->action_label,
        'release_id'   => $release_id,
        'created_at'   => $n->created_at,
        'is_dismissed' => isset( $n->is_dismissed ) ? (bool) $n->is_dismissed : false,
    ];
}

/* ============================================================================
 * GET /notifications
 * ============================================================================ */

function rmbz_api_handle_get_notifications( WP_REST_Request $request ) {
    list( $telemovel, $device_id ) = rmbz_api_notif_telemovel_and_device( $request );

    $page = max( 1, (int) $request->get_param( 'page' ) );
    $per_page = max( 1, min( 50, (int) $request->get_param( 'per_page' ) ) );
    $offset = ( $page - 1 ) * $per_page;

    $since = rmbz_api_notif_get_or_init_since( $telemovel, $device_id );

    $result = rmbz_api_notif_get_filtered( $telemovel, $since, $per_page, $offset );

    $items = array_map( 'rmbz_api_format_notification', $result['rows'] );

    return rmbz_api_ok( [
        'items' => array_values( $items ),
        'pagination' => [
            'page'        => $page,
            'per_page'    => $per_page,
            'total'       => $result['total'],
            'total_pages' => (int) ceil( $result['total'] / $per_page ),
        ],
        'since' => $since,
    ] );
}

/* ============================================================================
 * GET /notifications/unread-count
 *
 * Conta notificações não-dispensadas, criadas depois do "since" do device.
 * Para o badge da campainha.
 * ============================================================================ */

function rmbz_api_handle_unread_count( WP_REST_Request $request ) {
    list( $telemovel, $device_id ) = rmbz_api_notif_telemovel_and_device( $request );

    $since = rmbz_api_notif_get_or_init_since( $telemovel, $device_id );

    global $wpdb;
    $telemovel_clean = preg_replace( '/[^0-9+]/', '', $telemovel );
    $now = current_time( 'mysql' );

    $count = 0;
    // [1.6.32] Aceita 3 formatos de target_telemovel
    $variants = rmbz_api_notif_telemovel_variants( $telemovel_clean );
    $placeholders = implode( ',', array_fill( 0, count( $variants ), '%s' ) );

    $count = (int) $wpdb->get_var( $wpdb->prepare(
        "SELECT COUNT(*)
         FROM {$wpdb->prefix}rmbz_notifications n
         LEFT JOIN {$wpdb->prefix}rmbz_notifications_dismissed d
                ON d.notification_id = n.id AND d.telemovel = %s
         WHERE n.is_archived = 0
           AND (n.expires_at IS NULL OR n.expires_at > %s)
           AND n.created_at >= %s
           AND d.id IS NULL
           AND (
                n.target_telemovel IN ($placeholders)
             OR (n.target_telemovel IS NULL AND (n.target_role IS NULL OR n.target_role = 'artist'))
           )",
        array_merge( [ $telemovel_clean, $now, $since ], $variants )
    ) );

    return rmbz_api_ok( [ 'count' => $count ] );
}

/* ============================================================================
 * POST /notifications/{id}/dismiss
 * ============================================================================ */

function rmbz_api_handle_dismiss_notification( WP_REST_Request $request ) {
    $telemovel = (string) $request->get_param( 'rmbz_telemovel' );
    $id = (int) $request->get_param( 'id' );

    if ( ! function_exists( 'rmbz_notify_dismiss' ) ) {
        return rmbz_api_error( 'Sistema de notificações indisponível.', 500, 'no_notifications_module' );
    }

    $ok = rmbz_notify_dismiss( $id, $telemovel );

    if ( ! $ok ) {
        return rmbz_api_error( 'Falha ao dispensar notificação.', 500, 'dismiss_failed' );
    }

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

/* ============================================================================
 * POST /notifications/dismiss-all
 *
 * Marca todas as notificações actuais (visíveis para este artista) como lidas.
 * ============================================================================ */

function rmbz_api_handle_dismiss_all( WP_REST_Request $request ) {
    list( $telemovel, $device_id ) = rmbz_api_notif_telemovel_and_device( $request );

    $since = rmbz_api_notif_get_or_init_since( $telemovel, $device_id );

    global $wpdb;
    $telemovel_clean = preg_replace( '/[^0-9+]/', '', $telemovel );
    $now = current_time( 'mysql' );

    // Buscar IDs de notificações não-dispensadas
    $ids = $wpdb->get_col( $wpdb->prepare(
        "SELECT n.id
         FROM {$wpdb->prefix}rmbz_notifications n
         LEFT JOIN {$wpdb->prefix}rmbz_notifications_dismissed d
                ON d.notification_id = n.id AND d.telemovel = %s
         WHERE n.is_archived = 0
           AND (n.expires_at IS NULL OR n.expires_at > %s)
           AND n.created_at >= %s
           AND d.id IS NULL
           AND (
                n.target_telemovel = %s
             OR (n.target_telemovel IS NULL AND (n.target_role IS NULL OR n.target_role = 'artist'))
           )",
        $telemovel_clean,
        $now,
        $since,
        $telemovel_clean
    ) );

    $count = 0;
    foreach ( $ids as $id ) {
        if ( rmbz_notify_dismiss( (int) $id, $telemovel ) ) {
            $count++;
        }
    }

    return rmbz_api_ok( [ 'dismissed_count' => $count ] );
}

/* ============================================================================
 * [1.6.32] GET /notifications/debug
 *
 * Diagnostica porque notificações não aparecem para um user. Retorna info
 * crua da BD + filtros aplicados em cada passo, para a app ter um botão
 * "diagnosticar" no Perfil.
 * ============================================================================ */
function rmbz_api_handle_notifications_debug( WP_REST_Request $request ) {
    global $wpdb;

    list( $telemovel, $device_id ) = rmbz_api_notif_telemovel_and_device( $request );
    $telemovel_clean = preg_replace( '/[^0-9+]/', '', $telemovel );
    $now = current_time( 'mysql' );

    // [1.6.32] Watermark vive em wp_options (não user_meta) — ver
    // get_or_init_since para detalhes do bug-fix.
    $dev_clean = substr( $device_id, 0, 32 );
    $option_key = '1mz_app_notifs_since_' . md5( $telemovel_clean . '_' . $dev_clean );
    $since_actual = get_option( $option_key, '' );

    // [1.6.32] Aceitar 3 formatos de target_telemovel
    $variants = rmbz_api_notif_telemovel_variants( $telemovel_clean );
    $placeholders = implode( ',', array_fill( 0, count( $variants ), '%s' ) );

    // 1) Total na BD sem qualquer filtro (só este telemovel ou broadcast)
    $total_bruto = (int) $wpdb->get_var( $wpdb->prepare(
        "SELECT COUNT(*)
         FROM {$wpdb->prefix}rmbz_notifications n
         WHERE (
                n.target_telemovel IN ($placeholders)
             OR (n.target_telemovel IS NULL AND (n.target_role IS NULL OR n.target_role = 'artist'))
           )",
        $variants
    ) );

    // 2) Total não-arquivado, não-expirado, não-dispensado
    $total_visivel = (int) $wpdb->get_var( $wpdb->prepare(
        "SELECT COUNT(*)
         FROM {$wpdb->prefix}rmbz_notifications n
         LEFT JOIN {$wpdb->prefix}rmbz_notifications_dismissed d
                ON d.notification_id = n.id AND d.telemovel = %s
         WHERE n.is_archived = 0
           AND (n.expires_at IS NULL OR n.expires_at > %s)
           AND d.id IS NULL
           AND (
                n.target_telemovel IN ($placeholders)
             OR (n.target_telemovel IS NULL AND (n.target_role IS NULL OR n.target_role = 'artist'))
           )",
        array_merge( [ $telemovel_clean, $now ], $variants )
    ) );

    // 3) Total com filtro de since aplicado
    $since_para_filtro = empty( $since_actual )
        ? gmdate( 'Y-m-d H:i:s', strtotime( '-7 days' ) )
        : $since_actual;

    $total_filtrado = (int) $wpdb->get_var( $wpdb->prepare(
        "SELECT COUNT(*)
         FROM {$wpdb->prefix}rmbz_notifications n
         LEFT JOIN {$wpdb->prefix}rmbz_notifications_dismissed d
                ON d.notification_id = n.id AND d.telemovel = %s
         WHERE n.is_archived = 0
           AND (n.expires_at IS NULL OR n.expires_at > %s)
           AND n.created_at >= %s
           AND d.id IS NULL
           AND (
                n.target_telemovel IN ($placeholders)
             OR (n.target_telemovel IS NULL AND (n.target_role IS NULL OR n.target_role = 'artist'))
           )",
        array_merge( [ $telemovel_clean, $now, $since_para_filtro ], $variants )
    ) );

    // Amostra das 5 últimas (mesmo que filtradas) — para ver datas/types reais
    $amostra = $wpdb->get_results( $wpdb->prepare(
        "SELECT id, type, severity, title, target_telemovel, target_role, created_at, is_archived, expires_at
         FROM {$wpdb->prefix}rmbz_notifications n
         WHERE (
                n.target_telemovel IN ($placeholders)
             OR (n.target_telemovel IS NULL AND (n.target_role IS NULL OR n.target_role = 'artist'))
           )
         ORDER BY n.created_at DESC
         LIMIT 5",
        $variants
    ), ARRAY_A );

    return rmbz_api_ok( [
        'now'              => $now,
        'telemovel'        => $telemovel_clean,
        'telemovel_variants' => $variants,
        'device_id'        => $device_id,
        'option_key'       => $option_key,
        'since_actual'     => $since_actual ?: '(nunca inicializado)',
        'since_para_filtro' => $since_para_filtro,
        'total_bruto'      => $total_bruto,
        'total_visivel'    => $total_visivel,
        'total_filtrado'   => $total_filtrado,
        'amostra_recentes' => $amostra,
    ] );
}

/* ============================================================================
 * [1.6.32] POST /notifications/reset-watermark
 *
 * Limpa o "since" do device — próxima chamada vai inicializar com -7 dias.
 * Útil para users que reportam "não vejo notificações" — basta clicar e
 * notificações dos últimos 7 dias passam a aparecer.
 * ============================================================================ */
function rmbz_api_handle_reset_watermark( WP_REST_Request $request ) {
    list( $telemovel, $device_id ) = rmbz_api_notif_telemovel_and_device( $request );

    if ( empty( $device_id ) ) {
        return rmbz_api_error( 'Device ID em falta no header X-1MZ-Device-Id.', 400, 'missing_device_id' );
    }

    // [1.6.32] Mesma chave que get_or_init_since usa.
    $tel_clean = preg_replace( '/[^0-9+]/', '', $telemovel );
    $dev_clean = substr( $device_id, 0, 32 );
    $option_key = '1mz_app_notifs_since_' . md5( $tel_clean . '_' . $dev_clean );

    // [1.6.32] Quando o user pede explicitamente "não vejo as minhas
    // notificações", retrocede 60 dias (mais agressivo do que os 7 dias
    // do init). Cobre o caso típico: artist fez release há um mês, foi
    // rejeitado, instalou app agora — quer ver mesmo as antigas.
    $novo_since = gmdate( 'Y-m-d H:i:s', strtotime( '-60 days' ) );
    update_option( $option_key, $novo_since, false );

    return rmbz_api_ok( [
        'reset'      => true,
        'novo_since' => $novo_since,
    ] );
}
