<?php
/**
 * Endpoint: /api/version/media-url.php
 * Parámetros: id (media id) o url (direct url), opcional download=1 para proxy y forzar descarga
 * - Si se solicita JSON (Accept: application/json) la endpoint requiere autenticación y devuelve {success:true,url:...}
 * - Si se accede mediante GET desde navegador, redirige (302) al URL del media (no requiere sesión)
 */
// Continuar procesando aunque Cloudflare/cliente corte la conexión (evita 502 por timeout)
ignore_user_abort(true);
set_time_limit(300);

require_once __DIR__ . '/../../config/config.php';

// Debugging helpers: log errors to a file and initialize control vars
error_reporting(E_ALL);
ini_set('display_errors', '0');
ini_set('log_errors', '1');
ini_set('error_log', __DIR__ . '/media-url_error.log');

function media_log($msg) {
    static $triedInit = false;
    $path = __DIR__ . '/media-url_debug.log';
    $dir = dirname($path);
    $entry = date('[Y-m-d H:i:s] ') . $msg . PHP_EOL;
    $ok = false;

    // Intentar crear directorio si es necesario
    if (!is_dir($dir) && !file_exists($dir)) {
        @mkdir($dir, 0755, true);
    }

    // Intentar abrir y escribir
    $fp = @fopen($path, 'a');
    if ($fp) {
        if (@fwrite($fp, $entry) !== false) {
            $ok = true;
        }
        @fclose($fp);
    }

    // Si falla, fallback a error_log y marcar bandera
    if (!$ok) {
        @error_log("media-url (fallback): " . $msg);
        global $log_write_failed;
        $log_write_failed = true;
    }

    // Intento inicial de escritura para detectar permisos temprano
    if (!$triedInit) {
        $triedInit = true;
        if (isset($log_write_failed) && $log_write_failed) {
            // escribir un pequeño aviso también en error_log
            @error_log('media-url: debug log write failed, check permissions on ' . $dir);
        }
    }
}

$log_write_failed = false;
$resolvedFromGraph = false;
$decoded = null;

/**
 * Stream a remote file directly to the browser without buffering in memory.
 * Solves timeout and memory issues with large videos/PDFs.
 */
