<?php
/**
 * 1MZ For Artists — Helpers de Origem
 *
 * Funções partilhadas para capturar e gravar metadados de origem
 * (canal/IP/dispositivo) em qualquer pedido — site web ou app.
 *
 * Convenções:
 *   - origin_channel: 'web' | 'app_android' | 'app_ios' | 'api' | 'admin'
 *   - origin_ip: IPv4 ou IPv6 (45 chars max)
 *   - origin_device_id: UUID estável da app (64 chars max)
 *   - origin_app_version: ex "1.0.5"
 *
 * @package 1MZ_For_Artists
 * @since 1.6.1
 */

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

/**
 * Devolve um array com os metadados da requisição actual.
 *
 * Aceita 3 fontes:
 *   1. HTTP headers da app (X-1MZ-Channel, X-1MZ-Device-Id, X-1MZ-App-Version)
 *   2. Função rmbz_api_get_client_ip() se a Fase 1 da API estiver carregada
 *   3. Defaults para o site web
 *
 * @param string $default_channel canal a assumir se não vier header
 * @return array
 */
function rmbz_origin_capture( $default_channel = 'web' ) {
    $channel = $default_channel;
    $device_id = null;
    $app_version = null;

    // Tentar headers da app primeiro
    if ( isset( $_SERVER['HTTP_X_1MZ_CHANNEL'] ) ) {
        $h = sanitize_key( $_SERVER['HTTP_X_1MZ_CHANNEL'] );
        if ( in_array( $h, [ 'web', 'app_android', 'app_ios', 'api', 'admin' ], true ) ) {
            $channel = $h;
        }
    }
    if ( isset( $_SERVER['HTTP_X_1MZ_DEVICE_ID'] ) ) {
        $raw = (string) $_SERVER['HTTP_X_1MZ_DEVICE_ID'];
        // Aceitar só caracteres seguros para UUID-like ids
        $clean = preg_replace( '/[^a-zA-Z0-9_\-]/', '', $raw );
        $device_id = substr( $clean, 0, 64 );
        if ( $device_id === '' ) $device_id = null;
    }
    if ( isset( $_SERVER['HTTP_X_1MZ_APP_VERSION'] ) ) {
        $v = sanitize_text_field( $_SERVER['HTTP_X_1MZ_APP_VERSION'] );
        $app_version = substr( $v, 0, 20 );
        if ( $app_version === '' ) $app_version = null;
    }

    // IP — usar helper da API se disponível, senão fallback
    if ( function_exists( 'rmbz_api_get_client_ip' ) ) {
        $ip = rmbz_api_get_client_ip();
    } else {
        $ip = rmbz_origin_get_client_ip_fallback();
    }

    $ua = isset( $_SERVER['HTTP_USER_AGENT'] )
        ? substr( (string) $_SERVER['HTTP_USER_AGENT'], 0, 255 )
        : null;

    return [
        'channel'     => $channel,
        'ip'          => $ip,
        'user_agent'  => $ua,
        'device_id'   => $device_id,
        'app_version' => $app_version,
    ];
}

/**
 * Fallback para obter IP quando o helper da API não está disponível.
 * Tem em conta proxies Cloudflare e X-Forwarded-For.
 */
function rmbz_origin_get_client_ip_fallback() {
    $candidates = [
        'HTTP_CF_CONNECTING_IP',
        'HTTP_X_FORWARDED_FOR',
        'HTTP_X_REAL_IP',
        'REMOTE_ADDR',
    ];
    foreach ( $candidates as $key ) {
        if ( ! empty( $_SERVER[ $key ] ) ) {
            $ip = (string) $_SERVER[ $key ];
            // X-Forwarded-For pode trazer "ip1, ip2" — pegar o primeiro
            if ( strpos( $ip, ',' ) !== false ) {
                $ip = trim( explode( ',', $ip )[0] );
            }
            if ( filter_var( $ip, FILTER_VALIDATE_IP ) ) {
                return substr( $ip, 0, 45 );
            }
        }
    }
    return null;
}

/**
 * Regista (ou actualiza) um dispositivo conhecido para um utilizador.
 *
 * Chamado em todos os logins bem sucedidos. Se o device_id já existia
 * para o user, só actualiza last_seen_at e estatísticas. Se é novo,
 * cria entrada em rmbz_app_devices.
 *
 * @param string $telemovel
 * @param array  $origin retorno de rmbz_origin_capture()
 * @param string|null $device_label rótulo amigável (ex: "Samsung Galaxy A52")
 * @return int|false ID da linha em rmbz_app_devices, ou false se não houver device_id
 */
