/**
 * @file common.c
 * @brief Common utilities for Synor C SDK
 */

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <curl/curl.h>
#include "synor/wallet.h"

/* Error messages */
static const char* error_messages[] = {
    "OK",
    "Invalid argument",
    "Out of memory",
    "Network error",
    "HTTP error",
    "JSON parse error",
    "Timeout",
    "Authentication error",
    "Not found",
    "Unknown error"
};

const char* synor_error_message(synor_error_t error) {
    if (error >= 0 && error <= SYNOR_ERROR_UNKNOWN) {
        return error_messages[error];
    }
    return "Unknown error code";
}

/* HTTP response buffer */
typedef struct {
    char* data;
    size_t size;
} http_response_t;

/* CURL write callback */
static size_t write_callback(void* contents, size_t size, size_t nmemb, void* userp) {
    size_t real_size = size * nmemb;
    http_response_t* resp = (http_response_t*)userp;

    char* ptr = realloc(resp->data, resp->size + real_size + 1);
    if (!ptr) {
        return 0;
    }

    resp->data = ptr;
    memcpy(&(resp->data[resp->size]), contents, real_size);
    resp->size += real_size;
    resp->data[resp->size] = 0;

    return real_size;
}

/* Internal HTTP client structure */
typedef struct {
    CURL* curl;
    char* api_key;
    char* endpoint;
    uint32_t timeout_ms;
    uint32_t retries;
    bool debug;
} synor_http_client_t;

synor_http_client_t* synor_http_client_create(
    const char* api_key,
    const char* endpoint,
    uint32_t timeout_ms,
    uint32_t retries,
    bool debug
) {
    synor_http_client_t* client = calloc(1, sizeof(synor_http_client_t));
    if (!client) return NULL;

    client->curl = curl_easy_init();
    if (!client->curl) {
        free(client);
        return NULL;
    }

    client->api_key = strdup(api_key);
    client->endpoint = strdup(endpoint);
    client->timeout_ms = timeout_ms;
    client->retries = retries;
    client->debug = debug;

    return client;
}

void synor_http_client_destroy(synor_http_client_t* client) {
    if (!client) return;

    if (client->curl) {
        curl_easy_cleanup(client->curl);
    }
    free(client->api_key);
    free(client->endpoint);
    free(client);
}

synor_error_t synor_http_get(
    synor_http_client_t* client,
    const char* path,
    char** response,
    size_t* response_size
) {
    if (!client || !path || !response) {
        return SYNOR_ERROR_INVALID_ARGUMENT;
    }

    char url[2048];
    snprintf(url, sizeof(url), "%s%s", client->endpoint, path);

    http_response_t resp = {0};
    struct curl_slist* headers = NULL;
    char auth_header[512];
    snprintf(auth_header, sizeof(auth_header), "Authorization: Bearer %s", client->api_key);

    headers = curl_slist_append(headers, auth_header);
    headers = curl_slist_append(headers, "Content-Type: application/json");

    curl_easy_setopt(client->curl, CURLOPT_URL, url);
    curl_easy_setopt(client->curl, CURLOPT_HTTPHEADER, headers);
    curl_easy_setopt(client->curl, CURLOPT_WRITEFUNCTION, write_callback);
    curl_easy_setopt(client->curl, CURLOPT_WRITEDATA, &resp);
    curl_easy_setopt(client->curl, CURLOPT_TIMEOUT_MS, client->timeout_ms);

    synor_error_t result = SYNOR_OK;

    for (uint32_t attempt = 0; attempt < client->retries; attempt++) {
        CURLcode res = curl_easy_perform(client->curl);

        if (res == CURLE_OK) {
            long http_code = 0;
            curl_easy_getinfo(client->curl, CURLINFO_RESPONSE_CODE, &http_code);

            if (http_code >= 200 && http_code < 300) {
                *response = resp.data;
                if (response_size) {
                    *response_size = resp.size;
                }
                curl_slist_free_all(headers);
                return SYNOR_OK;
            } else if (http_code == 401) {
                result = SYNOR_ERROR_AUTH;
            } else if (http_code == 404) {
                result = SYNOR_ERROR_NOT_FOUND;
            } else {
                result = SYNOR_ERROR_HTTP;
            }
        } else if (res == CURLE_OPERATION_TIMEDOUT) {
            result = SYNOR_ERROR_TIMEOUT;
        } else {
            result = SYNOR_ERROR_NETWORK;
        }

        if (client->debug) {
            fprintf(stderr, "HTTP attempt %u failed: %s\n",
                    attempt + 1, curl_easy_strerror(res));
        }

        free(resp.data);
        resp.data = NULL;
        resp.size = 0;
    }

    curl_slist_free_all(headers);
    return result;
}

synor_error_t synor_http_post(
    synor_http_client_t* client,
    const char* path,
    const char* body,
    char** response,
    size_t* response_size
) {
    if (!client || !path || !body || !response) {
        return SYNOR_ERROR_INVALID_ARGUMENT;
    }

    char url[2048];
    snprintf(url, sizeof(url), "%s%s", client->endpoint, path);

    http_response_t resp = {0};
    struct curl_slist* headers = NULL;
    char auth_header[512];
    snprintf(auth_header, sizeof(auth_header), "Authorization: Bearer %s", client->api_key);

    headers = curl_slist_append(headers, auth_header);
    headers = curl_slist_append(headers, "Content-Type: application/json");

    curl_easy_setopt(client->curl, CURLOPT_URL, url);
    curl_easy_setopt(client->curl, CURLOPT_HTTPHEADER, headers);
    curl_easy_setopt(client->curl, CURLOPT_POSTFIELDS, body);
    curl_easy_setopt(client->curl, CURLOPT_WRITEFUNCTION, write_callback);
    curl_easy_setopt(client->curl, CURLOPT_WRITEDATA, &resp);
    curl_easy_setopt(client->curl, CURLOPT_TIMEOUT_MS, client->timeout_ms);

    synor_error_t result = SYNOR_OK;

    for (uint32_t attempt = 0; attempt < client->retries; attempt++) {
        CURLcode res = curl_easy_perform(client->curl);

        if (res == CURLE_OK) {
            long http_code = 0;
            curl_easy_getinfo(client->curl, CURLINFO_RESPONSE_CODE, &http_code);

            if (http_code >= 200 && http_code < 300) {
                *response = resp.data;
                if (response_size) {
                    *response_size = resp.size;
                }
                curl_slist_free_all(headers);
                return SYNOR_OK;
            } else if (http_code == 401) {
                result = SYNOR_ERROR_AUTH;
            } else if (http_code == 404) {
                result = SYNOR_ERROR_NOT_FOUND;
            } else {
                result = SYNOR_ERROR_HTTP;
            }
        } else if (res == CURLE_OPERATION_TIMEDOUT) {
            result = SYNOR_ERROR_TIMEOUT;
        } else {
            result = SYNOR_ERROR_NETWORK;
        }

        if (client->debug) {
            fprintf(stderr, "HTTP attempt %u failed: %s\n",
                    attempt + 1, curl_easy_strerror(res));
        }

        free(resp.data);
        resp.data = NULL;
        resp.size = 0;
    }

    curl_slist_free_all(headers);
    return result;
}

/* String duplication helper */
char* synor_strdup(const char* s) {
    if (!s) return NULL;
    return strdup(s);
}
