// Copyright (2025) Beijing Volcano Engine Technology Ltd.
// SPDX-License-Identifier: Apache-2.0

#include "volc_http.h"

#include <string.h>
#include <esp_log.h>
#include <esp_http_client.h>
#include "volc_platform.h"
#include "esp_crt_bundle.h"

static const char* TAG = "VOLC_HTTP";

// Response data structure for event handler
typedef struct {
    char* buffer;
    size_t buffer_size;
    size_t total_read;
    size_t max_size;
} response_data_t;

// HTTP client event handler to capture response data
static esp_err_t http_event_handler(esp_http_client_event_t *evt)
{
    response_data_t *response_data = (response_data_t *)evt->user_data;
    
    switch (evt->event_id) {
        case HTTP_EVENT_ON_DATA:
            if (response_data && evt->data && evt->data_len > 0) {
                // Calculate available space in buffer
                size_t available = response_data->max_size - response_data->total_read;
                if (available > 0) {
                    // Copy data to buffer (don't exceed max size)
                    size_t copy_len = (evt->data_len < available) ? evt->data_len : available;
                    memcpy(response_data->buffer + response_data->total_read, evt->data, copy_len);
                    response_data->total_read += copy_len;
                    
                    ESP_LOGI(TAG, "HTTP_EVENT_ON_DATA: read %d bytes, total: %d", evt->data_len, response_data->total_read);
                }
            }
            break;
        default:
            break;
    }
    return ESP_OK;
}

char* volc_http_post(const char* uri, const char* post_data, int data_len)
{
    char* response_buffer = NULL;
    esp_http_client_handle_t client = NULL;
    int response_status = 0;
    esp_err_t err = ESP_OK;
    int attempts = 0;
    const int max_attempts = 3;
    
    if (!uri || !post_data || data_len <= 0) {
        ESP_LOGE(TAG, "Invalid parameters: uri=%p, post_data=%p, data_len=%d", uri, post_data, data_len);
        return NULL;
    }
    
    ESP_LOGD(TAG, "HTTP POST: uri=%s, post_data=%s, data_len=%d", uri, post_data, data_len);
    
    /* Perform HTTP request with retries */
    while (attempts < max_attempts) {
        ESP_LOGI(TAG, "HTTP POST attempt %d/%d", attempts + 1, max_attempts);
        
        /* Allocate buffer for response */
        const size_t max_response_size = 16384;  // 增加缓冲区大小到16KB，解决响应被截断的问题
        char *buffer = (char*)hal_malloc(max_response_size + 1);
        if (!buffer) {
            ESP_LOGE(TAG, "Failed to allocate buffer for response reading");
            goto cleanup_attempt;
        }
        
        memset(buffer, 0, max_response_size + 1);
        
        /* Initialize response data structure */
        response_data_t response_data = {
            .buffer = buffer,
            .buffer_size = max_response_size + 1,
            .total_read = 0,
            .max_size = max_response_size
        };
        
        /* Configure HTTP client with event handler */
        esp_http_client_config_t config = {
            .url = uri,
            .timeout_ms = 15000,
            .transport_type = HTTP_TRANSPORT_OVER_SSL,
            .crt_bundle_attach = esp_crt_bundle_attach,
            .skip_cert_common_name_check = true,
            .method = HTTP_METHOD_POST,
            .event_handler = http_event_handler,
            .user_data = &response_data,  // Pass response data to event handler
            .disable_auto_redirect = false,  // Enable redirects
            .max_redirection_count = 3,  // Allow up to 3 redirects
        };
        
        /* Initialize HTTP client */
        client = esp_http_client_init(&config);
        if (client == NULL) {
            ESP_LOGE(TAG, "Failed to initialize HTTP client");
            hal_free(buffer);
            goto cleanup_attempt;
        }
        
        /* Set content type to application/json */
        err = esp_http_client_set_header(client, "Content-Type", "application/json");
        if (err != ESP_OK) {
            ESP_LOGE(TAG, "Failed to set Content-Type header: %s", esp_err_to_name(err));
            goto cleanup_attempt;
        }
        
        /* Set POST data */
        err = esp_http_client_set_post_field(client, post_data, data_len);
        if (err != ESP_OK) {
            ESP_LOGE(TAG, "Failed to set POST field: %s", esp_err_to_name(err));
            goto cleanup_attempt;
        }
        
        /* Perform HTTP request */
        /* The event handler will capture response data */
        ESP_LOGI(TAG, "Performing HTTP request...");
        err = esp_http_client_perform(client);
        if (err != ESP_OK) {
            ESP_LOGE(TAG, "Failed to perform HTTP request: %s", esp_err_to_name(err));
            goto cleanup_attempt;
        }
        
        /* Get response status */
        response_status = esp_http_client_get_status_code(client);
        ESP_LOGI(TAG, "HTTP response status: %d", response_status);
        
        if (response_status == 200) {
            /* Get content length from response header */
            int64_t content_length = esp_http_client_get_content_length(client);
            ESP_LOGI(TAG, "Content-Length: %lld, actually read: %d", content_length, response_data.total_read);
            
            if (response_data.total_read > 0) {
                /* Ensure null termination */
                buffer[response_data.total_read] = '\0';
                
                ESP_LOGI(TAG, "Successfully read response: %d bytes", response_data.total_read);
                ESP_LOGD(TAG, "Response data: %s", buffer);
                
                /* Copy to final buffer */
                response_buffer = (char*)hal_malloc(response_data.total_read + 1);
                if (response_buffer) {
                    memcpy(response_buffer, buffer, response_data.total_read + 1);
                } else {
                    ESP_LOGE(TAG, "Failed to allocate final response buffer");
                }
            } else {
                ESP_LOGE(TAG, "Failed to read any response data via event handler");
                
                /* Try a fallback method - read directly after perform */
                ESP_LOGI(TAG, "Trying fallback: direct read after perform...");
                int fallback_read = esp_http_client_read_response(client, buffer, max_response_size);
                if (fallback_read > 0) {
                    ESP_LOGI(TAG, "Fallback successful: read %d bytes", fallback_read);
                    buffer[fallback_read] = '\0';
                    ESP_LOGD(TAG, "Response data: %s", buffer);
                    
                    response_buffer = (char*)hal_malloc(fallback_read + 1);
                    if (response_buffer) {
                        memcpy(response_buffer, buffer, fallback_read + 1);
                    }
                } else {
                    ESP_LOGE(TAG, "Fallback also failed: %d", fallback_read);
                }
            }
        } else {
            ESP_LOGW(TAG, "Non-200 status: %d", response_status);
        }
        
        cleanup_attempt:
        /* Clean up client and buffer */
        if (client != NULL) {
            esp_http_client_cleanup(client);
            client = NULL;
        }
        if (buffer != NULL) {
            hal_free(buffer);
            buffer = NULL;
        }
        
        /* If we got a response, break the retry loop */
        if (response_buffer) {
            break;
        }
        
        attempts++;
        if (attempts < max_attempts) {
            int backoff_ms = 500 * attempts;
            ESP_LOGI(TAG, "Retrying in %d ms...", backoff_ms);
            hal_thread_sleep(backoff_ms);
        }
    }
    
    if (attempts == max_attempts) {
        ESP_LOGE(TAG, "HTTP POST failed after %d attempts", max_attempts);
    }
    
    ESP_LOGI(TAG, "HTTP POST request completed, response_buffer=%p", response_buffer);
    return response_buffer;
}