function streamRemoteFile($url, $token = '', $forceDownload = false, $suggestedFilename = null) {
    set_time_limit(300);
    while (ob_get_level()) @ob_end_clean();

    $curlHeaders = ['User-Agent: WhatsAppMediaProxy/1.0', 'Accept: */*'];
    if ($token) $curlHeaders[] = 'Authorization: Bearer ' . $token;

    $respHeaders = [];
    $browserHeadersSent = false;
    $isError = false;
    $errorBody = '';

    $ch = curl_init();
    curl_setopt_array($ch, [
        CURLOPT_URL            => $url,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_TIMEOUT        => 300,
        CURLOPT_CONNECTTIMEOUT => 30,
        CURLOPT_BUFFERSIZE     => 65536,
        CURLOPT_HTTPHEADER     => $curlHeaders,
        CURLOPT_HEADERFUNCTION => function($ch, $headerLine) use (&$respHeaders) {
            $len = strlen($headerLine);
            $parts = explode(':', $headerLine, 2);
            if (count($parts) == 2) {
                $respHeaders[strtolower(trim($parts[0]))] = trim($parts[1]);
            }
            return $len;
        },
        CURLOPT_WRITEFUNCTION  => function($ch, $data) use (&$browserHeadersSent, &$respHeaders, &$isError, &$errorBody, $forceDownload, $suggestedFilename) {
            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

            // Buffer error responses
            if ($httpCode >= 400) {
                $isError = true;
                $errorBody .= $data;
                return strlen($data);
            }

            // Detect JSON error responses
            if (!$browserHeadersSent) {
                $ct = $respHeaders['content-type'] ?? curl_getinfo($ch, CURLINFO_CONTENT_TYPE) ?? '';
                if (stripos($ct, 'application/json') !== false) {
                    $isError = true;
                    $errorBody .= $data;
                    return strlen($data);
                }
            }

            // Send browser headers on first data chunk
            if (!$browserHeadersSent) {
                $browserHeadersSent = true;
                $contentType = $respHeaders['content-type'] ?? curl_getinfo($ch, CURLINFO_CONTENT_TYPE) ?? 'application/octet-stream';
                $contentLength = $respHeaders['content-length'] ?? null;

                header_remove('Content-Type');
                header('Cache-Control: public, max-age=86400');
                header('Content-Type: ' . $contentType);
                if ($contentLength) header('Content-Length: ' . $contentLength);

                if ($forceDownload) {
                    $filename = $suggestedFilename;
                    if (!$filename) {
                        $cd = $respHeaders['content-disposition'] ?? null;
                        if ($cd) {
                            if (preg_match("/filename\\*=(?:[^']*'')?(.+)$/i", $cd, $m)) {
                                $filename = rawurldecode(trim($m[1], " \"'\r\n"));
                            } elseif (preg_match('/filename="?([^";]+)"?/i', $cd, $m)) {
                                $filename = trim($m[1]);
                            }
                        }
                    }
                    if (!$filename) {
                        $path = parse_url(curl_getinfo($ch, CURLINFO_EFFECTIVE_URL) ?: '', PHP_URL_PATH) ?: '';
                        $filename = basename($path) ?: 'download';
                        if (strpos($filename, '.') === false) {
                            $mimeMap = ['application/pdf'=>'.pdf','image/jpeg'=>'.jpg','image/png'=>'.png','video/mp4'=>'.mp4','audio/mpeg'=>'.mp3','audio/ogg'=>'.ogg'];
                            $baseCt = strtolower(trim(explode(';', $contentType)[0]));
                            if (isset($mimeMap[$baseCt])) $filename .= $mimeMap[$baseCt];
                        }
                    }
                    header('Content-Disposition: attachment; filename="' . $filename . '"');
                }
            }

            echo $data;
            flush();
            return strlen($data);
        },
    ]);

    curl_exec($ch);
    $err = curl_error($ch);
    $finalHttpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($err || $isError) {
        if (!$browserHeadersSent) {
            http_response_code(502);
            header('Content-Type: application/json; charset=utf-8');
            if ($isError && $errorBody) {
                $decoded = json_decode($errorBody, true);
                echo json_encode(['success' => false, 'error' => 'Remote server error', 'http_code' => $finalHttpCode, 'remote' => $decoded]);
            } else {
                echo json_encode(['success' => false, 'error' => 'Error streaming file', 'detail' => $err]);
            }
        }
        media_log("streamRemoteFile failed: url={$url} err={$err} http={$finalHttpCode}");
        return false;
    }

    return true;
}

// Log de petición inicial
media_log("Request: " . ($_SERVER['REQUEST_METHOD'] ?? 'GET') . " " . ($_SERVER['REQUEST_URI'] ?? '') . " GET:" . json_encode($_GET) . " POST:" . json_encode($_POST));

// Manejador de excepciones no capturadas -> devolver JSON claro
set_exception_handler(function($e){
    media_log("Uncaught exception: " . $e->getMessage() . " in " . $e->getFile() . ":" . $e->getLine());
    http_response_code(500);
    header('Content-Type: application/json; charset=utf-8');
    echo json_encode(['success'=>false,'error'=>'Uncaught exception','detail'=>$e->getMessage()]);
    exit;
});