function rmbz_origin_record_device( $telemovel, array $origin, $device_label = null ) {
    if ( empty( $origin['device_id'] ) ) {
        return false; // pedidos do site web não têm device_id
    }

    global $wpdb;
    $table = $wpdb->prefix . 'rmbz_app_devices';
    $now = current_time( 'mysql', 1 );

    // Tentar buscar entrada existente
    $existing = $wpdb->get_row( $wpdb->prepare(
        "SELECT id, login_count FROM {$table}
         WHERE user_telemovel = %s AND device_id = %s",
        $telemovel, $origin['device_id']
    ) );

    if ( $existing ) {
        $wpdb->update(
            $table,
            [
                'last_seen_at'      => $now,
                'last_ip'           => $origin['ip'],
                'last_user_agent'   => $origin['user_agent'],
                'last_app_version'  => $origin['app_version'],
                'login_count'       => (int) $existing->login_count + 1,
            ],
            [ 'id' => $existing->id ],
            [ '%s', '%s', '%s', '%s', '%d' ],
            [ '%d' ]
        );
        return (int) $existing->id;
    }

    // Novo dispositivo — criar
    $wpdb->insert(
        $table,
        [
            'user_telemovel'   => $telemovel,
            'device_id'        => $origin['device_id'],
            'device_label'     => $device_label ? substr( $device_label, 0, 120 ) : null,
            'first_seen_at'    => $now,
            'last_seen_at'     => $now,
            'first_ip'         => $origin['ip'],
            'last_ip'          => $origin['ip'],
            'last_user_agent'  => $origin['user_agent'],
            'last_app_version' => $origin['app_version'],
            'login_count'      => 1,
            'is_trusted'       => 0,
        ],
        [ '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%d', '%d' ]
    );
    return (int) $wpdb->insert_id;
}

/**
 * Devolve o número de dias desde o primeiro login deste dispositivo
 * para este utilizador. Útil para o Guarda-Costas decidir se o dispositivo
 * é "novo" ou "estabelecido".
 *
 * @return int|null  null se device desconhecido
 */
function rmbz_origin_device_age_days( $telemovel, $device_id ) {
    if ( empty( $device_id ) ) return null;
    global $wpdb;
    $first_seen = $wpdb->get_var( $wpdb->prepare(
        "SELECT first_seen_at FROM {$wpdb->prefix}rmbz_app_devices
         WHERE user_telemovel = %s AND device_id = %s",
        $telemovel, $device_id
    ) );
    if ( ! $first_seen ) return null;
    return (int) floor( ( time() - strtotime( $first_seen . ' UTC' ) ) / 86400 );
}

/**
 * Devolve metadados completos do dispositivo (para uso no Guarda-Costas).
 */
function rmbz_origin_get_device_info( $telemovel, $device_id ) {
    if ( empty( $device_id ) ) return null;
    global $wpdb;
    return $wpdb->get_row( $wpdb->prepare(
        "SELECT * FROM {$wpdb->prefix}rmbz_app_devices
         WHERE user_telemovel = %s AND device_id = %s",
        $telemovel, $device_id
    ) );
}

/**
 * Lista todos os dispositivos conhecidos de um utilizador,
 * ordenados por última utilização (mais recente primeiro).
 */
function rmbz_origin_list_devices_for_user( $telemovel ) {
    global $wpdb;
    return $wpdb->get_results( $wpdb->prepare(
        "SELECT * FROM {$wpdb->prefix}rmbz_app_devices
         WHERE user_telemovel = %s
         ORDER BY last_seen_at DESC",
        $telemovel
    ) );
}

/**
 * Marca um dispositivo como suspeito (chamado pelo admin no Guarda-Costas).
 */
function rmbz_origin_mark_device_suspicious( $device_row_id, $reason ) {
    global $wpdb;
    $wpdb->update(
        $wpdb->prefix . 'rmbz_app_devices',
        [
            'marked_suspicious_at' => current_time( 'mysql', 1 ),
            'suspicious_reason'    => substr( $reason, 0, 255 ),
            'is_trusted'           => 0,
        ],
        [ 'id' => (int) $device_row_id ],
        [ '%s', '%s', '%d' ],
        [ '%d' ]
    );
}

/**
 * Marca um dispositivo como confiável (após admin verificar).
 */
function rmbz_origin_mark_device_trusted( $device_row_id ) {
    global $wpdb;
    $wpdb->update(
        $wpdb->prefix . 'rmbz_app_devices',
        [ 'is_trusted' => 1, 'marked_suspicious_at' => null, 'suspicious_reason' => null ],
        [ 'id' => (int) $device_row_id ],
        [ '%d', '%s', '%s' ],
        [ '%d' ]
    );
}