// Shutdown handler para capturar errores fatales (E_ERROR, E_PARSE, ...)
register_shutdown_function(function(){
    $err = error_get_last();
    if ($err && in_array($err['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
        media_log("Fatal error: " . ($err['message'] ?? '') . " in " . ($err['file'] ?? '') . ":" . ($err['line'] ?? '')); 
        http_response_code(500);
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode(['success'=>false,'error'=>'Fatal error','detail'=>$err]);
        // no exit; (shutdown)
    }
});

$isGet = $_SERVER['REQUEST_METHOD'] === 'GET';
$accept = $_SERVER['HTTP_ACCEPT'] ?? '';
$wantsJson = strpos($accept, 'application/json') !== false;

// Requerir autenticación sólo para peticiones no-GET o cuando se solicita JSON (API/AJAX)
if (!$isGet || $wantsJson) {
    requireAuthentication();
}

header('Content-Type: application/json; charset=utf-8');

$mediaId = $_GET['id'] ?? $_POST['id'] ?? null;
$providedUrl = $_GET['url'] ?? $_POST['url'] ?? null;
$download = isset($_GET['download']) && $_GET['download'] === '1';
$debug = isset($_GET['debug']) && $_GET['debug'] === '1';

if (!$mediaId && !$providedUrl) {
    http_response_code(400);
    echo json_encode(['success' => false, 'error' => 'Se requiere id o url']);
    exit;
}

// ── PRIORIDAD: Buscar archivo local en BD antes de consultar Graph API ──
// También buscar por URL completa (para videos/docs enviados como URL lookaside)
if ($providedUrl) {
    try {
        $db = Database::getInstance();
        $row = $db->fetch(
            "SELECT local_file, local_thumb, mime_type, filename FROM conversations WHERE media_url = :url AND local_file IS NOT NULL AND local_file != '' ORDER BY id DESC LIMIT 1",
            ['url' => $providedUrl]
        );
        if ($row && !empty($row['local_file'])) {
            $localPath = realpath(__DIR__ . '/../../' . ltrim($row['local_file'], '/'));
            $base = realpath(__DIR__ . '/../../uploads');
            if ($localPath && strpos($localPath, $base) === 0 && file_exists($localPath)) {
                media_log("Serving local file for URL {$providedUrl}: {$localPath}");
                $finfo = finfo_open(FILEINFO_MIME_TYPE);
                $ctype = finfo_file($finfo, $localPath) ?: ($row['mime_type'] ?? 'application/octet-stream');
                finfo_close($finfo);
                
                header_remove('Content-Type');
                header('Cache-Control: public, max-age=86400');
                header('Content-Type: ' . $ctype);
                header('Content-Length: ' . filesize($localPath));
                if ($download) {
                    $fname = $row['filename'] ?: basename($localPath);
                    header('Content-Disposition: attachment; filename="' . $fname . '"');
                }
                readfile($localPath);
                exit;
            }
        }
    } catch (Exception $e) {
        media_log("Error buscando local_file por URL: " . $e->getMessage());
    }
}

if ($mediaId) {
    try {
        $db = Database::getInstance();
        // Buscar por media_url (que almacena el media ID) o por whatsapp_media_id
        $row = $db->fetch(
            "SELECT local_file, local_thumb, mime_type, filename FROM conversations WHERE (media_url = :mid OR whatsapp_media_id = :mid2) AND local_file IS NOT NULL AND local_file != '' ORDER BY id DESC LIMIT 1",
            ['mid' => $mediaId, 'mid2' => $mediaId]
        );
        if ($row && !empty($row['local_file'])) {
            $localPath = realpath(__DIR__ . '/../../' . ltrim($row['local_file'], '/'));
            $base = realpath(__DIR__ . '/../../uploads');
            if ($localPath && strpos($localPath, $base) === 0 && file_exists($localPath)) {
                media_log("Serving local file for media ID {$mediaId}: {$localPath}");
                $finfo = finfo_open(FILEINFO_MIME_TYPE);
                $ctype = finfo_file($finfo, $localPath) ?: ($row['mime_type'] ?? 'application/octet-stream');
                finfo_close($finfo);
                
                header_remove('Content-Type');
                header('Cache-Control: public, max-age=86400');
                header('Content-Type: ' . $ctype);
                header('Content-Length: ' . filesize($localPath));
                if ($download) {
                    $fname = $row['filename'] ?: basename($localPath);
                    header('Content-Disposition: attachment; filename="' . $fname . '"');
                }
                readfile($localPath);
                exit;
            }
        }
    } catch (Exception $e) {
        media_log("Error buscando local_file en BD: " . $e->getMessage());
        // Continuar con Graph API como fallback
    }
}

// ── Si no hay archivo local, descargar con MediaService y guardar como cache permanente ──
if ($mediaId) {
    try {
        require_once __DIR__ . '/../../classes/MediaService.php';
        $ms = new MediaService();
        $subdir = date('Y/m');
        $result = $ms->fetchAndStoreFromGraph($mediaId, $subdir);
        if ($result && !empty($result['local_file'])) {
            // Actualizar BD para que próximas peticiones se sirvan desde local
            try {
                $db = $db ?? Database::getInstance();
                $db->query(
                    "UPDATE conversations SET local_file = :lf, local_thumb = :lt, mime_type = :mt WHERE (media_url = :mid OR whatsapp_media_id = :mid2) AND (local_file IS NULL OR local_file = '')",
                    [
                        'lf' => $result['local_file'],
                        'lt' => $result['local_thumb'] ?? null,
                        'mt' => $result['mime_type'] ?? null,
                        'mid' => $mediaId,
                        'mid2' => $mediaId
                    ]
                );
                media_log("Auto-cached media {$mediaId} → {$result['local_file']}");
            } catch (Exception $dbErr) {
                media_log("DB update failed after caching media {$mediaId}: " . $dbErr->getMessage());
            }

            // Servir el archivo recién descargado
            $localPath = realpath(__DIR__ . '/../../' . ltrim($result['local_file'], '/'));
            if ($localPath && file_exists($localPath)) {
                $finfo = finfo_open(FILEINFO_MIME_TYPE);
                $ctype = finfo_file($finfo, $localPath) ?: ($result['mime_type'] ?? 'application/octet-stream');
                finfo_close($finfo);

                header_remove('Content-Type');
                header('Cache-Control: public, max-age=86400');
                header('Content-Type: ' . $ctype);
                header('Content-Length: ' . filesize($localPath));
                if ($download) {
                    $fname = basename($localPath);
                    header('Content-Disposition: attachment; filename="' . $fname . '"');
                }
                readfile($localPath);
                exit;
            }
        }
    } catch (Exception $e) {
        media_log("Auto-download failed for media {$mediaId}: " . $e->getMessage());
        // Continuar con Graph API proxy como fallback
    }
}

$mediaUrl = null;

if ($providedUrl) {
    // Reconstruir la URL si fue pasada sin codificar y otros parámetros quedaron como GET separados
    $mediaUrl = $providedUrl;
    // Normalizar entidades HTML que puedan haber sido almacenadas (ej. &amp; -> &)
    $mediaUrl = html_entity_decode($mediaUrl, ENT_QUOTES | ENT_HTML5);
    $ignoredKeys = ['url','download','id','local'];
    $extraParts = [];
    foreach ($_GET as $k => $v) {
        if (in_array($k, $ignoredKeys, true)) continue;
        // Reconstruir sólo si el parámetro no está ya presente en la URL
        if (strpos($mediaUrl, $k . '=') === false) {
            $extraParts[] = $k . ($v !== '' ? '=' . rawurlencode($v) : '');
        }
    }
    if (!empty($extraParts)) {
        $mediaUrl .= (strpos($mediaUrl, '?') === false ? '?' : '&') . implode('&', $extraParts);
    }

    // Intentar detectar un media id embebido en la URL (mid=... o URL hacia graph.facebook.com/v.../{id})
    $resolvedFromGraph = false;
    $mid = null;

    // Buscar mid en query string (lookaside or other URLs)
    if (preg_match('/[?&]mid=([^&]+)/i', $mediaUrl, $m)) {
        $mid = rawurldecode($m[1]);
    }

    // Buscar patrón directo hacia graph.facebook.com/.../{mediaId}
    if (!$mid && preg_match('#https?://[^/]*graph\.facebook\.com(?:/v[0-9]+\.[0-9]+)?/([^/?#&]+)#i', $mediaUrl, $m)) {
        // El primer segmento después de la versión suele ser el media id
        $candidate = $m[1];
        // Aceptar si parece un id (numérico o alfanum corto)
        if (preg_match('/^[a-zA-Z0-9_-]+$/', $candidate)) {
            $mid = $candidate;
        }
    }

    // Si detectamos un mid, consultamos Graph API desde el servidor para obtener la URL (autenticada)
    if ($mid) {
        $token = getConfigFromDB('whatsapp_token', '');
        $apiUrl = rtrim(getConfigFromDB('whatsapp_api_url', 'https://graph.facebook.com/v22.0/'), '/');
        $endpoint = "{$apiUrl}/{$mid}";

        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_URL => $endpoint,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . $token, 'Accept: application/json' ],
            CURLOPT_TIMEOUT => 15,
        ]);
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $err = curl_error($ch);
        curl_close($ch);

        if ($err || $httpCode >= 400) {
            media_log("Graph API fetch failed for mid {$mid} - http: {$httpCode} - err: {$err} - resp: " . substr($response ?? '', 0, 1000));
        } else {
            $decoded = json_decode($response, true);
            $resolvedUrl = $decoded['url'] ?? ($decoded['data'][0]['url'] ?? null);
            if ($resolvedUrl) {
                $mediaUrl = $resolvedUrl;
                $resolvedFromGraph = true;
                media_log("Resolved media URL from Graph for mid {$mid}: {$mediaUrl}");
            } else {
                media_log("Graph returned no media URL for mid {$mid} - resp: " . substr($response ?? '', 0, 1000));
            }
        }
        // Si hubo error o no devolvió URL, seguimos usando la URL provista como fallback
    }

} else {
    $token = getConfigFromDB('whatsapp_token', '');
    $apiUrl = rtrim(getConfigFromDB('whatsapp_api_url', 'https://graph.facebook.com/v22.0/'), '/');
    
    // Validar que mediaId no esté vacío antes de construir el endpoint
    if (empty($mediaId) || !is_string($mediaId) || trim($mediaId) === '') {
        media_log("Invalid or empty mediaId provided: " . var_export($mediaId, true));
        http_response_code(400);
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode([
            'success' => false,
            'error' => 'Invalid media ID',
            'hint' => 'Media ID cannot be empty or invalid'
        ]);
        exit;
    }
    
    $endpoint = "{$apiUrl}/{$mediaId}";

    if (empty($token)) {
        media_log("Missing whatsapp_token for Graph API request to {$endpoint}");
        http_response_code(400);
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode([
            'success' => false,
            'error' => 'Missing whatsapp_token in configuration',
            'hint' => 'Set the token using /configurar_whatsapp.php or insert into system_config (key: whatsapp_token). Run verificar_token.php to validate.'
        ]);
        exit;
    }

    media_log("Graph API request to {$endpoint}");

    $ch = curl_init();
    curl_setopt_array($ch, [
        CURLOPT_URL => $endpoint,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . $token, 'Accept: application/json' ],
        CURLOPT_TIMEOUT => 15,
    ]);
    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $err = curl_error($ch);
    curl_close($ch);

    if ($err) {
        http_response_code(502);
        echo json_encode(['success' => false, 'error' => 'Error comunicando con Graph API', 'detail' => $err]);
        exit;
    }

    $decoded = json_decode($response, true);
    if ($httpCode >= 400 || !$decoded) {
        http_response_code($httpCode ?: 500);
        echo json_encode(['success' => false, 'error' => 'Graph API error', 'response' => $decoded]);
        exit;
    }

    $mediaUrl = $decoded['url'] ?? ($decoded['data'][0]['url'] ?? null);
    if (!$mediaUrl) {
        http_response_code(404);
        echo json_encode(['success' => false, 'error' => 'No se recibió URL del media', 'raw' => $decoded]);
        exit;
    }
}

// === MODO DEBUG: muestra diagnóstico en lugar de redirigir/descargar ===
if ($debug) {
    header('Content-Type: text/html; charset=utf-8');
    $tokenRaw = getConfigFromDB('whatsapp_token', '');
    $tokenPreview = $tokenRaw ? substr($tokenRaw, 0, 15) . '...' . substr($tokenRaw, -10) : '(vacío)';
    $apiUrlConfig = rtrim(getConfigFromDB('whatsapp_api_url', 'https://graph.facebook.com/v22.0/'), '/');
    
    echo '<html><head><title>Media URL Debug</title>';
    echo '<style>body{font-family:monospace;padding:20px;background:#1e1e1e;color:#d4d4d4}';
    echo '.ok{color:#4ec9b0}.err{color:#f44747}.warn{color:#dcdcaa}';
    echo 'pre{background:#2d2d2d;padding:15px;border-radius:5px;overflow-x:auto;white-space:pre-wrap}';
    echo 'h2{color:#569cd6}h3{color:#9cdcfe}</style></head><body>';
    echo '<h2>🔍 Diagnóstico media-url.php</h2>';
    echo '<h3>Parámetros de entrada</h3><pre>';
    echo "Media ID: " . htmlspecialchars($mediaId ?? '(no proporcionado)') . "\n";
    echo "URL proporcionada: " . htmlspecialchars($providedUrl ?? '(no proporcionada)') . "\n";
    echo "download: " . ($download ? '1' : '0') . "\n";
    echo '</pre>';
    
    echo '<h3>Configuración</h3><pre>';
    echo "Token: " . htmlspecialchars($tokenPreview) . "\n";
    echo "API URL: " . htmlspecialchars($apiUrlConfig) . "\n";
    
    if (empty($tokenRaw)) {
        echo '<span class="err">⛔ ERROR: Token de WhatsApp vacío o no configurado</span>' . "\n";
        echo 'Solución: Configurar en /configurar_whatsapp.php o en system_config (key: whatsapp_token)' . "\n";
    } else {
        echo '<span class="ok">✅ Token presente</span>' . "\n";
    }
    echo '</pre>';
    
    echo '<h3>Resultado Graph API</h3><pre>';
    if ($mediaUrl) {
        echo '<span class="ok">✅ URL resuelta:</span> ' . htmlspecialchars($mediaUrl) . "\n";
        echo "Resuelto desde Graph: " . ($resolvedFromGraph ? 'Sí' : 'No') . "\n";
        
        // Validar que la URL sea accesible desde el servidor
        echo "\n<span class=\"warn\">Verificando accesibilidad de la URL...</span>\n";
        $testCh = curl_init();
        $testHeaders = ['User-Agent: WhatsAppMediaProxy/1.0', 'Accept: */*'];
        if ($tokenRaw) $testHeaders[] = 'Authorization: Bearer ' . $tokenRaw;
        curl_setopt_array($testCh, [
            CURLOPT_URL => $mediaUrl,
            CURLOPT_NOBODY => true,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_TIMEOUT => 10,
            CURLOPT_HTTPHEADER => $testHeaders,
        ]);
        curl_exec($testCh);
        $testInfo = curl_getinfo($testCh);
        $testErr = curl_error($testCh);
        curl_close($testCh);
        
        echo "HTTP Status: " . ($testInfo['http_code'] ?? 'N/A') . "\n";
        echo "Content-Type: " . ($testInfo['content_type'] ?? 'N/A') . "\n";
        echo "Content-Length: " . ($testInfo['download_content_length'] ?? 'N/A') . " bytes\n";
        
        if ($testErr) {
            echo '<span class="err">⛔ Error cURL: ' . htmlspecialchars($testErr) . '</span>' . "\n";
        }
        
        if (($testInfo['http_code'] ?? 0) >= 400) {
            echo '<span class="err">⛔ La URL del media NO es accesible (HTTP ' . $testInfo['http_code'] . ')</span>' . "\n";
            if (($testInfo['http_code'] ?? 0) == 404) {
                echo '<span class="warn">⚠️ El media probablemente expiró. Los archivos multimedia de WhatsApp expiran después de ~30 días.</span>' . "\n";
            } elseif (($testInfo['http_code'] ?? 0) == 401 || ($testInfo['http_code'] ?? 0) == 403) {
                echo '<span class="warn">⚠️ Error de autenticación. Verificar que el token sea válido y tenga permisos.</span>' . "\n";
            }
        } else {
            echo '<span class="ok">✅ URL accesible correctamente</span>' . "\n";
        }
    } else {
        echo '<span class="err">⛔ No se pudo resolver la URL del media</span>' . "\n";
        if (!empty($decoded)) {
            echo "Respuesta de Graph API:\n";
            echo htmlspecialchars(json_encode($decoded, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)) . "\n";
            
            // Diagnóstico específico de errores comunes
            $errCode = $decoded['error']['code'] ?? null;
            $errSubcode = $decoded['error']['error_subcode'] ?? null;
            $errMsg = $decoded['error']['message'] ?? '';
            
            if ($errCode == 190) {
                echo '<span class="err">⛔ Token expirado o inválido (code 190)</span>' . "\n";
                echo '<span class="warn">Solución: Renovar el token en /configurar_whatsapp.php</span>' . "\n";
            } elseif ($errCode == 100 && $errSubcode == 33) {
                echo '<span class="err">⛔ Media ID no encontrado o expirado</span>' . "\n";
                echo '<span class="warn">Los archivos multimedia de WhatsApp Business API expiran aproximadamente 30 días después de ser recibidos.</span>' . "\n";
            } elseif ($errCode == 100) {
                echo '<span class="err">⛔ Parámetro inválido (code 100)</span>' . "\n";
                echo '<span class="warn">El media ID puede ser incorrecto: ' . htmlspecialchars($mediaId ?? '') . '</span>' . "\n";
            }
        }
    }
    echo '</pre>';
    
    echo '<h3>Respuesta raw de Graph API ($decoded)</h3><pre>';
    echo htmlspecialchars(json_encode($decoded ?? null, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
    echo '</pre>';
    
    echo '<p style="margin-top:20px;color:#888">Generado: ' . date('Y-m-d H:i:s T') . '</p>';
    echo '</body></html>';
    exit;
}

// Soporte para archivos locales (local=uploads/media/...)
$local = $_GET['local'] ?? null;
if ($local) {
    $localPath = realpath(__DIR__ . '/../../' . ltrim($local, '/'));
    $base = realpath(__DIR__ . '/../../uploads/media');
    // Prevención simple de path traversal
    if (!$localPath || strpos($localPath, $base) !== 0) {
        http_response_code(400);
        echo json_encode(['success' => false, 'error' => 'Ruta local inválida']);
        exit;
    }

    if (!file_exists($localPath)) {
        http_response_code(404);
        echo json_encode(['success' => false, 'error' => 'Archivo no encontrado']);
        exit;
    }

    if ($wantsJson) {
        // devolver URL pública directa (relative)
        echo json_encode(['success' => true, 'url' => '/' . ltrim($local, '/')]);
        exit;
    }

    if ($download) {
        // Forzar descarga del archivo local
        header('Cache-Control: public, max-age=3600');
        $finfo = finfo_open(FILEINFO_MIME_TYPE);
        $ctype = finfo_file($finfo, $localPath);
        finfo_close($finfo);
        header('Content-Type: ' . $ctype);
        header('Content-Length: ' . filesize($localPath));
        header('Content-Disposition: attachment; filename="' . basename($localPath) . '"');
        readfile($localPath);
        exit;
    }

    // Redirigir al archivo local (servidor web lo servirá)
    header('Cache-Control: public, max-age=3600');
    header('Location: /' . ltrim($local, '/'), true, 302);
    exit;
}

// Si se solicitó JSON, devolver la URL y metadata si está disponible
if ($wantsJson) {
    $result = ['success' => true, 'url' => $mediaUrl];

    // Incluir metadata retornada por Graph API si existe en $decoded
    if (!empty($decoded) && is_array($decoded)) {
        // Campos esperados directamente
        foreach (['url','mime_type','sha256','file_size','id','messaging_product'] as $k) {
            if (isset($decoded[$k])) $result[$k] = $decoded[$k];
        }
        // También comprobar 'data'[0] (caso de respuestas anidadas)
        if (isset($decoded['data']) && is_array($decoded['data']) && isset($decoded['data'][0]) && is_array($decoded['data'][0])) {
            foreach (['url','mime_type','sha256','file_size','id','messaging_product'] as $k) {
                if (isset($decoded['data'][0][$k])) $result[$k] = $decoded['data'][0][$k];
            }
        }
    }

    echo json_encode($result);
    exit;
}

// Si se pidió proxy (download=1), traer el contenido y retornarlo como descarga
if ($download) {
    // Si la URL apunta al mismo servidor (localhost/127.0.0.1 o al host actual), no usar curl (evita deadlocks en PHP built-in server)
    $parsedUrl = parse_url($mediaUrl);
    $host = isset($parsedUrl['host']) ? strtolower($parsedUrl['host']) : '';
    $port = isset($parsedUrl['port']) ? $parsedUrl['port'] : null;

    $isLocalHost = in_array($host, ['127.0.0.1', 'localhost']) || ($host && isset($_SERVER['HTTP_HOST']) && stripos($_SERVER['HTTP_HOST'], $host) !== false);

    if ($isLocalHost) {
        media_log("Serving local file directly for URL: {$mediaUrl}");

        // Mapear path a archivo local seguro
        $path = $parsedUrl['path'] ?? '';
        // Asumir que los archivos subidos están en uploads/
        $localPath = realpath(__DIR__ . '/../../' . ltrim($path, '/'));
        $base = realpath(__DIR__ . '/../../uploads');

        if (!$localPath || strpos($localPath, $base) !== 0 || !file_exists($localPath)) {
            http_response_code(404);
            header('Content-Type: application/json; charset=utf-8');
            echo json_encode(['success' => false, 'error' => 'Archivo local no encontrado o ruta inválida', 'path' => $localPath]);
            exit;
        }

        // Determinar mime type
        $finfo = finfo_open(FILEINFO_MIME_TYPE);
        $contentType = finfo_file($finfo, $localPath);
        finfo_close($finfo);

        header_remove('Content-Type');
        header('Cache-Control: public, max-age=3600');
        if (!empty($contentType)) header('Content-Type: ' . $contentType);
        header('Content-Length: ' . filesize($localPath));
        header('Content-Disposition: attachment; filename="' . basename($localPath) . '"');
        readfile($localPath);
        exit;
    }

    // Streaming proxy para descargas (soporta archivos grandes sin buffering en memoria)
    $token = getConfigFromDB('whatsapp_token', '');
    media_log("Streaming download for: {$mediaUrl}");
    streamRemoteFile($mediaUrl, $token, true);
    exit;
}

// Por defecto: si la URL fue resuelta mediante Graph API, streaming proxy
// (evita buffering en memoria para archivos grandes como videos)
if (!$wantsJson && !empty($resolvedFromGraph) && $resolvedFromGraph) {
    $token = getConfigFromDB('whatsapp_token', '');
    media_log("Streaming Graph-resolved media: {$mediaUrl}");
    streamRemoteFile($mediaUrl, $token);
    exit;
}

// Por defecto: si la URL parece pertenecer a Facebook/lookaside y disponemos de token, proxificar con Authorization
$parsed = parse_url($mediaUrl);
$host = isset($parsed['host']) ? strtolower($parsed['host']) : '';
$token = getConfigFromDB('whatsapp_token', '');
$shouldProxy = false;
if ($token && ($host === 'lookaside.fbsbx.com' || stripos($host, 'graph.facebook.com') !== false || stripos($host, 'facebook.com') !== false)) {
    $shouldProxy = true;
}

if ($shouldProxy) {
    media_log("Streaming Facebook media with Authorization: {$mediaUrl}");
    streamRemoteFile($mediaUrl, $token);
    exit;
}

// Fallback: redirigir al URL (navegador hará la descarga o mostrará la imagen)
header('Cache-Control: public, max-age=3600');
header('Location: ' . $mediaUrl, true, 302);
exit;
